Make driver flags available through control interface
[mech_eap.git] / wpa_supplicant / wpa_supplicant.c
index 0829d09..6999bbb 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * WPA Supplicant
- * Copyright (c) 2003-2014, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2003-2016, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
  */
 
 #include "includes.h"
+#ifdef CONFIG_MATCH_IFACE
+#include <net/if.h>
+#include <fnmatch.h>
+#endif /* CONFIG_MATCH_IFACE */
 
 #include "common.h"
 #include "crypto/random.h"
@@ -33,7 +37,9 @@
 #include "rsn_supp/pmksa_cache.h"
 #include "common/wpa_ctrl.h"
 #include "common/ieee802_11_defs.h"
+#include "common/hw_features_common.h"
 #include "p2p/p2p.h"
+#include "fst/fst.h"
 #include "blacklist.h"
 #include "wpas_glue.h"
 #include "wps_supplicant.h"
 #include "offchannel.h"
 #include "hs20_supplicant.h"
 #include "wnm_sta.h"
+#include "wpas_kay.h"
+#include "mesh.h"
 
-const char *wpa_supplicant_version =
+const char *const wpa_supplicant_version =
 "wpa_supplicant v" VERSION_STR "\n"
-"Copyright (c) 2003-2014, Jouni Malinen <j@w1.fi> and contributors";
+"Copyright (c) 2003-2016, Jouni Malinen <j@w1.fi> and contributors";
 
-const char *wpa_supplicant_license =
+const char *const wpa_supplicant_license =
 "This software may be distributed under the terms of the BSD license.\n"
 "See README for more details.\n"
 #ifdef EAP_TLS_OPENSSL
@@ -67,16 +75,16 @@ const char *wpa_supplicant_license =
 
 #ifndef CONFIG_NO_STDOUT_DEBUG
 /* Long text divided into parts in order to fit in C89 strings size limits. */
-const char *wpa_supplicant_full_license1 =
+const char *const wpa_supplicant_full_license1 =
 "";
-const char *wpa_supplicant_full_license2 =
+const char *const wpa_supplicant_full_license2 =
 "This software may be distributed under the terms of the BSD license.\n"
 "\n"
 "Redistribution and use in source and binary forms, with or without\n"
 "modification, are permitted provided that the following conditions are\n"
 "met:\n"
 "\n";
-const char *wpa_supplicant_full_license3 =
+const char *const wpa_supplicant_full_license3 =
 "1. Redistributions of source code must retain the above copyright\n"
 "   notice, this list of conditions and the following disclaimer.\n"
 "\n"
@@ -84,7 +92,7 @@ const char *wpa_supplicant_full_license3 =
 "   notice, this list of conditions and the following disclaimer in the\n"
 "   documentation and/or other materials provided with the distribution.\n"
 "\n";
-const char *wpa_supplicant_full_license4 =
+const char *const wpa_supplicant_full_license4 =
 "3. Neither the name(s) of the above-listed copyright holder(s) nor the\n"
 "   names of its contributors may be used to endorse or promote products\n"
 "   derived from this software without specific prior written permission.\n"
@@ -93,7 +101,7 @@ const char *wpa_supplicant_full_license4 =
 "\"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n"
 "LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\n"
 "A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\n";
-const char *wpa_supplicant_full_license5 =
+const char *const wpa_supplicant_full_license5 =
 "OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n"
 "SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\n"
 "LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\n"
@@ -130,6 +138,7 @@ int wpa_supplicant_set_wpa_none_key(struct wpa_supplicant *wpa_s,
        size_t keylen;
        enum wpa_alg alg;
        u8 seq[6] = { 0 };
+       int ret;
 
        /* IBSS/WPA-None uses only one key (Group) for both receiving and
         * sending unicast and multicast packets. */
@@ -173,7 +182,9 @@ int wpa_supplicant_set_wpa_none_key(struct wpa_supplicant *wpa_s,
        /* TODO: should actually remember the previously used seq#, both for TX
         * and RX from each STA.. */
 
-       return wpa_drv_set_key(wpa_s, alg, NULL, 0, 1, seq, 6, key, keylen);
+       ret = wpa_drv_set_key(wpa_s, alg, NULL, 0, 1, seq, 6, key, keylen);
+       os_memset(key, 0, sizeof(key));
+       return ret;
 }
 
 
@@ -296,7 +307,27 @@ void wpa_supplicant_initiate_eapol(struct wpa_supplicant *wpa_s)
                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;
+
+#ifdef CONFIG_WPS
+       if (wpa_s->key_mgmt == WPA_KEY_MGMT_WPS) {
+               eapol_conf.wps |= EAPOL_LOCAL_WPS_IN_USE;
+               if (wpa_s->current_bss) {
+                       struct wpabuf *ie;
+                       ie = wpa_bss_get_vendor_ie_multi(wpa_s->current_bss,
+                                                        WPS_IE_VENDOR_TYPE);
+                       if (ie) {
+                               if (wps_is_20(ie))
+                                       eapol_conf.wps |=
+                                               EAPOL_PEER_IS_WPS20_AP;
+                               wpabuf_free(ie);
+                       }
+               }
+       }
+#endif /* CONFIG_WPS */
+
        eapol_sm_notify_config(wpa_s->eapol, &ssid->eap, &eapol_conf);
+
+       ieee802_1x_alloc_kay_sm(wpa_s, ssid);
 #endif /* IEEE8021X_EAPOL */
 }
 
@@ -370,8 +401,22 @@ void free_hw_features(struct wpa_supplicant *wpa_s)
 }
 
 
+static void free_bss_tmp_disallowed(struct wpa_supplicant *wpa_s)
+{
+       struct wpa_bss_tmp_disallowed *bss, *prev;
+
+       dl_list_for_each_safe(bss, prev, &wpa_s->bss_tmp_disallowed,
+                             struct wpa_bss_tmp_disallowed, list) {
+               dl_list_del(&bss->list);
+               os_free(bss);
+       }
+}
+
+
 static void wpa_supplicant_cleanup(struct wpa_supplicant *wpa_s)
 {
+       int i;
+
        bgscan_deinit(wpa_s);
        autoscan_deinit(wpa_s);
        scard_deinit(wpa_s->scard);
@@ -384,6 +429,10 @@ static void wpa_supplicant_cleanup(struct wpa_supplicant *wpa_s)
                l2_packet_deinit(wpa_s->l2_br);
                wpa_s->l2_br = NULL;
        }
+#ifdef CONFIG_TESTING_OPTIONS
+       l2_packet_deinit(wpa_s->l2_test);
+       wpa_s->l2_test = NULL;
+#endif /* CONFIG_TESTING_OPTIONS */
 
        if (wpa_s->conf != NULL) {
                struct wpa_ssid *ssid;
@@ -407,6 +456,7 @@ static void wpa_supplicant_cleanup(struct wpa_supplicant *wpa_s)
        wpa_tdls_deinit(wpa_s->wpa);
 #endif /* CONFIG_TDLS */
 
+       wmm_ac_clear_saved_tspecs(wpa_s);
        pmksa_candidate_free(wpa_s->wpa);
        wpa_sm_deinit(wpa_s->wpa);
        wpa_s->wpa = NULL;
@@ -423,6 +473,8 @@ static void wpa_supplicant_cleanup(struct wpa_supplicant *wpa_s)
                             wpa_s, NULL);
 #endif /* CONFIG_DELAYED_MIC_ERROR_REPORT */
 
+       eloop_cancel_timeout(wpas_network_reenabled, wpa_s, NULL);
+
        wpas_wps_deinit(wpa_s);
 
        wpabuf_free(wpa_s->pending_eapol_rx);
@@ -439,9 +491,7 @@ static void wpa_supplicant_cleanup(struct wpa_supplicant *wpa_s)
        wpa_supplicant_ap_deinit(wpa_s);
 #endif /* CONFIG_AP */
 
-#ifdef CONFIG_P2P
        wpas_p2p_deinit(wpa_s);
-#endif /* CONFIG_P2P */
 
 #ifdef CONFIG_OFFCHANNEL
        offchannel_deinit(wpa_s);
@@ -455,11 +505,28 @@ static void wpa_supplicant_cleanup(struct wpa_supplicant *wpa_s)
        os_free(wpa_s->manual_scan_freqs);
        wpa_s->manual_scan_freqs = NULL;
 
+       os_free(wpa_s->manual_sched_scan_freqs);
+       wpa_s->manual_sched_scan_freqs = NULL;
+
+       wpas_mac_addr_rand_scan_clear(wpa_s, MAC_ADDR_RAND_ALL);
+
+       /*
+        * Need to remove any pending gas-query radio work before the
+        * gas_query_deinit() call because gas_query::work has not yet been set
+        * for works that have not been started. gas_query_free() will be unable
+        * to cancel such pending radio works and once the pending gas-query
+        * radio work eventually gets removed, the deinit notification call to
+        * gas_query_start_cb() would result in dereferencing freed memory.
+        */
+       if (wpa_s->radio)
+               radio_remove_works(wpa_s, "gas-query", 0);
        gas_query_deinit(wpa_s->gas);
        wpa_s->gas = NULL;
 
        free_hw_features(wpa_s);
 
+       ieee802_1x_dealloc_kay_sm(wpa_s);
+
        os_free(wpa_s->bssid_filter);
        wpa_s->bssid_filter = NULL;
 
@@ -485,8 +552,32 @@ static void wpa_supplicant_cleanup(struct wpa_supplicant *wpa_s)
        wpa_s->last_scan_res = NULL;
 
 #ifdef CONFIG_HS20
-       hs20_free_osu_prov(wpa_s);
+       if (wpa_s->drv_priv)
+               wpa_drv_configure_frame_filters(wpa_s, 0);
+       hs20_deinit(wpa_s);
 #endif /* CONFIG_HS20 */
+
+       for (i = 0; i < NUM_VENDOR_ELEM_FRAMES; i++) {
+               wpabuf_free(wpa_s->vendor_elem[i]);
+               wpa_s->vendor_elem[i] = NULL;
+       }
+
+       wmm_ac_notify_disassoc(wpa_s);
+
+       wpa_s->sched_scan_plans_num = 0;
+       os_free(wpa_s->sched_scan_plans);
+       wpa_s->sched_scan_plans = NULL;
+
+#ifdef CONFIG_MBO
+       wpa_s->non_pref_chan_num = 0;
+       os_free(wpa_s->non_pref_chan);
+       wpa_s->non_pref_chan = NULL;
+#endif /* CONFIG_MBO */
+
+       free_bss_tmp_disallowed(wpa_s);
+
+       wpabuf_free(wpa_s->lci);
+       wpa_s->lci = NULL;
 }
 
 
@@ -579,6 +670,10 @@ static void wpa_supplicant_start_bgscan(struct wpa_supplicant *wpa_s)
                return;
        if (wpa_s->current_ssid == wpa_s->bgscan_ssid)
                return;
+#ifdef CONFIG_P2P
+       if (wpa_s->p2p_group_interface != NOT_P2P_GROUP_INTERFACE)
+               return;
+#endif /* CONFIG_P2P */
 
        bgscan_deinit(wpa_s);
        if (wpa_s->current_ssid) {
@@ -661,8 +756,35 @@ void wpa_supplicant_set_state(struct wpa_supplicant *wpa_s,
                wpa_s->normal_scans = 0;
        }
 
-       if (state == WPA_COMPLETED)
+       if (state == WPA_COMPLETED) {
                wpas_connect_work_done(wpa_s);
+               /* Reinitialize normal_scan counter */
+               wpa_s->normal_scans = 0;
+       }
+
+#ifdef CONFIG_P2P
+       /*
+        * P2PS client has to reply to Probe Request frames received on the
+        * group operating channel. Enable Probe Request frame reporting for
+        * P2P connected client in case p2p_cli_probe configuration property is
+        * set to 1.
+        */
+       if (wpa_s->conf->p2p_cli_probe && wpa_s->current_ssid &&
+           wpa_s->current_ssid->mode == WPAS_MODE_INFRA &&
+           wpa_s->current_ssid->p2p_group) {
+               if (state == WPA_COMPLETED && !wpa_s->p2p_cli_probe) {
+                       wpa_dbg(wpa_s, MSG_DEBUG,
+                               "P2P: Enable CLI Probe Request RX reporting");
+                       wpa_s->p2p_cli_probe =
+                               wpa_drv_probe_req_report(wpa_s, 1) >= 0;
+               } else if (state != WPA_COMPLETED && wpa_s->p2p_cli_probe) {
+                       wpa_dbg(wpa_s, MSG_DEBUG,
+                               "P2P: Disable CLI Probe Request RX reporting");
+                       wpa_s->p2p_cli_probe = 0;
+                       wpa_drv_probe_req_report(wpa_s, 0);
+               }
+       }
+#endif /* CONFIG_P2P */
 
        if (state != WPA_SCANNING)
                wpa_supplicant_notify_scanning(wpa_s, 0);
@@ -677,6 +799,7 @@ void wpa_supplicant_set_state(struct wpa_supplicant *wpa_s,
                        ssid && ssid->id_str ? ssid->id_str : "");
 #endif /* CONFIG_CTRL_IFACE || !CONFIG_NO_STDOUT_DEBUG */
                wpas_clear_temp_disabled(wpa_s, ssid, 1);
+               wpa_blacklist_clear(wpa_s);
                wpa_s->extra_blacklist_count = 0;
                wpa_s->new_connection = 0;
                wpa_drv_set_operstate(wpa_s, 1);
@@ -685,9 +808,7 @@ void wpa_supplicant_set_state(struct wpa_supplicant *wpa_s,
 #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 */
 
                sme_sched_obss_scan(wpa_s, 1);
        } else if (state == WPA_DISCONNECTED || state == WPA_ASSOCIATING ||
@@ -714,9 +835,18 @@ void wpa_supplicant_set_state(struct wpa_supplicant *wpa_s,
        if (state == WPA_DISCONNECTED || state == WPA_INACTIVE)
                wpa_supplicant_start_autoscan(wpa_s);
 
+       if (old_state >= WPA_ASSOCIATED && wpa_s->wpa_state < WPA_ASSOCIATED)
+               wmm_ac_notify_disassoc(wpa_s);
+
        if (wpa_s->wpa_state != old_state) {
                wpas_notify_state_changed(wpa_s, wpa_s->wpa_state, old_state);
 
+               /*
+                * Notify the P2P Device interface about a state change in one
+                * of the interfaces.
+                */
+               wpas_p2p_indicate_state_change(wpa_s);
+
                if (wpa_s->wpa_state == WPA_COMPLETED ||
                    old_state == WPA_COMPLETED)
                        wpas_notify_auth_changed(wpa_s);
@@ -731,13 +861,13 @@ void wpa_supplicant_terminate_proc(struct wpa_global *global)
        struct wpa_supplicant *wpa_s = global->ifaces;
        while (wpa_s) {
                struct wpa_supplicant *next = wpa_s->next;
+               if (wpas_wps_terminate_pending(wpa_s) == 1)
+                       pending = 1;
 #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 = next;
        }
 #endif /* CONFIG_WPS */
@@ -811,13 +941,15 @@ int wpa_supplicant_reload_configuration(struct wpa_supplicant *wpa_s)
 
        eapol_sm_invalidate_cached_session(wpa_s->eapol);
        if (wpa_s->current_ssid) {
+               if (wpa_s->wpa_state >= WPA_AUTHENTICATING)
+                       wpa_s->own_disconnect_req = 1;
                wpa_supplicant_deauthenticate(wpa_s,
                                              WLAN_REASON_DEAUTH_LEAVING);
        }
 
        /*
         * TODO: should notify EAPOL SM about changes in opensc_engine_path,
-        * pkcs11_engine_path, pkcs11_module_path.
+        * pkcs11_engine_path, pkcs11_module_path, openssl_ciphers.
         */
        if (wpa_key_mgmt_wpa_psk(wpa_s->key_mgmt)) {
                /*
@@ -864,6 +996,11 @@ static void wpa_supplicant_reconfig(int sig, void *signal_ctx)
                        wpa_supplicant_terminate_proc(global);
                }
        }
+
+       if (wpa_debug_reopen_file() < 0) {
+               /* Ignore errors since we cannot really do much to fix this */
+               wpa_printf(MSG_DEBUG, "Could not reopen debug log file");
+       }
 }
 
 
@@ -903,9 +1040,7 @@ static int wpa_supplicant_suites_from_ai(struct wpa_supplicant *wpa_s,
 
 #ifdef CONFIG_IEEE80211W
        if (!(ie->capabilities & WPA_CAPABILITY_MFPC) &&
-           (ssid->ieee80211w == MGMT_FRAME_PROTECTION_DEFAULT ?
-            wpa_s->conf->pmf : ssid->ieee80211w) ==
-           MGMT_FRAME_PROTECTION_REQUIRED) {
+           wpas_get_ssid_pmf(wpa_s, ssid) == MGMT_FRAME_PROTECTION_REQUIRED) {
                wpa_msg(wpa_s, MSG_INFO, "WPA: Driver associated with an AP "
                        "that does not support management frame protection - "
                        "reject");
@@ -954,7 +1089,7 @@ int wpa_supplicant_set_suites(struct wpa_supplicant *wpa_s,
                wpa_dbg(wpa_s, MSG_DEBUG, "RSN: using IEEE 802.11i/D9.0");
                proto = WPA_PROTO_RSN;
        } else if (bss_wpa && (ssid->proto & WPA_PROTO_WPA) &&
-                  wpa_parse_wpa_ie(bss_wpa, 2 +bss_wpa[1], &ie) == 0 &&
+                  wpa_parse_wpa_ie(bss_wpa, 2 + bss_wpa[1], &ie) == 0 &&
                   (ie.group_cipher & ssid->group_cipher) &&
                   (ie.pairwise_cipher & ssid->pairwise_cipher) &&
                   (ie.key_mgmt & ssid->key_mgmt)) {
@@ -964,6 +1099,7 @@ int wpa_supplicant_set_suites(struct wpa_supplicant *wpa_s,
        } else if (bss_osen && (ssid->proto & WPA_PROTO_OSEN)) {
                wpa_dbg(wpa_s, MSG_DEBUG, "HS 2.0: using OSEN");
                /* TODO: parse OSEN element */
+               os_memset(&ie, 0, sizeof(ie));
                ie.group_cipher = WPA_CIPHER_CCMP;
                ie.pairwise_cipher = WPA_CIPHER_CCMP;
                ie.key_mgmt = WPA_KEY_MGMT_OSEN;
@@ -971,6 +1107,40 @@ int wpa_supplicant_set_suites(struct wpa_supplicant *wpa_s,
 #endif /* CONFIG_HS20 */
        } else if (bss) {
                wpa_msg(wpa_s, MSG_WARNING, "WPA: Failed to select WPA/RSN");
+               wpa_dbg(wpa_s, MSG_DEBUG,
+                       "WPA: ssid proto=0x%x pairwise_cipher=0x%x group_cipher=0x%x key_mgmt=0x%x",
+                       ssid->proto, ssid->pairwise_cipher, ssid->group_cipher,
+                       ssid->key_mgmt);
+               wpa_dbg(wpa_s, MSG_DEBUG, "WPA: BSS " MACSTR " ssid='%s'%s%s%s",
+                       MAC2STR(bss->bssid),
+                       wpa_ssid_txt(bss->ssid, bss->ssid_len),
+                       bss_wpa ? " WPA" : "",
+                       bss_rsn ? " RSN" : "",
+                       bss_osen ? " OSEN" : "");
+               if (bss_rsn) {
+                       wpa_hexdump(MSG_DEBUG, "RSN", bss_rsn, 2 + bss_rsn[1]);
+                       if (wpa_parse_wpa_ie(bss_rsn, 2 + bss_rsn[1], &ie)) {
+                               wpa_dbg(wpa_s, MSG_DEBUG,
+                                       "Could not parse RSN element");
+                       } else {
+                               wpa_dbg(wpa_s, MSG_DEBUG,
+                                       "RSN: pairwise_cipher=0x%x group_cipher=0x%x key_mgmt=0x%x",
+                                       ie.pairwise_cipher, ie.group_cipher,
+                                       ie.key_mgmt);
+                       }
+               }
+               if (bss_wpa) {
+                       wpa_hexdump(MSG_DEBUG, "WPA", bss_wpa, 2 + bss_wpa[1]);
+                       if (wpa_parse_wpa_ie(bss_wpa, 2 + bss_wpa[1], &ie)) {
+                               wpa_dbg(wpa_s, MSG_DEBUG,
+                                       "Could not parse WPA element");
+                       } else {
+                               wpa_dbg(wpa_s, MSG_DEBUG,
+                                       "WPA: pairwise_cipher=0x%x group_cipher=0x%x key_mgmt=0x%x",
+                                       ie.pairwise_cipher, ie.group_cipher,
+                                       ie.key_mgmt);
+                       }
+               }
                return -1;
        } else {
                if (ssid->proto & WPA_PROTO_OSEN)
@@ -1018,6 +1188,10 @@ int wpa_supplicant_set_suites(struct wpa_supplicant *wpa_s,
                        return -1;
        }
 
+#ifdef CONFIG_NO_WPA
+       wpa_s->group_cipher = WPA_CIPHER_NONE;
+       wpa_s->pairwise_cipher = WPA_CIPHER_NONE;
+#else /* CONFIG_NO_WPA */
        sel = ie.group_cipher & ssid->group_cipher;
        wpa_s->group_cipher = wpa_pick_group_cipher(sel);
        if (wpa_s->group_cipher < 0) {
@@ -1037,6 +1211,7 @@ int wpa_supplicant_set_suites(struct wpa_supplicant *wpa_s,
        }
        wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using PTK %s",
                wpa_cipher_txt(wpa_s->pairwise_cipher));
+#endif /* CONFIG_NO_WPA */
 
        sel = ie.key_mgmt & ssid->key_mgmt;
 #ifdef CONFIG_SAE
@@ -1044,6 +1219,18 @@ int wpa_supplicant_set_suites(struct wpa_supplicant *wpa_s,
                sel &= ~(WPA_KEY_MGMT_SAE | WPA_KEY_MGMT_FT_SAE);
 #endif /* CONFIG_SAE */
        if (0) {
+#ifdef CONFIG_SUITEB192
+       } else if (sel & WPA_KEY_MGMT_IEEE8021X_SUITE_B_192) {
+               wpa_s->key_mgmt = WPA_KEY_MGMT_IEEE8021X_SUITE_B_192;
+               wpa_dbg(wpa_s, MSG_DEBUG,
+                       "WPA: using KEY_MGMT 802.1X with Suite B (192-bit)");
+#endif /* CONFIG_SUITEB192 */
+#ifdef CONFIG_SUITEB
+       } else if (sel & WPA_KEY_MGMT_IEEE8021X_SUITE_B) {
+               wpa_s->key_mgmt = WPA_KEY_MGMT_IEEE8021X_SUITE_B;
+               wpa_dbg(wpa_s, MSG_DEBUG,
+                       "WPA: using KEY_MGMT 802.1X with Suite B");
+#endif /* CONFIG_SUITEB */
 #ifdef CONFIG_IEEE80211R
        } else if (sel & WPA_KEY_MGMT_FT_IEEE8021X) {
                wpa_s->key_mgmt = WPA_KEY_MGMT_FT_IEEE8021X;
@@ -1097,14 +1284,25 @@ int wpa_supplicant_set_suites(struct wpa_supplicant *wpa_s,
 
 #ifdef CONFIG_IEEE80211W
        sel = ie.mgmt_group_cipher;
-       if ((ssid->ieee80211w == MGMT_FRAME_PROTECTION_DEFAULT ?
-            wpa_s->conf->pmf : ssid->ieee80211w) == NO_MGMT_FRAME_PROTECTION ||
+       if (wpas_get_ssid_pmf(wpa_s, ssid) == NO_MGMT_FRAME_PROTECTION ||
            !(ie.capabilities & WPA_CAPABILITY_MFPC))
                sel = 0;
        if (sel & WPA_CIPHER_AES_128_CMAC) {
                wpa_s->mgmt_group_cipher = WPA_CIPHER_AES_128_CMAC;
                wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using MGMT group cipher "
                        "AES-128-CMAC");
+       } else if (sel & WPA_CIPHER_BIP_GMAC_128) {
+               wpa_s->mgmt_group_cipher = WPA_CIPHER_BIP_GMAC_128;
+               wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using MGMT group cipher "
+                       "BIP-GMAC-128");
+       } else if (sel & WPA_CIPHER_BIP_GMAC_256) {
+               wpa_s->mgmt_group_cipher = WPA_CIPHER_BIP_GMAC_256;
+               wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using MGMT group cipher "
+                       "BIP-GMAC-256");
+       } else if (sel & WPA_CIPHER_BIP_CMAC_256) {
+               wpa_s->mgmt_group_cipher = WPA_CIPHER_BIP_CMAC_256;
+               wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using MGMT group cipher "
+                       "BIP-CMAC-256");
        } else {
                wpa_s->mgmt_group_cipher = 0;
                wpa_dbg(wpa_s, MSG_DEBUG, "WPA: not using MGMT group cipher");
@@ -1112,8 +1310,7 @@ int wpa_supplicant_set_suites(struct wpa_supplicant *wpa_s,
        wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_MGMT_GROUP,
                         wpa_s->mgmt_group_cipher);
        wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_MFP,
-                        (ssid->ieee80211w == MGMT_FRAME_PROTECTION_DEFAULT ?
-                         wpa_s->conf->pmf : ssid->ieee80211w));
+                        wpas_get_ssid_pmf(wpa_s, ssid));
 #endif /* CONFIG_IEEE80211W */
 
        if (wpa_sm_set_assoc_wpa_ie_default(wpa_s->wpa, wpa_ie, wpa_ie_len)) {
@@ -1122,7 +1319,13 @@ int wpa_supplicant_set_suites(struct wpa_supplicant *wpa_s,
        }
 
        if (wpa_key_mgmt_wpa_psk(ssid->key_mgmt)) {
-               wpa_sm_set_pmk(wpa_s->wpa, ssid->psk, PMK_LEN);
+               int psk_set = 0;
+
+               if (ssid->psk_set) {
+                       wpa_sm_set_pmk(wpa_s->wpa, ssid->psk, PMK_LEN, NULL,
+                                      NULL);
+                       psk_set = 1;
+               }
 #ifndef CONFIG_NO_PBKDF2
                if (bss && ssid->bssid_set && ssid->ssid_len == 0 &&
                    ssid->passphrase) {
@@ -1131,7 +1334,9 @@ int wpa_supplicant_set_suites(struct wpa_supplicant *wpa_s,
                                    4096, psk, PMK_LEN);
                        wpa_hexdump_key(MSG_MSGDUMP, "PSK (from passphrase)",
                                        psk, PMK_LEN);
-                       wpa_sm_set_pmk(wpa_s->wpa, psk, PMK_LEN);
+                       wpa_sm_set_pmk(wpa_s->wpa, psk, PMK_LEN, NULL, NULL);
+                       psk_set = 1;
+                       os_memset(psk, 0, sizeof(psk));
                }
 #endif /* CONFIG_NO_PBKDF2 */
 #ifdef CONFIG_EXT_PASSWORD
@@ -1167,7 +1372,10 @@ int wpa_supplicant_set_suites(struct wpa_supplicant *wpa_s,
                                wpa_hexdump_key(MSG_MSGDUMP, "PSK (from "
                                                "external passphrase)",
                                                psk, PMK_LEN);
-                               wpa_sm_set_pmk(wpa_s->wpa, psk, PMK_LEN);
+                               wpa_sm_set_pmk(wpa_s->wpa, psk, PMK_LEN, NULL,
+                                              NULL);
+                               psk_set = 1;
+                               os_memset(psk, 0, sizeof(psk));
                        } else
 #endif /* CONFIG_NO_PBKDF2 */
                        if (wpabuf_len(pw) == 2 * PMK_LEN) {
@@ -1178,7 +1386,10 @@ int wpa_supplicant_set_suites(struct wpa_supplicant *wpa_s,
                                        ext_password_free(pw);
                                        return -1;
                                }
-                               wpa_sm_set_pmk(wpa_s->wpa, psk, PMK_LEN);
+                               wpa_sm_set_pmk(wpa_s->wpa, psk, PMK_LEN, NULL,
+                                              NULL);
+                               psk_set = 1;
+                               os_memset(psk, 0, sizeof(psk));
                        } else {
                                wpa_msg(wpa_s, MSG_INFO, "EXT PW: No suitable "
                                        "PSK available");
@@ -1191,6 +1402,12 @@ int wpa_supplicant_set_suites(struct wpa_supplicant *wpa_s,
                        ext_password_free(pw);
                }
 #endif /* CONFIG_EXT_PASSWORD */
+
+               if (!psk_set) {
+                       wpa_msg(wpa_s, MSG_INFO,
+                               "No PSK available for association");
+                       return -1;
+               }
        } else
                wpa_sm_set_pmk_from_pmksa(wpa_s->wpa);
 
@@ -1233,6 +1450,9 @@ static void wpas_ext_capab_byte(struct wpa_supplicant *wpa_s, u8 *pos, int idx)
                if (wpa_s->conf->hs20)
                        *pos |= 0x40; /* Bit 46 - WNM-Notification */
 #endif /* CONFIG_HS20 */
+#ifdef CONFIG_MBO
+               *pos |= 0x40; /* Bit 46 - WNM-Notification */
+#endif /* CONFIG_MBO */
                break;
        case 6: /* Bits 48-55 */
                break;
@@ -1240,13 +1460,18 @@ static void wpas_ext_capab_byte(struct wpa_supplicant *wpa_s, u8 *pos, int idx)
 }
 
 
-int wpas_build_ext_capab(struct wpa_supplicant *wpa_s, u8 *buf)
+int wpas_build_ext_capab(struct wpa_supplicant *wpa_s, u8 *buf, size_t buflen)
 {
        u8 *pos = buf;
        u8 len = 6, i;
 
        if (len < wpa_s->extended_capa_len)
                len = wpa_s->extended_capa_len;
+       if (buflen < (size_t) len + 2) {
+               wpa_printf(MSG_INFO,
+                          "Not enough room for building extended capabilities element");
+               return -1;
+       }
 
        *pos++ = WLAN_EID_EXT_CAPAB;
        *pos++ = len;
@@ -1332,6 +1557,68 @@ void wpas_connect_work_done(struct wpa_supplicant *wpa_s)
 }
 
 
+int wpas_update_random_addr(struct wpa_supplicant *wpa_s, int style)
+{
+       struct os_reltime now;
+       u8 addr[ETH_ALEN];
+
+       os_get_reltime(&now);
+       if (wpa_s->last_mac_addr_style == style &&
+           wpa_s->last_mac_addr_change.sec != 0 &&
+           !os_reltime_expired(&now, &wpa_s->last_mac_addr_change,
+                               wpa_s->conf->rand_addr_lifetime)) {
+               wpa_msg(wpa_s, MSG_DEBUG,
+                       "Previously selected random MAC address has not yet expired");
+               return 0;
+       }
+
+       switch (style) {
+       case 1:
+               if (random_mac_addr(addr) < 0)
+                       return -1;
+               break;
+       case 2:
+               os_memcpy(addr, wpa_s->perm_addr, ETH_ALEN);
+               if (random_mac_addr_keep_oui(addr) < 0)
+                       return -1;
+               break;
+       default:
+               return -1;
+       }
+
+       if (wpa_drv_set_mac_addr(wpa_s, addr) < 0) {
+               wpa_msg(wpa_s, MSG_INFO,
+                       "Failed to set random MAC address");
+               return -1;
+       }
+
+       os_get_reltime(&wpa_s->last_mac_addr_change);
+       wpa_s->mac_addr_changed = 1;
+       wpa_s->last_mac_addr_style = style;
+
+       if (wpa_supplicant_update_mac_addr(wpa_s) < 0) {
+               wpa_msg(wpa_s, MSG_INFO,
+                       "Could not update MAC address information");
+               return -1;
+       }
+
+       wpa_msg(wpa_s, MSG_DEBUG, "Using random MAC address " MACSTR,
+               MAC2STR(addr));
+
+       return 0;
+}
+
+
+int wpas_update_random_addr_disassoc(struct wpa_supplicant *wpa_s)
+{
+       if (wpa_s->wpa_state >= WPA_AUTHENTICATING ||
+           !wpa_s->conf->preassoc_mac_addr)
+               return 0;
+
+       return wpas_update_random_addr(wpa_s, wpa_s->conf->preassoc_mac_addr);
+}
+
+
 static void wpas_start_assoc_cb(struct wpa_radio_work *work, int deinit);
 
 /**
@@ -1346,6 +1633,52 @@ void wpa_supplicant_associate(struct wpa_supplicant *wpa_s,
                              struct wpa_bss *bss, struct wpa_ssid *ssid)
 {
        struct wpa_connect_work *cwork;
+       int rand_style;
+
+       wpa_s->own_disconnect_req = 0;
+
+       /*
+        * If we are starting a new connection, any previously pending EAPOL
+        * RX cannot be valid anymore.
+        */
+       wpabuf_free(wpa_s->pending_eapol_rx);
+       wpa_s->pending_eapol_rx = NULL;
+
+       if (ssid->mac_addr == -1)
+               rand_style = wpa_s->conf->mac_addr;
+       else
+               rand_style = ssid->mac_addr;
+
+       wmm_ac_clear_saved_tspecs(wpa_s);
+       wpa_s->reassoc_same_bss = 0;
+       wpa_s->reassoc_same_ess = 0;
+
+       if (wpa_s->last_ssid == ssid) {
+               wpa_dbg(wpa_s, MSG_DEBUG, "Re-association to the same ESS");
+               wpa_s->reassoc_same_ess = 1;
+               if (wpa_s->current_bss && wpa_s->current_bss == bss) {
+                       wmm_ac_save_tspecs(wpa_s);
+                       wpa_s->reassoc_same_bss = 1;
+               }
+       } else if (rand_style > 0) {
+               if (wpas_update_random_addr(wpa_s, rand_style) < 0)
+                       return;
+               wpa_sm_pmksa_cache_flush(wpa_s->wpa, ssid);
+       } else if (wpa_s->mac_addr_changed) {
+               if (wpa_drv_set_mac_addr(wpa_s, NULL) < 0) {
+                       wpa_msg(wpa_s, MSG_INFO,
+                               "Could not restore permanent MAC address");
+                       return;
+               }
+               wpa_s->mac_addr_changed = 0;
+               if (wpa_supplicant_update_mac_addr(wpa_s) < 0) {
+                       wpa_msg(wpa_s, MSG_INFO,
+                               "Could not update MAC address information");
+                       return;
+               }
+               wpa_msg(wpa_s, MSG_DEBUG, "Using permanent MAC address");
+       }
+       wpa_s->last_ssid = ssid;
 
 #ifdef CONFIG_IBSS_RSN
        ibss_rsn_deinit(wpa_s->ibss_rsn);
@@ -1374,6 +1707,30 @@ void wpa_supplicant_associate(struct wpa_supplicant *wpa_s,
                return;
        }
 
+       if (ssid->mode == WPAS_MODE_MESH) {
+#ifdef CONFIG_MESH
+               if (!(wpa_s->drv_flags & WPA_DRIVER_FLAGS_MESH)) {
+                       wpa_msg(wpa_s, MSG_INFO,
+                               "Driver does not support mesh mode");
+                       return;
+               }
+               if (bss)
+                       ssid->frequency = bss->freq;
+               if (wpa_supplicant_join_mesh(wpa_s, ssid) < 0) {
+                       wpa_msg(wpa_s, MSG_ERROR, "Could not join mesh");
+                       return;
+               }
+               wpa_s->current_bss = bss;
+               wpa_msg(wpa_s, MSG_INFO, MESH_GROUP_STARTED "ssid=\"%s\" id=%d",
+                       wpa_ssid_txt(ssid->ssid, ssid->ssid_len),
+                       ssid->id);
+#else /* CONFIG_MESH */
+               wpa_msg(wpa_s, MSG_ERROR,
+                       "mesh mode support not included in the build");
+#endif /* CONFIG_MESH */
+               return;
+       }
+
 #ifdef CONFIG_TDLS
        if (bss)
                wpa_tdls_ap_ies(wpa_s->wpa, (const u8 *) (bss + 1),
@@ -1396,6 +1753,8 @@ void wpa_supplicant_associate(struct wpa_supplicant *wpa_s,
                return;
        }
 
+       wpas_abort_ongoing_scan(wpa_s);
+
        cwork = os_zalloc(sizeof(*cwork));
        if (cwork == NULL)
                return;
@@ -1410,107 +1769,417 @@ void wpa_supplicant_associate(struct wpa_supplicant *wpa_s,
 }
 
 
-static void wpas_start_assoc_cb(struct wpa_radio_work *work, int deinit)
+static int bss_is_ibss(struct wpa_bss *bss)
 {
-       struct wpa_connect_work *cwork = work->ctx;
-       struct wpa_bss *bss = cwork->bss;
-       struct wpa_ssid *ssid = cwork->ssid;
-       struct wpa_supplicant *wpa_s = work->wpa_s;
-       u8 wpa_ie[200];
-       size_t wpa_ie_len;
-       int use_crypt, ret, i, bssid_changed;
-       int algs = WPA_AUTH_ALG_OPEN;
-       unsigned int cipher_pairwise, cipher_group;
-       struct wpa_driver_associate_params params;
-       int wep_keys_set = 0;
-       int assoc_failed = 0;
-       struct wpa_ssid *old_ssid;
+       return (bss->caps & (IEEE80211_CAP_ESS | IEEE80211_CAP_IBSS)) ==
+               IEEE80211_CAP_IBSS;
+}
+
+
+static int drv_supports_vht(struct wpa_supplicant *wpa_s,
+                           const struct wpa_ssid *ssid)
+{
+       enum hostapd_hw_mode hw_mode;
+       struct hostapd_hw_modes *mode = NULL;
+       u8 channel;
+       int i;
+
 #ifdef CONFIG_HT_OVERRIDES
-       struct ieee80211_ht_capabilities htcaps;
-       struct ieee80211_ht_capabilities htcaps_mask;
+       if (ssid->disable_ht)
+               return 0;
 #endif /* CONFIG_HT_OVERRIDES */
 
-       if (deinit) {
-               if (work->started) {
-                       wpa_s->connect_work = NULL;
+       hw_mode = ieee80211_freq_to_chan(ssid->frequency, &channel);
+       if (hw_mode == NUM_HOSTAPD_MODES)
+               return 0;
+       for (i = 0; wpa_s->hw.modes && i < wpa_s->hw.num_modes; i++) {
+               if (wpa_s->hw.modes[i].mode == hw_mode) {
+                       mode = &wpa_s->hw.modes[i];
+                       break;
+               }
+       }
 
-                       /* cancel possible auth. timeout */
-                       eloop_cancel_timeout(wpa_supplicant_timeout, wpa_s,
-                                            NULL);
+       if (!mode)
+               return 0;
+
+       return mode->vht_capab != 0;
+}
+
+
+void ibss_mesh_setup_freq(struct wpa_supplicant *wpa_s,
+                         const struct wpa_ssid *ssid,
+                         struct hostapd_freq_params *freq)
+{
+       enum hostapd_hw_mode hw_mode;
+       struct hostapd_hw_modes *mode = NULL;
+       int ht40plus[] = { 36, 44, 52, 60, 100, 108, 116, 124, 132, 149, 157,
+                          184, 192 };
+       int vht80[] = { 36, 52, 100, 116, 132, 149 };
+       struct hostapd_channel_data *pri_chan = NULL, *sec_chan = NULL;
+       u8 channel;
+       int i, chan_idx, ht40 = -1, res, obss_scan = 1;
+       unsigned int j, k;
+       struct hostapd_freq_params vht_freq;
+       int chwidth, seg0, seg1;
+       u32 vht_caps = 0;
+
+       freq->freq = ssid->frequency;
+
+       for (j = 0; j < wpa_s->last_scan_res_used; j++) {
+               struct wpa_bss *bss = wpa_s->last_scan_res[j];
+
+               if (ssid->mode != WPAS_MODE_IBSS)
+                       break;
+
+               /* Don't adjust control freq in case of fixed_freq */
+               if (ssid->fixed_freq)
+                       break;
+
+               if (!bss_is_ibss(bss))
+                       continue;
+
+               if (ssid->ssid_len == bss->ssid_len &&
+                   os_memcmp(ssid->ssid, bss->ssid, bss->ssid_len) == 0) {
+                       wpa_printf(MSG_DEBUG,
+                                  "IBSS already found in scan results, adjust control freq: %d",
+                                  bss->freq);
+                       freq->freq = bss->freq;
+                       obss_scan = 0;
+                       break;
                }
-               wpas_connect_work_free(cwork);
-               return;
        }
 
-       wpa_s->connect_work = work;
+       /* For IBSS check HT_IBSS flag */
+       if (ssid->mode == WPAS_MODE_IBSS &&
+           !(wpa_s->drv_flags & WPA_DRIVER_FLAGS_HT_IBSS))
+               return;
 
-       if (!wpas_valid_bss_ssid(wpa_s, bss, ssid)) {
-               wpa_dbg(wpa_s, MSG_DEBUG, "BSS/SSID entry for association not valid anymore - drop connection attempt");
-               wpas_connect_work_done(wpa_s);
+       if (wpa_s->group_cipher == WPA_CIPHER_WEP40 ||
+           wpa_s->group_cipher == WPA_CIPHER_WEP104 ||
+           wpa_s->pairwise_cipher == WPA_CIPHER_TKIP) {
+               wpa_printf(MSG_DEBUG,
+                          "IBSS: WEP/TKIP detected, do not try to enable HT");
                return;
        }
 
-       os_memset(&params, 0, sizeof(params));
-       wpa_s->reassociate = 0;
-       wpa_s->eap_expected_failure = 0;
-       if (bss && !wpas_driver_bss_selection(wpa_s)) {
-#ifdef CONFIG_IEEE80211R
-               const u8 *ie, *md = NULL;
-#endif /* CONFIG_IEEE80211R */
-               wpa_msg(wpa_s, MSG_INFO, "Trying to associate with " MACSTR
-                       " (SSID='%s' freq=%d MHz)", MAC2STR(bss->bssid),
-                       wpa_ssid_txt(bss->ssid, bss->ssid_len), bss->freq);
-               bssid_changed = !is_zero_ether_addr(wpa_s->bssid);
-               os_memset(wpa_s->bssid, 0, ETH_ALEN);
-               os_memcpy(wpa_s->pending_bssid, bss->bssid, ETH_ALEN);
-               if (bssid_changed)
-                       wpas_notify_bssid_changed(wpa_s);
-#ifdef CONFIG_IEEE80211R
-               ie = wpa_bss_get_ie(bss, WLAN_EID_MOBILITY_DOMAIN);
-               if (ie && ie[1] >= MOBILITY_DOMAIN_ID_LEN)
-                       md = ie + 2;
-               wpa_sm_set_ft_params(wpa_s->wpa, ie, ie ? 2 + ie[1] : 0);
-               if (md) {
-                       /* Prepare for the next transition */
-                       wpa_ft_prepare_auth_request(wpa_s->wpa, ie);
+       hw_mode = ieee80211_freq_to_chan(freq->freq, &channel);
+       for (i = 0; wpa_s->hw.modes && i < wpa_s->hw.num_modes; i++) {
+               if (wpa_s->hw.modes[i].mode == hw_mode) {
+                       mode = &wpa_s->hw.modes[i];
+                       break;
                }
-#endif /* CONFIG_IEEE80211R */
-#ifdef CONFIG_WPS
-       } else if ((ssid->ssid == NULL || ssid->ssid_len == 0) &&
-                  wpa_s->conf->ap_scan == 2 &&
-                  (ssid->key_mgmt & WPA_KEY_MGMT_WPS)) {
-               /* Use ap_scan==1 style network selection to find the network
-                */
-               wpa_s->scan_req = MANUAL_SCAN_REQ;
-               wpa_s->reassociate = 1;
-               wpa_supplicant_req_scan(wpa_s, 0, 0);
+       }
+
+       if (!mode)
                return;
-#endif /* CONFIG_WPS */
-       } else {
-               wpa_msg(wpa_s, MSG_INFO, "Trying to associate with SSID '%s'",
-                       wpa_ssid_txt(ssid->ssid, ssid->ssid_len));
-               os_memset(wpa_s->pending_bssid, 0, ETH_ALEN);
+
+       freq->ht_enabled = ht_supported(mode);
+       if (!freq->ht_enabled)
+               return;
+
+       /* Setup higher BW only for 5 GHz */
+       if (mode->mode != HOSTAPD_MODE_IEEE80211A)
+               return;
+
+       for (chan_idx = 0; chan_idx < mode->num_channels; chan_idx++) {
+               pri_chan = &mode->channels[chan_idx];
+               if (pri_chan->chan == channel)
+                       break;
+               pri_chan = NULL;
        }
-       wpa_supplicant_cancel_sched_scan(wpa_s);
-       wpa_supplicant_cancel_scan(wpa_s);
+       if (!pri_chan)
+               return;
 
-       /* Starting new association, so clear the possibly used WPA IE from the
-        * previous association. */
-       wpa_sm_set_assoc_wpa_ie(wpa_s->wpa, NULL, 0);
+       /* Check primary channel flags */
+       if (pri_chan->flag & (HOSTAPD_CHAN_DISABLED | HOSTAPD_CHAN_NO_IR))
+               return;
 
-#ifdef IEEE8021X_EAPOL
-       if (ssid->key_mgmt & WPA_KEY_MGMT_IEEE8021X_NO_WPA) {
-               if (ssid->leap) {
-                       if (ssid->non_leap == 0)
-                               algs = WPA_AUTH_ALG_LEAP;
-                       else
-                               algs |= WPA_AUTH_ALG_LEAP;
+       /* Check/setup HT40+/HT40- */
+       for (j = 0; j < ARRAY_SIZE(ht40plus); j++) {
+               if (ht40plus[j] == channel) {
+                       ht40 = 1;
+                       break;
                }
        }
-#endif /* IEEE8021X_EAPOL */
-       wpa_dbg(wpa_s, MSG_DEBUG, "Automatic auth_alg selection: 0x%x", algs);
-       if (ssid->auth_alg) {
-               algs = ssid->auth_alg;
+
+       /* Find secondary channel */
+       for (i = 0; i < mode->num_channels; i++) {
+               sec_chan = &mode->channels[i];
+               if (sec_chan->chan == channel + ht40 * 4)
+                       break;
+               sec_chan = NULL;
+       }
+       if (!sec_chan)
+               return;
+
+       /* Check secondary channel flags */
+       if (sec_chan->flag & (HOSTAPD_CHAN_DISABLED | HOSTAPD_CHAN_NO_IR))
+               return;
+
+       freq->channel = pri_chan->chan;
+
+       switch (ht40) {
+       case -1:
+               if (!(pri_chan->flag & HOSTAPD_CHAN_HT40MINUS))
+                       return;
+               freq->sec_channel_offset = -1;
+               break;
+       case 1:
+               if (!(pri_chan->flag & HOSTAPD_CHAN_HT40PLUS))
+                       return;
+               freq->sec_channel_offset = 1;
+               break;
+       default:
+               break;
+       }
+
+       if (freq->sec_channel_offset && obss_scan) {
+               struct wpa_scan_results *scan_res;
+
+               scan_res = wpa_supplicant_get_scan_results(wpa_s, NULL, 0);
+               if (scan_res == NULL) {
+                       /* Back to HT20 */
+                       freq->sec_channel_offset = 0;
+                       return;
+               }
+
+               res = check_40mhz_5g(mode, scan_res, pri_chan->chan,
+                                    sec_chan->chan);
+               switch (res) {
+               case 0:
+                       /* Back to HT20 */
+                       freq->sec_channel_offset = 0;
+                       break;
+               case 1:
+                       /* Configuration allowed */
+                       break;
+               case 2:
+                       /* Switch pri/sec channels */
+                       freq->freq = hw_get_freq(mode, sec_chan->chan);
+                       freq->sec_channel_offset = -freq->sec_channel_offset;
+                       freq->channel = sec_chan->chan;
+                       break;
+               default:
+                       freq->sec_channel_offset = 0;
+                       break;
+               }
+
+               wpa_scan_results_free(scan_res);
+       }
+
+       wpa_printf(MSG_DEBUG,
+                  "IBSS/mesh: setup freq channel %d, sec_channel_offset %d",
+                  freq->channel, freq->sec_channel_offset);
+
+       if (!drv_supports_vht(wpa_s, ssid))
+               return;
+
+       /* For IBSS check VHT_IBSS flag */
+       if (ssid->mode == WPAS_MODE_IBSS &&
+           !(wpa_s->drv_flags & WPA_DRIVER_FLAGS_VHT_IBSS))
+               return;
+
+       vht_freq = *freq;
+
+       vht_freq.vht_enabled = vht_supported(mode);
+       if (!vht_freq.vht_enabled)
+               return;
+
+       /* setup center_freq1, bandwidth */
+       for (j = 0; j < ARRAY_SIZE(vht80); j++) {
+               if (freq->channel >= vht80[j] &&
+                   freq->channel < vht80[j] + 16)
+                       break;
+       }
+
+       if (j == ARRAY_SIZE(vht80))
+               return;
+
+       for (i = vht80[j]; i < vht80[j] + 16; i += 4) {
+               struct hostapd_channel_data *chan;
+
+               chan = hw_get_channel_chan(mode, i, NULL);
+               if (!chan)
+                       return;
+
+               /* Back to HT configuration if channel not usable */
+               if (chan->flag & (HOSTAPD_CHAN_DISABLED | HOSTAPD_CHAN_NO_IR))
+                       return;
+       }
+
+       chwidth = VHT_CHANWIDTH_80MHZ;
+       seg0 = vht80[j] + 6;
+       seg1 = 0;
+
+       if (ssid->max_oper_chwidth == VHT_CHANWIDTH_80P80MHZ) {
+               /* setup center_freq2, bandwidth */
+               for (k = 0; k < ARRAY_SIZE(vht80); k++) {
+                       /* Only accept 80 MHz segments separated by a gap */
+                       if (j == k || abs(vht80[j] - vht80[k]) == 16)
+                               continue;
+                       for (i = vht80[k]; i < vht80[k] + 16; i += 4) {
+                               struct hostapd_channel_data *chan;
+
+                               chan = hw_get_channel_chan(mode, i, NULL);
+                               if (!chan)
+                                       continue;
+
+                               if (chan->flag & (HOSTAPD_CHAN_DISABLED |
+                                                 HOSTAPD_CHAN_NO_IR |
+                                                 HOSTAPD_CHAN_RADAR))
+                                       continue;
+
+                               /* Found a suitable second segment for 80+80 */
+                               chwidth = VHT_CHANWIDTH_80P80MHZ;
+                               vht_caps |=
+                                       VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ;
+                               seg1 = vht80[k] + 6;
+                       }
+
+                       if (chwidth == VHT_CHANWIDTH_80P80MHZ)
+                               break;
+               }
+       } else if (ssid->max_oper_chwidth == VHT_CHANWIDTH_160MHZ) {
+               if (freq->freq == 5180) {
+                       chwidth = VHT_CHANWIDTH_160MHZ;
+                       vht_caps |= VHT_CAP_SUPP_CHAN_WIDTH_160MHZ;
+                       seg0 = 50;
+               } else if (freq->freq == 5520) {
+                       chwidth = VHT_CHANWIDTH_160MHZ;
+                       vht_caps |= VHT_CAP_SUPP_CHAN_WIDTH_160MHZ;
+                       seg0 = 114;
+               }
+       }
+
+       if (hostapd_set_freq_params(&vht_freq, mode->mode, freq->freq,
+                                   freq->channel, freq->ht_enabled,
+                                   vht_freq.vht_enabled,
+                                   freq->sec_channel_offset,
+                                   chwidth, seg0, seg1, vht_caps) != 0)
+               return;
+
+       *freq = vht_freq;
+
+       wpa_printf(MSG_DEBUG, "IBSS: VHT setup freq cf1 %d, cf2 %d, bw %d",
+                  freq->center_freq1, freq->center_freq2, freq->bandwidth);
+}
+
+
+static void wpas_start_assoc_cb(struct wpa_radio_work *work, int deinit)
+{
+       struct wpa_connect_work *cwork = work->ctx;
+       struct wpa_bss *bss = cwork->bss;
+       struct wpa_ssid *ssid = cwork->ssid;
+       struct wpa_supplicant *wpa_s = work->wpa_s;
+       u8 wpa_ie[200];
+       size_t wpa_ie_len;
+       int use_crypt, ret, i, bssid_changed;
+       int algs = WPA_AUTH_ALG_OPEN;
+       unsigned int cipher_pairwise, cipher_group;
+       struct wpa_driver_associate_params params;
+       int wep_keys_set = 0;
+       int assoc_failed = 0;
+       struct wpa_ssid *old_ssid;
+       u8 prev_bssid[ETH_ALEN];
+#ifdef CONFIG_HT_OVERRIDES
+       struct ieee80211_ht_capabilities htcaps;
+       struct ieee80211_ht_capabilities htcaps_mask;
+#endif /* CONFIG_HT_OVERRIDES */
+#ifdef CONFIG_VHT_OVERRIDES
+       struct ieee80211_vht_capabilities vhtcaps;
+       struct ieee80211_vht_capabilities vhtcaps_mask;
+#endif /* CONFIG_VHT_OVERRIDES */
+#ifdef CONFIG_MBO
+       const u8 *mbo = NULL;
+#endif /* CONFIG_MBO */
+
+       if (deinit) {
+               if (work->started) {
+                       wpa_s->connect_work = NULL;
+
+                       /* cancel possible auth. timeout */
+                       eloop_cancel_timeout(wpa_supplicant_timeout, wpa_s,
+                                            NULL);
+               }
+               wpas_connect_work_free(cwork);
+               return;
+       }
+
+       wpa_s->connect_work = work;
+
+       if (cwork->bss_removed || !wpas_valid_bss_ssid(wpa_s, bss, ssid) ||
+           wpas_network_disabled(wpa_s, ssid)) {
+               wpa_dbg(wpa_s, MSG_DEBUG, "BSS/SSID entry for association not valid anymore - drop connection attempt");
+               wpas_connect_work_done(wpa_s);
+               return;
+       }
+
+       os_memcpy(prev_bssid, wpa_s->bssid, ETH_ALEN);
+       os_memset(&params, 0, sizeof(params));
+       wpa_s->reassociate = 0;
+       wpa_s->eap_expected_failure = 0;
+       if (bss &&
+           (!wpas_driver_bss_selection(wpa_s) || wpas_wps_searching(wpa_s))) {
+#ifdef CONFIG_IEEE80211R
+               const u8 *ie, *md = NULL;
+#endif /* CONFIG_IEEE80211R */
+               wpa_msg(wpa_s, MSG_INFO, "Trying to associate with " MACSTR
+                       " (SSID='%s' freq=%d MHz)", MAC2STR(bss->bssid),
+                       wpa_ssid_txt(bss->ssid, bss->ssid_len), bss->freq);
+               bssid_changed = !is_zero_ether_addr(wpa_s->bssid);
+               os_memset(wpa_s->bssid, 0, ETH_ALEN);
+               os_memcpy(wpa_s->pending_bssid, bss->bssid, ETH_ALEN);
+               if (bssid_changed)
+                       wpas_notify_bssid_changed(wpa_s);
+#ifdef CONFIG_IEEE80211R
+               ie = wpa_bss_get_ie(bss, WLAN_EID_MOBILITY_DOMAIN);
+               if (ie && ie[1] >= MOBILITY_DOMAIN_ID_LEN)
+                       md = ie + 2;
+               wpa_sm_set_ft_params(wpa_s->wpa, ie, ie ? 2 + ie[1] : 0);
+               if (md) {
+                       /* Prepare for the next transition */
+                       wpa_ft_prepare_auth_request(wpa_s->wpa, ie);
+               }
+#endif /* CONFIG_IEEE80211R */
+#ifdef CONFIG_WPS
+       } else if ((ssid->ssid == NULL || ssid->ssid_len == 0) &&
+                  wpa_s->conf->ap_scan == 2 &&
+                  (ssid->key_mgmt & WPA_KEY_MGMT_WPS)) {
+               /* Use ap_scan==1 style network selection to find the network
+                */
+               wpas_connect_work_done(wpa_s);
+               wpa_s->scan_req = MANUAL_SCAN_REQ;
+               wpa_s->reassociate = 1;
+               wpa_supplicant_req_scan(wpa_s, 0, 0);
+               return;
+#endif /* CONFIG_WPS */
+       } else {
+               wpa_msg(wpa_s, MSG_INFO, "Trying to associate with SSID '%s'",
+                       wpa_ssid_txt(ssid->ssid, ssid->ssid_len));
+               os_memset(wpa_s->pending_bssid, 0, ETH_ALEN);
+       }
+       if (!wpa_s->pno)
+               wpa_supplicant_cancel_sched_scan(wpa_s);
+
+       wpa_supplicant_cancel_scan(wpa_s);
+
+       /* Starting new association, so clear the possibly used WPA IE from the
+        * previous association. */
+       wpa_sm_set_assoc_wpa_ie(wpa_s->wpa, NULL, 0);
+
+#ifdef IEEE8021X_EAPOL
+       if (ssid->key_mgmt & WPA_KEY_MGMT_IEEE8021X_NO_WPA) {
+               if (ssid->leap) {
+                       if (ssid->non_leap == 0)
+                               algs = WPA_AUTH_ALG_LEAP;
+                       else
+                               algs |= WPA_AUTH_ALG_LEAP;
+               }
+       }
+#endif /* IEEE8021X_EAPOL */
+       wpa_dbg(wpa_s, MSG_DEBUG, "Automatic auth_alg selection: 0x%x", algs);
+       if (ssid->auth_alg) {
+               algs = ssid->auth_alg;
                wpa_dbg(wpa_s, MSG_DEBUG, "Overriding auth_alg selection: "
                        "0x%x", algs);
        }
@@ -1525,12 +2194,13 @@ static void wpas_start_assoc_cb(struct wpa_radio_work *work, int deinit)
                        (ssid->proto & WPA_PROTO_RSN);
                if (pmksa_cache_set_current(wpa_s->wpa, NULL, bss->bssid,
                                            ssid, try_opportunistic) == 0)
-                       eapol_sm_notify_pmkid_attempt(wpa_s->eapol, 1);
+                       eapol_sm_notify_pmkid_attempt(wpa_s->eapol);
                wpa_ie_len = sizeof(wpa_ie);
                if (wpa_supplicant_set_suites(wpa_s, bss, ssid,
                                              wpa_ie, &wpa_ie_len)) {
                        wpa_msg(wpa_s, MSG_WARNING, "WPA: Failed to set WPA "
                                "key management and encryption suites");
+                       wpas_connect_work_done(wpa_s);
                        return;
                }
        } else if ((ssid->key_mgmt & WPA_KEY_MGMT_IEEE8021X_NO_WPA) && bss &&
@@ -1550,6 +2220,7 @@ static void wpas_start_assoc_cb(struct wpa_radio_work *work, int deinit)
                        wpa_msg(wpa_s, MSG_WARNING, "WPA: Failed to set WPA "
                                "key management and encryption suites (no "
                                "scan results)");
+                       wpas_connect_work_done(wpa_s);
                        return;
                }
 #ifdef CONFIG_WPS
@@ -1606,20 +2277,21 @@ static void wpas_start_assoc_cb(struct wpa_radio_work *work, int deinit)
        os_memset(wpa_s->p2p_ip_addr_info, 0, sizeof(wpa_s->p2p_ip_addr_info));
 #endif /* CONFIG_P2P */
 
-#ifdef CONFIG_HS20
-       if (is_hs20_network(wpa_s, ssid, bss)) {
-               struct wpabuf *hs20;
-               hs20 = wpabuf_alloc(20);
-               if (hs20) {
-                       int pps_mo_id = hs20_get_pps_mo_id(wpa_s, ssid);
-                       wpas_hs20_add_indication(hs20, pps_mo_id);
-                       os_memcpy(wpa_ie + wpa_ie_len, wpabuf_head(hs20),
-                                 wpabuf_len(hs20));
-                       wpa_ie_len += wpabuf_len(hs20);
-                       wpabuf_free(hs20);
+#ifdef CONFIG_MBO
+       if (bss) {
+               mbo = wpa_bss_get_vendor_ie(bss, MBO_IE_VENDOR_TYPE);
+               if (mbo) {
+                       int len;
+
+                       len = wpas_mbo_supp_op_class_ie(wpa_s, bss->freq,
+                                                       wpa_ie + wpa_ie_len,
+                                                       sizeof(wpa_ie) -
+                                                       wpa_ie_len);
+                       if (len > 0)
+                               wpa_ie_len += len;
                }
        }
-#endif /* CONFIG_HS20 */
+#endif /* CONFIG_MBO */
 
        /*
         * Workaround: Add Extended Capabilities element only if the AP
@@ -1629,10 +2301,16 @@ static void wpas_start_assoc_cb(struct wpa_radio_work *work, int deinit)
         * element in all cases, it is justifiable to skip it to avoid
         * interoperability issues.
         */
+       if (ssid->p2p_group)
+               wpa_drv_get_ext_capa(wpa_s, WPA_IF_P2P_CLIENT);
+       else
+               wpa_drv_get_ext_capa(wpa_s, WPA_IF_STATION);
+
        if (!bss || wpa_bss_get_ie(bss, WLAN_EID_EXT_CAPAB)) {
-               u8 ext_capab[10];
+               u8 ext_capab[18];
                int ext_capab_len;
-               ext_capab_len = wpas_build_ext_capab(wpa_s, ext_capab);
+               ext_capab_len = wpas_build_ext_capab(wpa_s, ext_capab,
+                                                    sizeof(ext_capab));
                if (ext_capab_len > 0) {
                        u8 *pos = wpa_ie;
                        if (wpa_ie_len > 0 && pos[0] == WLAN_EID_RSN)
@@ -1644,6 +2322,64 @@ static void wpas_start_assoc_cb(struct wpa_radio_work *work, int deinit)
                }
        }
 
+#ifdef CONFIG_HS20
+       if (is_hs20_network(wpa_s, ssid, bss)) {
+               struct wpabuf *hs20;
+
+               hs20 = wpabuf_alloc(20);
+               if (hs20) {
+                       int pps_mo_id = hs20_get_pps_mo_id(wpa_s, ssid);
+                       size_t len;
+
+                       wpas_hs20_add_indication(hs20, pps_mo_id);
+                       len = sizeof(wpa_ie) - wpa_ie_len;
+                       if (wpabuf_len(hs20) <= len) {
+                               os_memcpy(wpa_ie + wpa_ie_len,
+                                         wpabuf_head(hs20), wpabuf_len(hs20));
+                               wpa_ie_len += wpabuf_len(hs20);
+                       }
+                       wpabuf_free(hs20);
+
+                       hs20_configure_frame_filters(wpa_s);
+               }
+       }
+#endif /* CONFIG_HS20 */
+
+       if (wpa_s->vendor_elem[VENDOR_ELEM_ASSOC_REQ]) {
+               struct wpabuf *buf = wpa_s->vendor_elem[VENDOR_ELEM_ASSOC_REQ];
+               size_t len;
+
+               len = sizeof(wpa_ie) - wpa_ie_len;
+               if (wpabuf_len(buf) <= len) {
+                       os_memcpy(wpa_ie + wpa_ie_len,
+                                 wpabuf_head(buf), wpabuf_len(buf));
+                       wpa_ie_len += wpabuf_len(buf);
+               }
+       }
+
+#ifdef CONFIG_FST
+       if (wpa_s->fst_ies) {
+               int fst_ies_len = wpabuf_len(wpa_s->fst_ies);
+
+               if (wpa_ie_len + fst_ies_len <= sizeof(wpa_ie)) {
+                       os_memcpy(wpa_ie + wpa_ie_len,
+                                 wpabuf_head(wpa_s->fst_ies), fst_ies_len);
+                       wpa_ie_len += fst_ies_len;
+               }
+       }
+#endif /* CONFIG_FST */
+
+#ifdef CONFIG_MBO
+       if (mbo) {
+               int len;
+
+               len = wpas_mbo_ie(wpa_s, wpa_ie + wpa_ie_len,
+                                 sizeof(wpa_ie) - wpa_ie_len);
+               if (len >= 0)
+                       wpa_ie_len += len;
+       }
+#endif /* CONFIG_MBO */
+
        wpa_clear_keys(wpa_s, bss ? bss->bssid : NULL);
        use_crypt = 1;
        cipher_pairwise = wpa_s->pairwise_cipher;
@@ -1692,13 +2428,15 @@ static void wpas_start_assoc_cb(struct wpa_radio_work *work, int deinit)
                                   MAC2STR(bss->bssid), bss->freq,
                                   ssid->bssid_set);
                        params.bssid = bss->bssid;
-                       params.freq = bss->freq;
+                       params.freq.freq = bss->freq;
                }
                params.bssid_hint = bss->bssid;
                params.freq_hint = bss->freq;
+               params.pbss = bss_is_pbss(bss);
        } else {
                params.ssid = ssid->ssid;
                params.ssid_len = ssid->ssid_len;
+               params.pbss = (ssid->pbss != 2) ? ssid->pbss : 0;
        }
 
        if (ssid->mode == WPAS_MODE_IBSS && ssid->bssid_set &&
@@ -1707,9 +2445,19 @@ static void wpas_start_assoc_cb(struct wpa_radio_work *work, int deinit)
                params.fixed_bssid = 1;
        }
 
-       if (ssid->mode == WPAS_MODE_IBSS && ssid->frequency > 0 &&
-           params.freq == 0)
-               params.freq = ssid->frequency; /* Initial channel for IBSS */
+       /* Initial frequency for IBSS/mesh */
+       if ((ssid->mode == WPAS_MODE_IBSS || ssid->mode == WPAS_MODE_MESH) &&
+           ssid->frequency > 0 && params.freq.freq == 0)
+               ibss_mesh_setup_freq(wpa_s, ssid, &params.freq);
+
+       if (ssid->mode == WPAS_MODE_IBSS) {
+               params.fixed_freq = ssid->fixed_freq;
+               if (ssid->beacon_int)
+                       params.beacon_int = ssid->beacon_int;
+               else
+                       params.beacon_int = wpa_s->conf->beacon_int;
+       }
+
        params.wpa_ie = wpa_ie;
        params.wpa_ie_len = wpa_ie_len;
        params.pairwise_suite = cipher_pairwise;
@@ -1734,12 +2482,28 @@ static void wpas_start_assoc_cb(struct wpa_radio_work *work, int deinit)
                        params.psk = ssid->psk;
        }
 
+       if (wpa_s->conf->key_mgmt_offload) {
+               if (params.key_mgmt_suite == WPA_KEY_MGMT_IEEE8021X ||
+                   params.key_mgmt_suite == WPA_KEY_MGMT_IEEE8021X_SHA256 ||
+                   params.key_mgmt_suite == WPA_KEY_MGMT_IEEE8021X_SUITE_B ||
+                   params.key_mgmt_suite == WPA_KEY_MGMT_IEEE8021X_SUITE_B_192)
+                       params.req_key_mgmt_offload =
+                               ssid->proactive_key_caching < 0 ?
+                               wpa_s->conf->okc : ssid->proactive_key_caching;
+               else
+                       params.req_key_mgmt_offload = 1;
+
+               if ((params.key_mgmt_suite == WPA_KEY_MGMT_PSK ||
+                    params.key_mgmt_suite == WPA_KEY_MGMT_PSK_SHA256 ||
+                    params.key_mgmt_suite == WPA_KEY_MGMT_FT_PSK) &&
+                   ssid->psk_set)
+                       params.psk = ssid->psk;
+       }
+
        params.drop_unencrypted = use_crypt;
 
 #ifdef CONFIG_IEEE80211W
-       params.mgmt_frame_protection =
-               ssid->ieee80211w == MGMT_FRAME_PROTECTION_DEFAULT ?
-               wpa_s->conf->pmf : ssid->ieee80211w;
+       params.mgmt_frame_protection = wpas_get_ssid_pmf(wpa_s, ssid);
        if (params.mgmt_frame_protection != NO_MGMT_FRAME_PROTECTION && bss) {
                const u8 *rsn = wpa_bss_get_ie(bss, WLAN_EID_RSN);
                struct wpa_ie_data ie;
@@ -1756,8 +2520,8 @@ static void wpas_start_assoc_cb(struct wpa_radio_work *work, int deinit)
 
        params.p2p = ssid->p2p_group;
 
-       if (wpa_s->parent->set_sta_uapsd)
-               params.uapsd = wpa_s->parent->sta_uapsd;
+       if (wpa_s->p2pdev->set_sta_uapsd)
+               params.uapsd = wpa_s->p2pdev->sta_uapsd;
        else
                params.uapsd = -1;
 
@@ -1768,6 +2532,13 @@ static void wpas_start_assoc_cb(struct wpa_radio_work *work, int deinit)
        params.htcaps_mask = (u8 *) &htcaps_mask;
        wpa_supplicant_apply_ht_overrides(wpa_s, ssid, &params);
 #endif /* CONFIG_HT_OVERRIDES */
+#ifdef CONFIG_VHT_OVERRIDES
+       os_memset(&vhtcaps, 0, sizeof(vhtcaps));
+       os_memset(&vhtcaps_mask, 0, sizeof(vhtcaps_mask));
+       params.vhtcaps = &vhtcaps;
+       params.vhtcaps_mask = &vhtcaps_mask;
+       wpa_supplicant_apply_vht_overrides(wpa_s, ssid, &params);
+#endif /* CONFIG_VHT_OVERRIDES */
 
 #ifdef CONFIG_P2P
        /*
@@ -1776,18 +2547,25 @@ static void wpas_start_assoc_cb(struct wpa_radio_work *work, int deinit)
         * 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)
+               int freq, num;
+               num = get_shared_radio_freqs(wpa_s, &freq, 1);
+               if (num > 0 && freq > 0 && freq != params.freq.freq) {
+                       wpa_printf(MSG_DEBUG,
+                                  "Assoc conflicting freq found (%d != %d)",
+                                  freq, params.freq.freq);
+                       if (wpas_p2p_handle_frequency_conflicts(
+                                   wpa_s, params.freq.freq, ssid) < 0) {
+                               wpas_connect_work_done(wpa_s);
                                return;
+                       }
                }
        }
 #endif /* CONFIG_P2P */
 
+       if (wpa_s->reassoc_same_ess && !is_zero_ether_addr(prev_bssid) &&
+           wpa_s->current_ssid)
+               params.prev_bssid = prev_bssid;
+
        ret = wpa_drv_associate(wpa_s, &params);
        if (ret < 0) {
                wpa_msg(wpa_s, MSG_INFO, "Association request to the driver "
@@ -1855,7 +2633,14 @@ static void wpas_start_assoc_cb(struct wpa_radio_work *work, int deinit)
        }
        old_ssid = wpa_s->current_ssid;
        wpa_s->current_ssid = ssid;
-       wpa_s->current_bss = bss;
+
+       if (!wpas_driver_bss_selection(wpa_s) || ssid->bssid_set) {
+               wpa_s->current_bss = bss;
+#ifdef CONFIG_HS20
+               hs20_configure_frame_filters(wpa_s);
+#endif /* CONFIG_HS20 */
+       }
+
        wpa_supplicant_rsn_supp_set_config(wpa_s, wpa_s->current_ssid);
        wpa_supplicant_initiate_eapol(wpa_s);
        if (old_ssid != wpa_s->current_ssid)
@@ -1868,6 +2653,7 @@ static void wpa_supplicant_clear_connection(struct wpa_supplicant *wpa_s,
 {
        struct wpa_ssid *old_ssid;
 
+       wpas_connect_work_done(wpa_s);
        wpa_clear_keys(wpa_s, addr);
        old_ssid = wpa_s->current_ssid;
        wpa_supplicant_mark_disassoc(wpa_s);
@@ -1920,6 +2706,14 @@ void wpa_supplicant_deauthenticate(struct wpa_supplicant *wpa_s,
        wpa_tdls_teardown_peers(wpa_s->wpa);
 #endif /* CONFIG_TDLS */
 
+#ifdef CONFIG_MESH
+       if (wpa_s->ifmsh) {
+               wpa_msg(wpa_s, MSG_INFO, MESH_GROUP_REMOVED "%s",
+                       wpa_s->ifname);
+               wpa_supplicant_leave_mesh(wpa_s);
+       }
+#endif /* CONFIG_MESH */
+
        if (addr) {
                wpa_drv_deauthenticate(wpa_s, addr, reason_code);
                os_memset(&event, 0, sizeof(event));
@@ -1968,15 +2762,20 @@ void wpa_supplicant_enable_network(struct wpa_supplicant *wpa_s,
        } else
                wpa_supplicant_enable_one_network(wpa_s, ssid);
 
-       if (wpa_s->reassociate && !wpa_s->disconnected) {
+       if (wpa_s->reassociate && !wpa_s->disconnected &&
+           (!wpa_s->current_ssid ||
+            wpa_s->wpa_state == WPA_DISCONNECTED ||
+            wpa_s->wpa_state == WPA_SCANNING)) {
                if (wpa_s->sched_scanning) {
                        wpa_printf(MSG_DEBUG, "Stop ongoing sched_scan to add "
                                   "new network to scan filters");
                        wpa_supplicant_cancel_sched_scan(wpa_s);
                }
 
-               if (wpa_supplicant_fast_associate(wpa_s) != 1)
+               if (wpa_supplicant_fast_associate(wpa_s) != 1) {
+                       wpa_s->scan_req = NORMAL_SCAN_REQ;
                        wpa_supplicant_req_scan(wpa_s, 0, 0);
+               }
        }
 }
 
@@ -2049,6 +2848,8 @@ void wpa_supplicant_select_network(struct wpa_supplicant *wpa_s,
        int disconnected = 0;
 
        if (ssid && ssid != wpa_s->current_ssid && wpa_s->current_ssid) {
+               if (wpa_s->wpa_state >= WPA_AUTHENTICATING)
+                       wpa_s->own_disconnect_req = 1;
                wpa_supplicant_deauthenticate(
                        wpa_s, WLAN_REASON_DEAUTH_LEAVING);
                disconnected = 1;
@@ -2075,7 +2876,8 @@ void wpa_supplicant_select_network(struct wpa_supplicant *wpa_s,
                        wpas_notify_network_enabled_changed(wpa_s, other_ssid);
        }
 
-       if (ssid && ssid == wpa_s->current_ssid && wpa_s->current_ssid) {
+       if (ssid && ssid == wpa_s->current_ssid && wpa_s->current_ssid &&
+           wpa_s->wpa_state >= WPA_AUTHENTICATING) {
                /* We are already associated with the selected network */
                wpa_printf(MSG_DEBUG, "Already associated with the "
                           "selected network - do nothing");
@@ -2085,13 +2887,27 @@ void wpa_supplicant_select_network(struct wpa_supplicant *wpa_s,
        if (ssid) {
                wpa_s->current_ssid = ssid;
                eapol_sm_notify_config(wpa_s->eapol, NULL, NULL);
+               wpa_s->connect_without_scan =
+                       (ssid->mode == WPAS_MODE_MESH) ? ssid : NULL;
+
+               /*
+                * Don't optimize next scan freqs since a new ESS has been
+                * selected.
+                */
+               os_free(wpa_s->next_scan_freqs);
+               wpa_s->next_scan_freqs = NULL;
+       } else {
+               wpa_s->connect_without_scan = NULL;
        }
-       wpa_s->connect_without_scan = NULL;
+
        wpa_s->disconnected = 0;
        wpa_s->reassociate = 1;
 
-       if (wpa_supplicant_fast_associate(wpa_s) != 1)
+       if (wpa_s->connect_without_scan ||
+           wpa_supplicant_fast_associate(wpa_s) != 1) {
+               wpa_s->scan_req = NORMAL_SCAN_REQ;
                wpa_supplicant_req_scan(wpa_s, 0, disconnected ? 100000 : 0);
+       }
 
        if (ssid)
                wpas_notify_network_selected(wpa_s, ssid);
@@ -2166,6 +2982,11 @@ int wpa_supplicant_set_ap_scan(struct wpa_supplicant *wpa_s, int ap_scan)
        if (ap_scan < 0 || ap_scan > 2)
                return -1;
 
+       if (ap_scan == 2 && os_strcmp(wpa_s->driver->name, "nl80211") == 0) {
+               wpa_printf(MSG_INFO,
+                          "Note: nl80211 driver interface is not designed to be used with ap_scan=2; this can result in connection failures");
+       }
+
 #ifdef ANDROID
        if (ap_scan == 2 && ap_scan != wpa_s->conf->ap_scan &&
            wpa_s->wpa_state >= WPA_ASSOCIATING &&
@@ -2305,7 +3126,7 @@ int wpa_supplicant_set_debug_params(struct wpa_global *global, int debug_level,
 struct wpa_ssid * wpa_supplicant_get_ssid(struct wpa_supplicant *wpa_s)
 {
        struct wpa_ssid *entry;
-       u8 ssid[MAX_SSID_LEN];
+       u8 ssid[SSID_MAX_LEN];
        int res;
        size_t ssid_len;
        u8 bssid[ETH_ALEN];
@@ -2362,7 +3183,7 @@ static int select_driver(struct wpa_supplicant *wpa_s, int i)
        struct wpa_global *global = wpa_s->global;
 
        if (wpa_drivers[i]->global_init && global->drv_priv[i] == NULL) {
-               global->drv_priv[i] = wpa_drivers[i]->global_init();
+               global->drv_priv[i] = wpa_drivers[i]->global_init(global);
                if (global->drv_priv[i] == NULL) {
                        wpa_printf(MSG_ERROR, "Failed to initialize driver "
                                   "'%s'", wpa_drivers[i]->name);
@@ -2510,12 +3331,36 @@ void wpa_supplicant_rx_eapol(void *ctx, const u8 *src_addr,
            (wpa_s->current_ssid == NULL ||
             wpa_s->current_ssid->mode != IEEE80211_MODE_IBSS)) {
                /* Timeout for completing IEEE 802.1X and WPA authentication */
-               wpa_supplicant_req_auth_timeout(
-                       wpa_s,
-                       (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) ?
-                       70 : 10, 0);
+               int timeout = 10;
+
+               if (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) {
+                       /* Use longer timeout for IEEE 802.1X/EAP */
+                       timeout = 70;
+               }
+
+#ifdef CONFIG_WPS
+               if (wpa_s->current_ssid && wpa_s->current_bss &&
+                   (wpa_s->current_ssid->key_mgmt & WPA_KEY_MGMT_WPS) &&
+                   eap_is_wps_pin_enrollee(&wpa_s->current_ssid->eap)) {
+                       /*
+                        * Use shorter timeout if going through WPS AP iteration
+                        * for PIN config method with an AP that does not
+                        * advertise Selected Registrar.
+                        */
+                       struct wpabuf *wps_ie;
+
+                       wps_ie = wpa_bss_get_vendor_ie_multi(
+                               wpa_s->current_bss, WPS_IE_VENDOR_TYPE);
+                       if (wps_ie &&
+                           !wps_is_addr_authorized(wps_ie, wpa_s->own_addr, 1))
+                               timeout = 10;
+                       wpabuf_free(wps_ie);
+               }
+#endif /* CONFIG_WPS */
+
+               wpa_supplicant_req_auth_timeout(wpa_s, timeout, 0);
        }
        wpa_s->eapol_received++;
 
@@ -2560,15 +3405,9 @@ void wpa_supplicant_rx_eapol(void *ctx, const u8 *src_addr,
 
 int wpa_supplicant_update_mac_addr(struct wpa_supplicant *wpa_s)
 {
-       if (wpa_s->driver->send_eapol) {
-               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->p2p_mgmt ||
-                   !(wpa_s->drv_flags &
-                     WPA_DRIVER_FLAGS_DEDICATED_P2P_DEVICE)) &&
-                  !(wpa_s->drv_flags &
-                    WPA_DRIVER_FLAGS_P2P_DEDICATED_INTERFACE)) {
+       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,
                                           wpa_drv_get_mac_addr(wpa_s),
@@ -2587,6 +3426,8 @@ int wpa_supplicant_update_mac_addr(struct wpa_supplicant *wpa_s)
                return -1;
        }
 
+       wpa_sm_set_own_addr(wpa_s->wpa, wpa_s->own_addr);
+
        return 0;
 }
 
@@ -2634,16 +3475,15 @@ int wpa_supplicant_driver_init(struct wpa_supplicant *wpa_s)
 
        wpa_dbg(wpa_s, MSG_DEBUG, "Own MAC address: " MACSTR,
                MAC2STR(wpa_s->own_addr));
+       os_memcpy(wpa_s->perm_addr, wpa_s->own_addr, ETH_ALEN);
        wpa_sm_set_own_addr(wpa_s->wpa, wpa_s->own_addr);
 
        if (wpa_s->bridge_ifname[0]) {
                wpa_dbg(wpa_s, MSG_DEBUG, "Receiving packets from bridge "
                        "interface '%s'", wpa_s->bridge_ifname);
-               wpa_s->l2_br = l2_packet_init(wpa_s->bridge_ifname,
-                                             wpa_s->own_addr,
-                                             ETH_P_EAPOL,
-                                             wpa_supplicant_rx_eapol_bridge,
-                                             wpa_s, 1);
+               wpa_s->l2_br = l2_packet_init_bridge(
+                       wpa_s->bridge_ifname, wpa_s->ifname, wpa_s->own_addr,
+                       ETH_P_EAPOL, wpa_supplicant_rx_eapol_bridge, wpa_s, 1);
                if (wpa_s->l2_br == NULL) {
                        wpa_msg(wpa_s, MSG_ERROR, "Failed to open l2_packet "
                                "connection for the bridge interface '%s'",
@@ -2652,6 +3492,12 @@ int wpa_supplicant_driver_init(struct wpa_supplicant *wpa_s)
                }
        }
 
+       if (wpa_s->conf->ap_scan == 2 &&
+           os_strcmp(wpa_s->driver->name, "nl80211") == 0) {
+               wpa_printf(MSG_INFO,
+                          "Note: nl80211 driver interface is not designed to be used with ap_scan=2; this can result in connection failures");
+       }
+
        wpa_clear_keys(wpa_s, NULL);
 
        /* Make sure that TKIP countermeasures are not left enabled (could
@@ -2669,10 +3515,14 @@ int wpa_supplicant_driver_init(struct wpa_supplicant *wpa_s)
                        wpa_supplicant_set_state(wpa_s, WPA_DISCONNECTED);
                        interface_count = 0;
                }
-               if (wpa_supplicant_delayed_sched_scan(wpa_s, interface_count,
+#ifndef ANDROID
+               if (!wpa_s->p2p_mgmt &&
+                   wpa_supplicant_delayed_sched_scan(wpa_s,
+                                                     interface_count % 3,
                                                      100000))
-                       wpa_supplicant_req_scan(wpa_s, interface_count,
+                       wpa_supplicant_req_scan(wpa_s, interface_count % 3,
                                                100000);
+#endif /* ANDROID */
                interface_count++;
        } else
                wpa_supplicant_set_state(wpa_s, WPA_INACTIVE);
@@ -2688,7 +3538,8 @@ static int wpa_supplicant_daemon(const char *pid_file)
 }
 
 
-static struct wpa_supplicant * wpa_supplicant_alloc(void)
+static struct wpa_supplicant *
+wpa_supplicant_alloc(struct wpa_supplicant *parent)
 {
        struct wpa_supplicant *wpa_s;
 
@@ -2698,9 +3549,12 @@ static struct wpa_supplicant * wpa_supplicant_alloc(void)
        wpa_s->scan_req = INITIAL_SCAN_REQ;
        wpa_s->scan_interval = 5;
        wpa_s->new_connection = 1;
-       wpa_s->parent = wpa_s;
+       wpa_s->parent = parent ? parent : wpa_s;
+       wpa_s->p2pdev = wpa_s->parent;
        wpa_s->sched_scanning = 0;
 
+       dl_list_init(&wpa_s->bss_tmp_disallowed);
+
        return wpa_s;
 }
 
@@ -2766,7 +3620,7 @@ static int wpa_disable_max_amsdu(struct wpa_supplicant *wpa_s,
                                 struct ieee80211_ht_capabilities *htcaps_mask,
                                 int disabled)
 {
-       u16 msk;
+       le16 msk;
 
        wpa_msg(wpa_s, MSG_DEBUG, "set_disable_max_amsdu: %d", disabled);
 
@@ -2839,8 +3693,8 @@ static int wpa_set_disable_ht40(struct wpa_supplicant *wpa_s,
                                int disabled)
 {
        /* Masking these out disables HT40 */
-       u16 msk = host_to_le16(HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET |
-                              HT_CAP_INFO_SHORT_GI40MHZ);
+       le16 msk = host_to_le16(HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET |
+                               HT_CAP_INFO_SHORT_GI40MHZ);
 
        wpa_msg(wpa_s, MSG_DEBUG, "set_disable_ht40: %d", disabled);
 
@@ -2861,8 +3715,8 @@ static int wpa_set_disable_sgi(struct wpa_supplicant *wpa_s,
                               int disabled)
 {
        /* Masking these out disables SGI */
-       u16 msk = host_to_le16(HT_CAP_INFO_SHORT_GI20MHZ |
-                              HT_CAP_INFO_SHORT_GI40MHZ);
+       le16 msk = host_to_le16(HT_CAP_INFO_SHORT_GI20MHZ |
+                               HT_CAP_INFO_SHORT_GI40MHZ);
 
        wpa_msg(wpa_s, MSG_DEBUG, "set_disable_sgi: %d", disabled);
 
@@ -2877,6 +3731,27 @@ static int wpa_set_disable_sgi(struct wpa_supplicant *wpa_s,
 }
 
 
+static int wpa_set_disable_ldpc(struct wpa_supplicant *wpa_s,
+                              struct ieee80211_ht_capabilities *htcaps,
+                              struct ieee80211_ht_capabilities *htcaps_mask,
+                              int disabled)
+{
+       /* Masking these out disables LDPC */
+       le16 msk = host_to_le16(HT_CAP_INFO_LDPC_CODING_CAP);
+
+       wpa_msg(wpa_s, MSG_DEBUG, "set_disable_ldpc: %d", disabled);
+
+       if (disabled)
+               htcaps->ht_capabilities_info &= ~msk;
+       else
+               htcaps->ht_capabilities_info |= msk;
+
+       htcaps_mask->ht_capabilities_info |= msk;
+
+       return 0;
+}
+
+
 void wpa_supplicant_apply_ht_overrides(
        struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid,
        struct wpa_driver_associate_params *params)
@@ -2900,6 +3775,13 @@ void wpa_supplicant_apply_ht_overrides(
        wpa_set_ampdu_density(wpa_s, htcaps, htcaps_mask, ssid->ampdu_density);
        wpa_set_disable_ht40(wpa_s, htcaps, htcaps_mask, ssid->disable_ht40);
        wpa_set_disable_sgi(wpa_s, htcaps, htcaps_mask, ssid->disable_sgi);
+       wpa_set_disable_ldpc(wpa_s, htcaps, htcaps_mask, ssid->disable_ldpc);
+
+       if (ssid->ht40_intolerant) {
+               le16 bit = host_to_le16(HT_CAP_INFO_40MHZ_INTOLERANT);
+               htcaps->ht_capabilities_info |= bit;
+               htcaps_mask->ht_capabilities_info |= bit;
+       }
 }
 
 #endif /* CONFIG_HT_OVERRIDES */
@@ -2924,21 +3806,40 @@ void wpa_supplicant_apply_vht_overrides(
        if (!vhtcaps || !vhtcaps_mask)
                return;
 
-       vhtcaps->vht_capabilities_info = ssid->vht_capa;
-       vhtcaps_mask->vht_capabilities_info = ssid->vht_capa_mask;
+       vhtcaps->vht_capabilities_info = host_to_le32(ssid->vht_capa);
+       vhtcaps_mask->vht_capabilities_info = host_to_le32(ssid->vht_capa_mask);
+
+#ifdef CONFIG_HT_OVERRIDES
+       /* if max ampdu is <= 3, we have to make the HT cap the same */
+       if (ssid->vht_capa_mask & VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MAX) {
+               int max_ampdu;
+
+               max_ampdu = (ssid->vht_capa &
+                            VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MAX) >>
+                       VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MAX_SHIFT;
+
+               max_ampdu = max_ampdu < 3 ? max_ampdu : 3;
+               wpa_set_ampdu_factor(wpa_s,
+                                    (void *) params->htcaps,
+                                    (void *) params->htcaps_mask,
+                                    max_ampdu);
+       }
+#endif /* CONFIG_HT_OVERRIDES */
 
 #define OVERRIDE_MCS(i)                                                        \
        if (ssid->vht_tx_mcs_nss_ ##i >= 0) {                           \
                vhtcaps_mask->vht_supported_mcs_set.tx_map |=           \
-                       3 << 2 * (i - 1);                               \
+                       host_to_le16(3 << 2 * (i - 1));                 \
                vhtcaps->vht_supported_mcs_set.tx_map |=                \
-                       ssid->vht_tx_mcs_nss_ ##i << 2 * (i - 1);       \
+                       host_to_le16(ssid->vht_tx_mcs_nss_ ##i <<       \
+                                    2 * (i - 1));                      \
        }                                                               \
        if (ssid->vht_rx_mcs_nss_ ##i >= 0) {                           \
                vhtcaps_mask->vht_supported_mcs_set.rx_map |=           \
-                       3 << 2 * (i - 1);                               \
+                       host_to_le16(3 << 2 * (i - 1));                 \
                vhtcaps->vht_supported_mcs_set.rx_map |=                \
-                       ssid->vht_rx_mcs_nss_ ##i << 2 * (i - 1);       \
+                       host_to_le16(ssid->vht_rx_mcs_nss_ ##i <<       \
+                                    2 * (i - 1));                      \
        }
 
        OVERRIDE_MCS(1);
@@ -3027,6 +3928,193 @@ int wpas_init_ext_pw(struct wpa_supplicant *wpa_s)
 }
 
 
+#ifdef CONFIG_FST
+
+static const u8 * wpas_fst_get_bssid_cb(void *ctx)
+{
+       struct wpa_supplicant *wpa_s = ctx;
+
+       return (is_zero_ether_addr(wpa_s->bssid) ||
+               wpa_s->wpa_state != WPA_COMPLETED) ? NULL : wpa_s->bssid;
+}
+
+
+static void wpas_fst_get_channel_info_cb(void *ctx,
+                                        enum hostapd_hw_mode *hw_mode,
+                                        u8 *channel)
+{
+       struct wpa_supplicant *wpa_s = ctx;
+
+       if (wpa_s->current_bss) {
+               *hw_mode = ieee80211_freq_to_chan(wpa_s->current_bss->freq,
+                                                 channel);
+       } else if (wpa_s->hw.num_modes) {
+               *hw_mode = wpa_s->hw.modes[0].mode;
+       } else {
+               WPA_ASSERT(0);
+               *hw_mode = 0;
+       }
+}
+
+
+static int wpas_fst_get_hw_modes(void *ctx, struct hostapd_hw_modes **modes)
+{
+       struct wpa_supplicant *wpa_s = ctx;
+
+       *modes = wpa_s->hw.modes;
+       return wpa_s->hw.num_modes;
+}
+
+
+static void wpas_fst_set_ies_cb(void *ctx, const struct wpabuf *fst_ies)
+{
+       struct wpa_supplicant *wpa_s = ctx;
+
+       wpa_hexdump_buf(MSG_DEBUG, "FST: Set IEs", fst_ies);
+       wpa_s->fst_ies = fst_ies;
+}
+
+
+static int wpas_fst_send_action_cb(void *ctx, const u8 *da, struct wpabuf *data)
+{
+       struct wpa_supplicant *wpa_s = ctx;
+
+       WPA_ASSERT(os_memcmp(wpa_s->bssid, da, ETH_ALEN) == 0);
+       return wpa_drv_send_action(wpa_s, wpa_s->assoc_freq, 0, wpa_s->bssid,
+                                         wpa_s->own_addr, wpa_s->bssid,
+                                         wpabuf_head(data), wpabuf_len(data),
+                                  0);
+}
+
+
+static const struct wpabuf * wpas_fst_get_mb_ie_cb(void *ctx, const u8 *addr)
+{
+       struct wpa_supplicant *wpa_s = ctx;
+
+       WPA_ASSERT(os_memcmp(wpa_s->bssid, addr, ETH_ALEN) == 0);
+       return wpa_s->received_mb_ies;
+}
+
+
+static void wpas_fst_update_mb_ie_cb(void *ctx, const u8 *addr,
+                                    const u8 *buf, size_t size)
+{
+       struct wpa_supplicant *wpa_s = ctx;
+       struct mb_ies_info info;
+
+       WPA_ASSERT(os_memcmp(wpa_s->bssid, addr, ETH_ALEN) == 0);
+
+       if (!mb_ies_info_by_ies(&info, buf, size)) {
+               wpabuf_free(wpa_s->received_mb_ies);
+               wpa_s->received_mb_ies = mb_ies_by_info(&info);
+       }
+}
+
+
+static const u8 * wpas_fst_get_peer_first(void *ctx,
+                                         struct fst_get_peer_ctx **get_ctx,
+                                         Boolean mb_only)
+{
+       struct wpa_supplicant *wpa_s = ctx;
+
+       *get_ctx = NULL;
+       if (!is_zero_ether_addr(wpa_s->bssid))
+               return (wpa_s->received_mb_ies || !mb_only) ?
+                       wpa_s->bssid : NULL;
+       return NULL;
+}
+
+
+static const u8 * wpas_fst_get_peer_next(void *ctx,
+                                        struct fst_get_peer_ctx **get_ctx,
+                                        Boolean mb_only)
+{
+       return NULL;
+}
+
+void fst_wpa_supplicant_fill_iface_obj(struct wpa_supplicant *wpa_s,
+                                      struct fst_wpa_obj *iface_obj)
+{
+       iface_obj->ctx              = wpa_s;
+       iface_obj->get_bssid        = wpas_fst_get_bssid_cb;
+       iface_obj->get_channel_info = wpas_fst_get_channel_info_cb;
+       iface_obj->get_hw_modes     = wpas_fst_get_hw_modes;
+       iface_obj->set_ies          = wpas_fst_set_ies_cb;
+       iface_obj->send_action      = wpas_fst_send_action_cb;
+       iface_obj->get_mb_ie        = wpas_fst_get_mb_ie_cb;
+       iface_obj->update_mb_ie     = wpas_fst_update_mb_ie_cb;
+       iface_obj->get_peer_first   = wpas_fst_get_peer_first;
+       iface_obj->get_peer_next    = wpas_fst_get_peer_next;
+}
+#endif /* CONFIG_FST */
+
+static int wpas_set_wowlan_triggers(struct wpa_supplicant *wpa_s,
+                                   const struct wpa_driver_capa *capa)
+{
+       struct wowlan_triggers *triggers;
+       int ret = 0;
+
+       if (!wpa_s->conf->wowlan_triggers)
+               return 0;
+
+       triggers = wpa_get_wowlan_triggers(wpa_s->conf->wowlan_triggers, capa);
+       if (triggers) {
+               ret = wpa_drv_wowlan(wpa_s, triggers);
+               os_free(triggers);
+       }
+       return ret;
+}
+
+
+enum wpa_radio_work_band wpas_freq_to_band(int freq)
+{
+       if (freq < 3000)
+               return BAND_2_4_GHZ;
+       if (freq > 50000)
+               return BAND_60_GHZ;
+       return BAND_5_GHZ;
+}
+
+
+unsigned int wpas_get_bands(struct wpa_supplicant *wpa_s, const int *freqs)
+{
+       int i;
+       unsigned int band = 0;
+
+       if (freqs) {
+               /* freqs are specified for the radio work */
+               for (i = 0; freqs[i]; i++)
+                       band |= wpas_freq_to_band(freqs[i]);
+       } else {
+               /*
+                * freqs are not specified, implies all
+                * the supported freqs by HW
+                */
+               for (i = 0; i < wpa_s->hw.num_modes; i++) {
+                       if (wpa_s->hw.modes[i].num_channels != 0) {
+                               if (wpa_s->hw.modes[i].mode ==
+                                   HOSTAPD_MODE_IEEE80211B ||
+                                   wpa_s->hw.modes[i].mode ==
+                                   HOSTAPD_MODE_IEEE80211G)
+                                       band |= BAND_2_4_GHZ;
+                               else if (wpa_s->hw.modes[i].mode ==
+                                        HOSTAPD_MODE_IEEE80211A)
+                                       band |= BAND_5_GHZ;
+                               else if (wpa_s->hw.modes[i].mode ==
+                                        HOSTAPD_MODE_IEEE80211AD)
+                                       band |= BAND_60_GHZ;
+                               else if (wpa_s->hw.modes[i].mode ==
+                                        HOSTAPD_MODE_IEEE80211ANY)
+                                       band = BAND_2_4_GHZ | BAND_5_GHZ |
+                                               BAND_60_GHZ;
+                       }
+               }
+       }
+
+       return band;
+}
+
+
 static struct wpa_radio * radio_add_interface(struct wpa_supplicant *wpa_s,
                                              const char *rn)
 {
@@ -3079,11 +4167,103 @@ static void radio_work_free(struct wpa_radio_work *work)
        }
 #endif /* CONFIG_P2P */
 
+       if (work->started) {
+               work->wpa_s->radio->num_active_works--;
+               wpa_dbg(work->wpa_s, MSG_DEBUG,
+                       "radio_work_free('%s'@%p: num_active_works --> %u",
+                       work->type, work,
+                       work->wpa_s->radio->num_active_works);
+       }
+
        dl_list_del(&work->list);
        os_free(work);
 }
 
 
+static struct wpa_radio_work * radio_work_get_next_work(struct wpa_radio *radio)
+{
+       struct wpa_radio_work *active_work = NULL;
+       struct wpa_radio_work *tmp;
+
+       /* Get the active work to know the type and band. */
+       dl_list_for_each(tmp, &radio->work, struct wpa_radio_work, list) {
+               if (tmp->started) {
+                       active_work = tmp;
+                       break;
+               }
+       }
+
+       if (!active_work) {
+               /* No active work, start one */
+               radio->num_active_works = 0;
+               dl_list_for_each(tmp, &radio->work, struct wpa_radio_work,
+                                list) {
+                       if (os_strcmp(tmp->type, "scan") == 0 &&
+                           radio->external_scan_running &&
+                           (((struct wpa_driver_scan_params *)
+                             tmp->ctx)->only_new_results ||
+                            tmp->wpa_s->clear_driver_scan_cache))
+                               continue;
+                       return tmp;
+               }
+               return NULL;
+       }
+
+       if (os_strcmp(active_work->type, "sme-connect") == 0 ||
+           os_strcmp(active_work->type, "connect") == 0) {
+               /*
+                * If the active work is either connect or sme-connect,
+                * do not parallelize them with other radio works.
+                */
+               wpa_dbg(active_work->wpa_s, MSG_DEBUG,
+                       "Do not parallelize radio work with %s",
+                       active_work->type);
+               return NULL;
+       }
+
+       dl_list_for_each(tmp, &radio->work, struct wpa_radio_work, list) {
+               if (tmp->started)
+                       continue;
+
+               /*
+                * If connect or sme-connect are enqueued, parallelize only
+                * those operations ahead of them in the queue.
+                */
+               if (os_strcmp(tmp->type, "connect") == 0 ||
+                   os_strcmp(tmp->type, "sme-connect") == 0)
+                       break;
+
+               /*
+                * Check that the radio works are distinct and
+                * on different bands.
+                */
+               if (os_strcmp(active_work->type, tmp->type) != 0 &&
+                   (active_work->bands != tmp->bands)) {
+                       /*
+                        * If a scan has to be scheduled through nl80211 scan
+                        * interface and if an external scan is already running,
+                        * do not schedule the scan since it is likely to get
+                        * rejected by kernel.
+                        */
+                       if (os_strcmp(tmp->type, "scan") == 0 &&
+                           radio->external_scan_running &&
+                           (((struct wpa_driver_scan_params *)
+                             tmp->ctx)->only_new_results ||
+                            tmp->wpa_s->clear_driver_scan_cache))
+                               continue;
+
+                       wpa_dbg(active_work->wpa_s, MSG_DEBUG,
+                               "active_work:%s new_work:%s",
+                               active_work->type, tmp->type);
+                       return tmp;
+               }
+       }
+
+       /* Did not find a radio work to schedule in parallel. */
+       return NULL;
+}
+
+
 static void radio_start_next_work(void *eloop_ctx, void *timeout_ctx)
 {
        struct wpa_radio *radio = eloop_ctx;
@@ -3092,26 +4272,48 @@ static void radio_start_next_work(void *eloop_ctx, void *timeout_ctx)
        struct wpa_supplicant *wpa_s;
 
        work = dl_list_first(&radio->work, struct wpa_radio_work, list);
-       if (work == NULL)
+       if (work == NULL) {
+               radio->num_active_works = 0;
                return;
-
-       if (work->started)
-               return; /* already started and still in progress */
+       }
 
        wpa_s = dl_list_first(&radio->ifaces, struct wpa_supplicant,
                              radio_list);
-       if (wpa_s && wpa_s->external_scan_running) {
-               wpa_printf(MSG_DEBUG, "Delay radio work start until externally triggered scan completes");
-               return;
+
+       if (!(wpa_s &&
+             wpa_s->drv_flags & WPA_DRIVER_FLAGS_OFFCHANNEL_SIMULTANEOUS)) {
+               if (work->started)
+                       return; /* already started and still in progress */
+
+               if (wpa_s && wpa_s->radio->external_scan_running) {
+                       wpa_printf(MSG_DEBUG, "Delay radio work start until externally triggered scan completes");
+                       return;
+               }
+       } else {
+               work = NULL;
+               if (radio->num_active_works < MAX_ACTIVE_WORKS) {
+                       /* get the work to schedule next */
+                       work = radio_work_get_next_work(radio);
+               }
+               if (!work)
+                       return;
        }
 
+       wpa_s = work->wpa_s;
        os_get_reltime(&now);
        os_reltime_sub(&now, &work->time, &diff);
-       wpa_dbg(work->wpa_s, MSG_DEBUG, "Starting radio work '%s'@%p after %ld.%06ld second wait",
+       wpa_dbg(wpa_s, MSG_DEBUG,
+               "Starting radio work '%s'@%p after %ld.%06ld second wait",
                work->type, work, diff.sec, diff.usec);
        work->started = 1;
        work->time = now;
+       radio->num_active_works++;
+
        work->cb(work, 0);
+
+       if ((wpa_s->drv_flags & WPA_DRIVER_FLAGS_OFFCHANNEL_SIMULTANEOUS) &&
+           radio->num_active_works < MAX_ACTIVE_WORKS)
+               radio_work_check_next(wpa_s);
 }
 
 
@@ -3162,15 +4364,13 @@ static void radio_remove_interface(struct wpa_supplicant *wpa_s)
        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;
+       radio_remove_works(wpa_s, NULL, 0);
+       wpa_s->radio = NULL;
+       if (!dl_list_empty(&radio->ifaces))
                return; /* Interfaces remain for this radio */
-       }
 
        wpa_printf(MSG_DEBUG, "Remove radio %s", radio->name);
-       radio_remove_works(wpa_s, NULL, 0);
        eloop_cancel_timeout(radio_start_next_work, radio, NULL);
-       wpa_s->radio = NULL;
        os_free(radio);
 }
 
@@ -3181,6 +4381,11 @@ void radio_work_check_next(struct wpa_supplicant *wpa_s)
 
        if (dl_list_empty(&radio->work))
                return;
+       if (wpa_s->ext_work_in_progress) {
+               wpa_printf(MSG_DEBUG,
+                          "External radio work in progress - delay start of pending item");
+               return;
+       }
        eloop_cancel_timeout(radio_start_next_work, radio, NULL);
        eloop_register_timeout(0, 0, radio_start_next_work, radio, NULL);
 }
@@ -3216,6 +4421,7 @@ int radio_add_work(struct wpa_supplicant *wpa_s, unsigned int freq,
                   void (*cb)(struct wpa_radio_work *work, int deinit),
                   void *ctx)
 {
+       struct wpa_radio *radio = wpa_s->radio;
        struct wpa_radio_work *work;
        int was_empty;
 
@@ -3230,6 +4436,16 @@ int radio_add_work(struct wpa_supplicant *wpa_s, unsigned int freq,
        work->cb = cb;
        work->ctx = ctx;
 
+       if (freq)
+               work->bands = wpas_freq_to_band(freq);
+       else if (os_strcmp(type, "scan") == 0 ||
+                os_strcmp(type, "p2p-scan") == 0)
+               work->bands = wpas_get_bands(wpa_s,
+                                            ((struct wpa_driver_scan_params *)
+                                             ctx)->freqs);
+       else
+               work->bands = wpas_get_bands(wpa_s, NULL);
+
        was_empty = dl_list_empty(&wpa_s->radio->work);
        if (next)
                dl_list_add(&wpa_s->radio->work, &work->list);
@@ -3238,6 +4454,12 @@ int radio_add_work(struct wpa_supplicant *wpa_s, unsigned int freq,
        if (was_empty) {
                wpa_dbg(wpa_s, MSG_DEBUG, "First radio work item in the queue - schedule start immediately");
                radio_work_check_next(wpa_s);
+       } else if ((wpa_s->drv_flags & WPA_DRIVER_FLAGS_OFFCHANNEL_SIMULTANEOUS)
+                  && radio->num_active_works < MAX_ACTIVE_WORKS) {
+               wpa_dbg(wpa_s, MSG_DEBUG,
+                       "Try to schedule a radio work (num_active_works=%u)",
+                       radio->num_active_works);
+               radio_work_check_next(wpa_s);
        }
 
        return 0;
@@ -3268,17 +4490,18 @@ void radio_work_done(struct wpa_radio_work *work)
 }
 
 
-int radio_work_pending(struct wpa_supplicant *wpa_s, const char *type)
+struct wpa_radio_work *
+radio_work_pending(struct wpa_supplicant *wpa_s, const char *type)
 {
        struct wpa_radio_work *work;
        struct wpa_radio *radio = wpa_s->radio;
 
        dl_list_for_each(work, &radio->work, struct wpa_radio_work, list) {
                if (work->wpa_s == wpa_s && os_strcmp(work->type, type) == 0)
-                       return 1;
+                       return work;
        }
 
-       return 0;
+       return NULL;
 }
 
 
@@ -3319,10 +4542,7 @@ next_driver:
                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;
+       rn = wpa_driver_get_radio_name(wpa_s);
        if (rn && rn[0] == '\0')
                rn = NULL;
 
@@ -3338,6 +4558,7 @@ static int wpa_supplicant_init_iface(struct wpa_supplicant *wpa_s,
                                     struct wpa_interface *iface)
 {
        struct wpa_driver_capa capa;
+       int capa_res;
 
        wpa_printf(MSG_DEBUG, "Initializing interface '%s' conf '%s' driver "
                   "'%s' ctrl_interface '%s' bridge '%s'", iface->ifname,
@@ -3466,14 +4687,39 @@ static int wpa_supplicant_init_iface(struct wpa_supplicant *wpa_s,
        wpa_s->hw.modes = wpa_drv_get_hw_feature_data(wpa_s,
                                                      &wpa_s->hw.num_modes,
                                                      &wpa_s->hw.flags);
+       if (wpa_s->hw.modes) {
+               u16 i;
+
+               for (i = 0; i < wpa_s->hw.num_modes; i++) {
+                       if (wpa_s->hw.modes[i].vht_capab) {
+                               wpa_s->hw_capab = CAPAB_VHT;
+                               break;
+                       }
+
+                       if (wpa_s->hw.modes[i].ht_capab &
+                           HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET)
+                               wpa_s->hw_capab = CAPAB_HT40;
+                       else if (wpa_s->hw.modes[i].ht_capab &&
+                                wpa_s->hw_capab == CAPAB_NO_HT_VHT)
+                               wpa_s->hw_capab = CAPAB_HT;
+               }
+       }
 
-       if (wpa_drv_get_capa(wpa_s, &capa) == 0) {
+       capa_res = wpa_drv_get_capa(wpa_s, &capa);
+       if (capa_res == 0) {
                wpa_s->drv_capa_known = 1;
                wpa_s->drv_flags = capa.flags;
                wpa_s->drv_enc = capa.enc;
+               wpa_s->drv_smps_modes = capa.smps_modes;
+               wpa_s->drv_rrm_flags = capa.rrm_flags;
                wpa_s->probe_resp_offloads = capa.probe_resp_offloads;
                wpa_s->max_scan_ssids = capa.max_scan_ssids;
                wpa_s->max_sched_scan_ssids = capa.max_sched_scan_ssids;
+               wpa_s->max_sched_scan_plans = capa.max_sched_scan_plans;
+               wpa_s->max_sched_scan_plan_interval =
+                       capa.max_sched_scan_plan_interval;
+               wpa_s->max_sched_scan_plan_iterations =
+                       capa.max_sched_scan_plan_iterations;
                wpa_s->sched_scan_supported = capa.sched_scan_supported;
                wpa_s->max_match_sets = capa.max_match_sets;
                wpa_s->max_remain_on_chan = capa.max_remain_on_chan;
@@ -3483,6 +4729,14 @@ static int wpa_supplicant_init_iface(struct wpa_supplicant *wpa_s,
                wpa_s->extended_capa_len = capa.extended_capa_len;
                wpa_s->num_multichan_concurrent =
                        capa.num_multichan_concurrent;
+               wpa_s->wmm_ac_supported = capa.wmm_ac_supported;
+
+               if (capa.mac_addr_rand_scan_supported)
+                       wpa_s->mac_addr_rand_supported |= MAC_ADDR_RAND_SCAN;
+               if (wpa_s->sched_scan_supported &&
+                   capa.mac_addr_rand_sched_scan_supported)
+                       wpa_s->mac_addr_rand_supported |=
+                               (MAC_ADDR_RAND_SCHED_SCAN | MAC_ADDR_RAND_PNO);
        }
        if (wpa_s->max_remain_on_chan == 0)
                wpa_s->max_remain_on_chan = 1000;
@@ -3517,6 +4771,28 @@ static int wpa_supplicant_init_iface(struct wpa_supplicant *wpa_s,
                return -1;
        }
 
+#ifdef CONFIG_FST
+       if (wpa_s->conf->fst_group_id) {
+               struct fst_iface_cfg cfg;
+               struct fst_wpa_obj iface_obj;
+
+               fst_wpa_supplicant_fill_iface_obj(wpa_s, &iface_obj);
+               os_strlcpy(cfg.group_id, wpa_s->conf->fst_group_id,
+                          sizeof(cfg.group_id));
+               cfg.priority = wpa_s->conf->fst_priority;
+               cfg.llt = wpa_s->conf->fst_llt;
+
+               wpa_s->fst = fst_attach(wpa_s->ifname, wpa_s->own_addr,
+                                       &iface_obj, &cfg);
+               if (!wpa_s->fst) {
+                       wpa_msg(wpa_s, MSG_ERROR,
+                               "FST: Cannot attach iface %s to group %s",
+                               wpa_s->ifname, cfg.group_id);
+                       return -1;
+               }
+       }
+#endif /* CONFIG_FST */
+
        if (wpas_wps_init(wpa_s))
                return -1;
 
@@ -3544,16 +4820,22 @@ static int wpa_supplicant_init_iface(struct wpa_supplicant *wpa_s,
                return -1;
        }
 
-#ifdef CONFIG_P2P
        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;
        }
-#endif /* CONFIG_P2P */
 
        if (wpa_bss_init(wpa_s) < 0)
                return -1;
 
+       /*
+        * Set Wake-on-WLAN triggers, if configured.
+        * Note: We don't restore/remove the triggers on shutdown (it doesn't
+        * have effect anyway when the interface is down).
+        */
+       if (capa_res == 0 && wpas_set_wowlan_triggers(wpa_s, &capa) < 0)
+               return -1;
+
 #ifdef CONFIG_EAP_PROXY
 {
        size_t len;
@@ -3575,6 +4857,17 @@ static int wpa_supplicant_init_iface(struct wpa_supplicant *wpa_s,
        if (wpas_init_ext_pw(wpa_s) < 0)
                return -1;
 
+       wpas_rrm_reset(wpa_s);
+
+       wpas_sched_scan_plans_set(wpa_s, wpa_s->conf->sched_scan_plans);
+
+#ifdef CONFIG_HS20
+       hs20_init(wpa_s);
+#endif /* CONFIG_HS20 */
+#ifdef CONFIG_MBO
+       wpas_mbo_update_non_pref_chan(wpa_s, wpa_s->conf->non_pref_chan);
+#endif /* CONFIG_MBO */
+
        return 0;
 }
 
@@ -3582,6 +4875,28 @@ static int wpa_supplicant_init_iface(struct wpa_supplicant *wpa_s,
 static void wpa_supplicant_deinit_iface(struct wpa_supplicant *wpa_s,
                                        int notify, int terminate)
 {
+       struct wpa_global *global = wpa_s->global;
+       struct wpa_supplicant *iface, *prev;
+
+       if (wpa_s == wpa_s->parent)
+               wpas_p2p_group_remove(wpa_s, "*");
+
+       iface = global->ifaces;
+       while (iface) {
+               if (iface->p2pdev == wpa_s)
+                       iface->p2pdev = iface->parent;
+               if (iface == wpa_s || iface->parent != wpa_s) {
+                       iface = iface->next;
+                       continue;
+               }
+               wpa_printf(MSG_DEBUG,
+                          "Remove remaining child interface %s from parent %s",
+                          iface->ifname, wpa_s->ifname);
+               prev = iface;
+               iface = iface->next;
+               wpa_supplicant_remove_iface(global, prev, terminate);
+       }
+
        wpa_s->disconnected = 1;
        if (wpa_s->drv_priv) {
                wpa_supplicant_deauthenticate(wpa_s,
@@ -3592,18 +4907,22 @@ static void wpa_supplicant_deinit_iface(struct wpa_supplicant *wpa_s,
        }
 
        wpa_supplicant_cleanup(wpa_s);
-
-#ifdef CONFIG_P2P
-       if (wpa_s == wpa_s->global->p2p_init_wpa_s && wpa_s->global->p2p) {
-               wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Disable P2P since removing "
-                       "the management interface is being removed");
-               wpas_p2p_deinit_global(wpa_s->global);
-       }
-#endif /* CONFIG_P2P */
+       wpas_p2p_deinit_iface(wpa_s);
 
        wpas_ctrl_radio_work_flush(wpa_s);
        radio_remove_interface(wpa_s);
 
+#ifdef CONFIG_FST
+       if (wpa_s->fst) {
+               fst_detach(wpa_s->fst);
+               wpa_s->fst = NULL;
+       }
+       if (wpa_s->received_mb_ies) {
+               wpabuf_free(wpa_s->received_mb_ies);
+               wpa_s->received_mb_ies = NULL;
+       }
+#endif /* CONFIG_FST */
+
        if (wpa_s->drv_priv)
                wpa_drv_deinit(wpa_s);
 
@@ -3618,19 +4937,97 @@ static void wpa_supplicant_deinit_iface(struct wpa_supplicant *wpa_s,
                wpa_s->ctrl_iface = NULL;
        }
 
+#ifdef CONFIG_MESH
+       if (wpa_s->ifmsh) {
+               wpa_supplicant_mesh_iface_deinit(wpa_s, wpa_s->ifmsh);
+               wpa_s->ifmsh = NULL;
+       }
+#endif /* CONFIG_MESH */
+
        if (wpa_s->conf != NULL) {
                wpa_config_free(wpa_s->conf);
                wpa_s->conf = NULL;
        }
 
+       os_free(wpa_s->ssids_from_scan_req);
+
        os_free(wpa_s);
 }
 
 
+#ifdef CONFIG_MATCH_IFACE
+
+/**
+ * wpa_supplicant_match_iface - Match an interface description to a name
+ * @global: Pointer to global data from wpa_supplicant_init()
+ * @ifname: Name of the interface to match
+ * Returns: Pointer to the created interface description or %NULL on failure
+ */
+struct wpa_interface * wpa_supplicant_match_iface(struct wpa_global *global,
+                                                 const char *ifname)
+{
+       int i;
+       struct wpa_interface *iface, *miface;
+
+       for (i = 0; i < global->params.match_iface_count; i++) {
+               miface = &global->params.match_ifaces[i];
+               if (!miface->ifname ||
+                   fnmatch(miface->ifname, ifname, 0) == 0) {
+                       iface = os_zalloc(sizeof(*iface));
+                       if (!iface)
+                               return NULL;
+                       *iface = *miface;
+                       iface->ifname = ifname;
+                       return iface;
+               }
+       }
+
+       return NULL;
+}
+
+
+/**
+ * wpa_supplicant_match_existing - Match existing interfaces
+ * @global: Pointer to global data from wpa_supplicant_init()
+ * Returns: 0 on success, -1 on failure
+ */
+static int wpa_supplicant_match_existing(struct wpa_global *global)
+{
+       struct if_nameindex *ifi, *ifp;
+       struct wpa_supplicant *wpa_s;
+       struct wpa_interface *iface;
+
+       ifp = if_nameindex();
+       if (!ifp) {
+               wpa_printf(MSG_ERROR, "if_nameindex: %s", strerror(errno));
+               return -1;
+       }
+
+       for (ifi = ifp; ifi->if_name; ifi++) {
+               wpa_s = wpa_supplicant_get_iface(global, ifi->if_name);
+               if (wpa_s)
+                       continue;
+               iface = wpa_supplicant_match_iface(global, ifi->if_name);
+               if (iface) {
+                       wpa_s = wpa_supplicant_add_iface(global, iface, NULL);
+                       os_free(iface);
+                       if (wpa_s)
+                               wpa_s->matched = 1;
+               }
+       }
+
+       if_freenameindex(ifp);
+       return 0;
+}
+
+#endif /* CONFIG_MATCH_IFACE */
+
+
 /**
  * wpa_supplicant_add_iface - Add a new network interface
  * @global: Pointer to global data from wpa_supplicant_init()
  * @iface: Interface configuration options
+ * @parent: Parent interface or %NULL to assign new interface as parent
  * Returns: Pointer to the created interface or %NULL on failure
  *
  * This function is used to add new network interfaces for %wpa_supplicant.
@@ -3640,7 +5037,8 @@ static void wpa_supplicant_deinit_iface(struct wpa_supplicant *wpa_s,
  * e.g., when a hotplug network adapter is inserted.
  */
 struct wpa_supplicant * wpa_supplicant_add_iface(struct wpa_global *global,
-                                                struct wpa_interface *iface)
+                                                struct wpa_interface *iface,
+                                                struct wpa_supplicant *parent)
 {
        struct wpa_supplicant *wpa_s;
        struct wpa_interface t_iface;
@@ -3649,7 +5047,7 @@ struct wpa_supplicant * wpa_supplicant_add_iface(struct wpa_global *global,
        if (global == NULL || iface == NULL)
                return NULL;
 
-       wpa_s = wpa_supplicant_alloc();
+       wpa_s = wpa_supplicant_alloc(parent);
        if (wpa_s == NULL)
                return NULL;
 
@@ -3677,14 +5075,16 @@ struct wpa_supplicant * wpa_supplicant_add_iface(struct wpa_global *global,
                return NULL;
        }
 
-       /* Notify the control interfaces about new iface */
-       if (wpas_notify_iface_added(wpa_s)) {
-               wpa_supplicant_deinit_iface(wpa_s, 1, 0);
-               return NULL;
-       }
-
-       for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next)
-               wpas_notify_network_added(wpa_s, ssid);
+       if (iface->p2p_mgmt == 0) {
+               /* Notify the control interfaces about new iface */
+               if (wpas_notify_iface_added(wpa_s)) {
+                       wpa_supplicant_deinit_iface(wpa_s, 1, 0);
+                       return NULL;
+               }
+
+               for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next)
+                       wpas_notify_network_added(wpa_s, ssid);
+       }
 
        wpa_s->next = global->ifaces;
        global->ifaces = wpa_s;
@@ -3692,6 +5092,18 @@ struct wpa_supplicant * wpa_supplicant_add_iface(struct wpa_global *global,
        wpa_dbg(wpa_s, MSG_DEBUG, "Added interface %s", wpa_s->ifname);
        wpa_supplicant_set_state(wpa_s, WPA_DISCONNECTED);
 
+#ifdef CONFIG_P2P
+       if (wpa_s->global->p2p == NULL &&
+           !wpa_s->global->p2p_disabled && !wpa_s->conf->p2p_disabled &&
+           (wpa_s->drv_flags & WPA_DRIVER_FLAGS_DEDICATED_P2P_DEVICE) &&
+           wpas_p2p_add_p2pdev_interface(
+                   wpa_s, wpa_s->global->params.conf_p2p_dev) < 0) {
+               wpa_printf(MSG_INFO,
+                          "P2P: Failed to enable P2P Device interface");
+               /* Try to continue without. P2P will be disabled. */
+       }
+#endif /* CONFIG_P2P */
+
        return wpa_s;
 }
 
@@ -3712,6 +5124,10 @@ int wpa_supplicant_remove_iface(struct wpa_global *global,
                                int terminate)
 {
        struct wpa_supplicant *prev;
+#ifdef CONFIG_MESH
+       unsigned int mesh_if_created = wpa_s->mesh_if_created;
+       char *ifname = NULL;
+#endif /* CONFIG_MESH */
 
        /* Remove interface from the global list of interfaces */
        prev = global->ifaces;
@@ -3727,12 +5143,30 @@ int wpa_supplicant_remove_iface(struct wpa_global *global,
 
        wpa_dbg(wpa_s, MSG_DEBUG, "Removing interface %s", wpa_s->ifname);
 
+#ifdef CONFIG_MESH
+       if (mesh_if_created) {
+               ifname = os_strdup(wpa_s->ifname);
+               if (ifname == NULL) {
+                       wpa_dbg(wpa_s, MSG_ERROR,
+                               "mesh: Failed to malloc ifname");
+                       return -1;
+               }
+       }
+#endif /* CONFIG_MESH */
+
        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);
 
+#ifdef CONFIG_MESH
+       if (mesh_if_created) {
+               wpa_drv_if_remove(global->ifaces, WPA_IF_MESH, ifname);
+               os_free(ifname);
+       }
+#endif /* CONFIG_MESH */
+
        return 0;
 }
 
@@ -3789,6 +5223,33 @@ static const char * wpa_supplicant_msg_ifname_cb(void *ctx)
 #endif /* CONFIG_NO_WPA_MSG */
 
 
+#ifndef WPA_SUPPLICANT_CLEANUP_INTERVAL
+#define WPA_SUPPLICANT_CLEANUP_INTERVAL 10
+#endif /* WPA_SUPPLICANT_CLEANUP_INTERVAL */
+
+/* Periodic cleanup tasks */
+static void wpas_periodic(void *eloop_ctx, void *timeout_ctx)
+{
+       struct wpa_global *global = eloop_ctx;
+       struct wpa_supplicant *wpa_s;
+
+       eloop_register_timeout(WPA_SUPPLICANT_CLEANUP_INTERVAL, 0,
+                              wpas_periodic, global, NULL);
+
+#ifdef CONFIG_P2P
+       if (global->p2p)
+               p2p_expire_peers(global->p2p);
+#endif /* CONFIG_P2P */
+
+       for (wpa_s = global->ifaces; wpa_s; wpa_s = wpa_s->next) {
+               wpa_bss_flush_by_age(wpa_s, wpa_s->conf->bss_expiration_age);
+#ifdef CONFIG_AP
+               ap_periodic(wpa_s);
+#endif /* CONFIG_AP */
+       }
+}
+
+
 /**
  * wpa_supplicant_init - Initialize %wpa_supplicant
  * @params: Parameters for %wpa_supplicant
@@ -3817,7 +5278,10 @@ struct wpa_global * wpa_supplicant_init(struct wpa_params *params)
        wpa_msg_register_ifname_cb(wpa_supplicant_msg_ifname_cb);
 #endif /* CONFIG_NO_WPA_MSG */
 
-       wpa_debug_open_file(params->wpa_debug_file_path);
+       if (params->wpa_debug_file_path)
+               wpa_debug_open_file(params->wpa_debug_file_path);
+       else
+               wpa_debug_setup_stdout();
        if (params->wpa_debug_syslog)
                wpa_debug_open_syslog();
        if (params->wpa_debug_tracing) {
@@ -3860,6 +5324,23 @@ struct wpa_global * wpa_supplicant_init(struct wpa_params *params)
        if (params->override_ctrl_interface)
                global->params.override_ctrl_interface =
                        os_strdup(params->override_ctrl_interface);
+#ifdef CONFIG_MATCH_IFACE
+       global->params.match_iface_count = params->match_iface_count;
+       if (params->match_iface_count) {
+               global->params.match_ifaces =
+                       os_calloc(params->match_iface_count,
+                                 sizeof(struct wpa_interface));
+               os_memcpy(global->params.match_ifaces,
+                         params->match_ifaces,
+                         params->match_iface_count *
+                         sizeof(struct wpa_interface));
+       }
+#endif /* CONFIG_MATCH_IFACE */
+#ifdef CONFIG_P2P
+       if (params->conf_p2p_dev)
+               global->params.conf_p2p_dev =
+                       os_strdup(params->conf_p2p_dev);
+#endif /* CONFIG_P2P */
        wpa_debug_level = global->params.wpa_debug_level =
                params->wpa_debug_level;
        wpa_debug_show_keys = global->params.wpa_debug_show_keys =
@@ -3895,7 +5376,7 @@ struct wpa_global * wpa_supplicant_init(struct wpa_params *params)
                wpa_supplicant_deinit(global);
                return NULL;
        }
-       global->drv_priv = os_zalloc(global->drv_count * sizeof(void *));
+       global->drv_priv = os_calloc(global->drv_count, sizeof(void *));
        if (global->drv_priv == NULL) {
                wpa_supplicant_deinit(global);
                return NULL;
@@ -3909,6 +5390,9 @@ struct wpa_global * wpa_supplicant_init(struct wpa_params *params)
        }
 #endif /* CONFIG_WIFI_DISPLAY */
 
+       eloop_register_timeout(WPA_SUPPLICANT_CLEANUP_INTERVAL, 0,
+                              wpas_periodic, global, NULL);
+
        return global;
 }
 
@@ -3927,12 +5411,18 @@ int wpa_supplicant_run(struct wpa_global *global)
        struct wpa_supplicant *wpa_s;
 
        if (global->params.daemonize &&
-           wpa_supplicant_daemon(global->params.pid_file))
+           (wpa_supplicant_daemon(global->params.pid_file) ||
+            eloop_sock_requeue()))
+               return -1;
+
+#ifdef CONFIG_MATCH_IFACE
+       if (wpa_supplicant_match_existing(global))
                return -1;
+#endif
 
        if (global->params.wait_for_monitor) {
                for (wpa_s = global->ifaces; wpa_s; wpa_s = wpa_s->next)
-                       if (wpa_s->ctrl_iface)
+                       if (wpa_s->ctrl_iface && !wpa_s->p2p_mgmt)
                                wpa_supplicant_ctrl_iface_wait(
                                        wpa_s->ctrl_iface);
        }
@@ -3960,6 +5450,8 @@ void wpa_supplicant_deinit(struct wpa_global *global)
        if (global == NULL)
                return;
 
+       eloop_cancel_timeout(wpas_periodic, global, NULL);
+
 #ifdef CONFIG_WIFI_DISPLAY
        wifi_display_deinit(global);
 #endif /* CONFIG_WIFI_DISPLAY */
@@ -3996,6 +5488,12 @@ void wpa_supplicant_deinit(struct wpa_global *global)
        os_free(global->params.ctrl_interface_group);
        os_free(global->params.override_driver);
        os_free(global->params.override_ctrl_interface);
+#ifdef CONFIG_MATCH_IFACE
+       os_free(global->params.match_ifaces);
+#endif /* CONFIG_MATCH_IFACE */
+#ifdef CONFIG_P2P
+       os_free(global->params.conf_p2p_dev);
+#endif /* CONFIG_P2P */
 
        os_free(global->p2p_disallow_freq.range);
        os_free(global->p2p_go_avoid_freq.range);
@@ -4025,19 +5523,18 @@ void wpa_supplicant_update_config(struct wpa_supplicant *wpa_s)
        if (wpa_s->conf->changed_parameters & CFG_CHANGED_EXT_PW_BACKEND)
                wpas_init_ext_pw(wpa_s);
 
+       if (wpa_s->conf->changed_parameters & CFG_CHANGED_SCHED_SCAN_PLANS)
+               wpas_sched_scan_plans_set(wpa_s, wpa_s->conf->sched_scan_plans);
+
 #ifdef CONFIG_WPS
        wpas_wps_update_config(wpa_s);
 #endif /* CONFIG_WPS */
-
-#ifdef CONFIG_P2P
        wpas_p2p_update_config(wpa_s);
-#endif /* CONFIG_P2P */
-
        wpa_s->conf->changed_parameters = 0;
 }
 
 
-static void add_freq(int *freqs, int *num_freqs, int freq)
+void add_freq(int *freqs, int *num_freqs, int freq)
 {
        int i;
 
@@ -4058,7 +5555,7 @@ static int * get_bss_freqs_in_ess(struct wpa_supplicant *wpa_s)
        int *freqs;
        int num_freqs = 0;
 
-       freqs = os_zalloc(sizeof(int) * (max_freqs + 1));
+       freqs = os_calloc(max_freqs + 1, sizeof(int));
        if (freqs == NULL)
                return NULL;
 
@@ -4098,11 +5595,17 @@ void wpas_connection_failed(struct wpa_supplicant *wpa_s, const u8 *bssid)
         */
        eloop_cancel_timeout(wpa_supplicant_timeout, wpa_s, NULL);
 
+       /*
+        * There is no point in blacklisting the AP if this event is
+        * generated based on local request to disconnect.
+        */
+       if (wpa_s->own_disconnect_req) {
+               wpa_s->own_disconnect_req = 0;
+               wpa_dbg(wpa_s, MSG_DEBUG,
+                       "Ignore connection failure due to local request to disconnect");
+               return;
+       }
        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");
@@ -4149,7 +5652,7 @@ void wpas_connection_failed(struct wpa_supplicant *wpa_s, const u8 *bssid)
        if (count > 3 && wpa_s->current_ssid) {
                wpa_printf(MSG_DEBUG, "Continuous association failures - "
                           "consider temporary network disabling");
-               wpas_auth_failed(wpa_s);
+               wpas_auth_failed(wpa_s, "CONN_FAILED");
        }
 
        switch (count) {
@@ -4212,7 +5715,7 @@ int wpa_supplicant_ctrl_iface_ctrl_rsp_handle(struct wpa_supplicant *wpa_s,
                        wpa_s->reassociate = 1;
                break;
        case WPA_CTRL_REQ_EAP_PASSWORD:
-               os_free(eap->password);
+               bin_clear_free(eap->password, eap->password_len);
                eap->password = (u8 *) os_strdup(value);
                eap->password_len = os_strlen(value);
                eap->pending_req_password = 0;
@@ -4220,7 +5723,7 @@ int wpa_supplicant_ctrl_iface_ctrl_rsp_handle(struct wpa_supplicant *wpa_s,
                        wpa_s->reassociate = 1;
                break;
        case WPA_CTRL_REQ_EAP_NEW_PASSWORD:
-               os_free(eap->new_password);
+               bin_clear_free(eap->new_password, eap->new_password_len);
                eap->new_password = (u8 *) os_strdup(value);
                eap->new_password_len = os_strlen(value);
                eap->pending_req_new_password = 0;
@@ -4228,14 +5731,14 @@ int wpa_supplicant_ctrl_iface_ctrl_rsp_handle(struct wpa_supplicant *wpa_s,
                        wpa_s->reassociate = 1;
                break;
        case WPA_CTRL_REQ_EAP_PIN:
-               os_free(eap->pin);
+               str_clear_free(eap->pin);
                eap->pin = os_strdup(value);
                eap->pending_req_pin = 0;
                if (ssid == wpa_s->current_ssid)
                        wpa_s->reassociate = 1;
                break;
        case WPA_CTRL_REQ_EAP_OTP:
-               os_free(eap->otp);
+               bin_clear_free(eap->otp, eap->otp_len);
                eap->otp = (u8 *) os_strdup(value);
                eap->otp_len = os_strlen(value);
                os_free(eap->pending_req_otp);
@@ -4243,16 +5746,35 @@ int wpa_supplicant_ctrl_iface_ctrl_rsp_handle(struct wpa_supplicant *wpa_s,
                eap->pending_req_otp_len = 0;
                break;
        case WPA_CTRL_REQ_EAP_PASSPHRASE:
-               os_free(eap->private_key_passwd);
-               eap->private_key_passwd = (u8 *) os_strdup(value);
+               str_clear_free(eap->private_key_passwd);
+               eap->private_key_passwd = os_strdup(value);
                eap->pending_req_passphrase = 0;
                if (ssid == wpa_s->current_ssid)
                        wpa_s->reassociate = 1;
                break;
        case WPA_CTRL_REQ_SIM:
-               os_free(eap->external_sim_resp);
+               str_clear_free(eap->external_sim_resp);
                eap->external_sim_resp = os_strdup(value);
                break;
+       case WPA_CTRL_REQ_PSK_PASSPHRASE:
+               if (wpa_config_set(ssid, "psk", value, 0) < 0)
+                       return -1;
+               ssid->mem_only_psk = 1;
+               if (ssid->passphrase)
+                       wpa_config_update_psk(ssid);
+               if (wpa_s->wpa_state == WPA_SCANNING && !wpa_s->scanning)
+                       wpa_supplicant_req_scan(wpa_s, 0, 0);
+               break;
+       case WPA_CTRL_REQ_EXT_CERT_CHECK:
+               if (eap->pending_ext_cert_check != PENDING_CHECK)
+                       return -1;
+               if (os_strcmp(value, "good") == 0)
+                       eap->pending_ext_cert_check = EXT_CERT_CHECK_GOOD;
+               else if (os_strcmp(value, "bad") == 0)
+                       eap->pending_ext_cert_check = EXT_CERT_CHECK_BAD;
+               else
+                       return -1;
+               break;
        default:
                wpa_printf(MSG_DEBUG, "CTRL_IFACE: Unknown field '%s'", field);
                return -1;
@@ -4272,13 +5794,16 @@ int wpas_network_disabled(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid)
        int i;
        unsigned int drv_enc;
 
+       if (wpa_s->p2p_mgmt)
+               return 1; /* no normal network profiles on p2p_mgmt interface */
+
        if (ssid == NULL)
                return 1;
 
        if (ssid->disabled)
                return 1;
 
-       if (wpa_s && wpa_s->drv_capa_known)
+       if (wpa_s->drv_capa_known)
                drv_enc = wpa_s->drv_enc;
        else
                drv_enc = (unsigned int) -1;
@@ -4297,13 +5822,51 @@ int wpas_network_disabled(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid)
        }
 
        if (wpa_key_mgmt_wpa_psk(ssid->key_mgmt) && !ssid->psk_set &&
-           !ssid->ext_psk)
+           (!ssid->passphrase || ssid->ssid_len != 0) && !ssid->ext_psk &&
+           !ssid->mem_only_psk)
                return 1;
 
        return 0;
 }
 
 
+int wpas_get_ssid_pmf(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid)
+{
+#ifdef CONFIG_IEEE80211W
+       if (ssid == NULL || ssid->ieee80211w == MGMT_FRAME_PROTECTION_DEFAULT) {
+               if (wpa_s->conf->pmf == MGMT_FRAME_PROTECTION_OPTIONAL &&
+                   !(wpa_s->drv_enc & WPA_DRIVER_CAPA_ENC_BIP)) {
+                       /*
+                        * Driver does not support BIP -- ignore pmf=1 default
+                        * since the connection with PMF would fail and the
+                        * configuration does not require PMF to be enabled.
+                        */
+                       return NO_MGMT_FRAME_PROTECTION;
+               }
+
+               if (ssid &&
+                   (ssid->key_mgmt &
+                    ~(WPA_KEY_MGMT_NONE | WPA_KEY_MGMT_WPS |
+                      WPA_KEY_MGMT_IEEE8021X_NO_WPA)) == 0) {
+                       /*
+                        * Do not use the default PMF value for non-RSN networks
+                        * since PMF is available only with RSN and pmf=2
+                        * configuration would otherwise prevent connections to
+                        * all open networks.
+                        */
+                       return NO_MGMT_FRAME_PROTECTION;
+               }
+
+               return wpa_s->conf->pmf;
+       }
+
+       return ssid->ieee80211w;
+#else /* CONFIG_IEEE80211W */
+       return NO_MGMT_FRAME_PROTECTION;
+#endif /* CONFIG_IEEE80211W */
+}
+
+
 int wpas_is_p2p_prioritized(struct wpa_supplicant *wpa_s)
 {
        if (wpa_s->global->conc_pref == WPA_CONC_PREF_P2P)
@@ -4314,7 +5877,7 @@ int wpas_is_p2p_prioritized(struct wpa_supplicant *wpa_s)
 }
 
 
-void wpas_auth_failed(struct wpa_supplicant *wpa_s)
+void wpas_auth_failed(struct wpa_supplicant *wpa_s, char *reason)
 {
        struct wpa_ssid *ssid = wpa_s->current_ssid;
        int dur;
@@ -4368,9 +5931,9 @@ void wpas_auth_failed(struct wpa_supplicant *wpa_s)
        ssid->disabled_until.sec = now.sec + dur;
 
        wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_TEMP_DISABLED
-               "id=%d ssid=\"%s\" auth_failures=%u duration=%d",
+               "id=%d ssid=\"%s\" auth_failures=%u duration=%d reason=%s",
                ssid->id, wpa_ssid_txt(ssid->ssid, ssid->ssid_len),
-               ssid->auth_failures, dur);
+               ssid->auth_failures, dur, reason);
 }
 
 
@@ -4439,6 +6002,7 @@ int disallowed_ssid(struct wpa_supplicant *wpa_s, const u8 *ssid,
 void wpas_request_connection(struct wpa_supplicant *wpa_s)
 {
        wpa_s->normal_scans = 0;
+       wpa_s->scan_req = NORMAL_SCAN_REQ;
        wpa_supplicant_reinit_autoscan(wpa_s);
        wpa_s->extra_blacklist_count = 0;
        wpa_s->disconnected = 0;
@@ -4446,27 +6010,35 @@ void wpas_request_connection(struct wpa_supplicant *wpa_s)
 
        if (wpa_supplicant_fast_associate(wpa_s) != 1)
                wpa_supplicant_req_scan(wpa_s, 0, 0);
+       else
+               wpa_s->reattach = 0;
 }
 
 
-void dump_freq_array(struct wpa_supplicant *wpa_s, const char *title,
-                    int *freq_array, unsigned int len)
+void dump_freq_data(struct wpa_supplicant *wpa_s, const char *title,
+                   struct wpa_used_freq_data *freqs_data,
+                   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]);
+       for (i = 0; i < len; i++) {
+               struct wpa_used_freq_data *cur = &freqs_data[i];
+               wpa_dbg(wpa_s, MSG_DEBUG, "freq[%u]: %d, flags=0x%X",
+                       i, cur->freq, cur->flags);
+       }
 }
 
 
 /*
  * Find the operating frequencies of any of the virtual interfaces that
- * are using the same radio as the current interface.
+ * are using the same radio as the current interface, and in addition, get
+ * information about the interface types that are using the frequency.
  */
-int get_shared_radio_freqs(struct wpa_supplicant *wpa_s,
-                          int *freq_array, unsigned int len)
+int get_shared_radio_freqs_data(struct wpa_supplicant *wpa_s,
+                               struct wpa_used_freq_data *freqs_data,
+                               unsigned int len)
 {
        struct wpa_supplicant *ifs;
        u8 bssid[ETH_ALEN];
@@ -4475,37 +6047,19 @@ int get_shared_radio_freqs(struct wpa_supplicant *wpa_s,
 
        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;
-       }
+       os_memset(freqs_data, 0, sizeof(struct wpa_used_freq_data) * len);
 
        dl_list_for_each(ifs, &wpa_s->radio->ifaces, struct wpa_supplicant,
                         radio_list) {
-               if (wpa_s == ifs)
-                       continue;
+               if (idx == len)
+                       break;
 
                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)
+                   ifs->current_ssid->mode == WPAS_MODE_P2P_GO ||
+                   ifs->current_ssid->mode == WPAS_MODE_MESH)
                        freq = ifs->current_ssid->frequency;
                else if (wpa_drv_get_bssid(ifs, bssid) == 0)
                        freq = ifs->assoc_freq;
@@ -4514,13 +6068,700 @@ int get_shared_radio_freqs(struct wpa_supplicant *wpa_s,
 
                /* Hold only distinct freqs */
                for (i = 0; i < idx; i++)
-                       if (freq_array[i] == freq)
+                       if (freqs_data[i].freq == freq)
                                break;
 
                if (i == idx)
-                       freq_array[idx++] = freq;
+                       freqs_data[idx++].freq = freq;
+
+               if (ifs->current_ssid->mode == WPAS_MODE_INFRA) {
+                       freqs_data[i].flags |= ifs->current_ssid->p2p_group ?
+                               WPA_FREQ_USED_BY_P2P_CLIENT :
+                               WPA_FREQ_USED_BY_INFRA_STATION;
+               }
        }
 
-       dump_freq_array(wpa_s, "completed iteration", freq_array, idx);
+       dump_freq_data(wpa_s, "completed iteration", freqs_data, idx);
        return idx;
 }
+
+
+/*
+ * 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_used_freq_data *freqs_data;
+       int num, i;
+
+       os_memset(freq_array, 0, sizeof(int) * len);
+
+       freqs_data = os_calloc(len, sizeof(struct wpa_used_freq_data));
+       if (!freqs_data)
+               return -1;
+
+       num = get_shared_radio_freqs_data(wpa_s, freqs_data, len);
+       for (i = 0; i < num; i++)
+               freq_array[i] = freqs_data[i].freq;
+
+       os_free(freqs_data);
+
+       return num;
+}
+
+
+static void wpas_rrm_neighbor_rep_timeout_handler(void *data, void *user_ctx)
+{
+       struct rrm_data *rrm = data;
+
+       if (!rrm->notify_neighbor_rep) {
+               wpa_printf(MSG_ERROR,
+                          "RRM: Unexpected neighbor report timeout");
+               return;
+       }
+
+       wpa_printf(MSG_DEBUG, "RRM: Notifying neighbor report - NONE");
+       rrm->notify_neighbor_rep(rrm->neighbor_rep_cb_ctx, NULL);
+
+       rrm->notify_neighbor_rep = NULL;
+       rrm->neighbor_rep_cb_ctx = NULL;
+}
+
+
+/*
+ * wpas_rrm_reset - Clear and reset all RRM data in wpa_supplicant
+ * @wpa_s: Pointer to wpa_supplicant
+ */
+void wpas_rrm_reset(struct wpa_supplicant *wpa_s)
+{
+       wpa_s->rrm.rrm_used = 0;
+
+       eloop_cancel_timeout(wpas_rrm_neighbor_rep_timeout_handler, &wpa_s->rrm,
+                            NULL);
+       if (wpa_s->rrm.notify_neighbor_rep)
+               wpas_rrm_neighbor_rep_timeout_handler(&wpa_s->rrm, NULL);
+       wpa_s->rrm.next_neighbor_rep_token = 1;
+}
+
+
+/*
+ * wpas_rrm_process_neighbor_rep - Handle incoming neighbor report
+ * @wpa_s: Pointer to wpa_supplicant
+ * @report: Neighbor report buffer, prefixed by a 1-byte dialog token
+ * @report_len: Length of neighbor report buffer
+ */
+void wpas_rrm_process_neighbor_rep(struct wpa_supplicant *wpa_s,
+                                  const u8 *report, size_t report_len)
+{
+       struct wpabuf *neighbor_rep;
+
+       wpa_hexdump(MSG_DEBUG, "RRM: New Neighbor Report", report, report_len);
+       if (report_len < 1)
+               return;
+
+       if (report[0] != wpa_s->rrm.next_neighbor_rep_token - 1) {
+               wpa_printf(MSG_DEBUG,
+                          "RRM: Discarding neighbor report with token %d (expected %d)",
+                          report[0], wpa_s->rrm.next_neighbor_rep_token - 1);
+               return;
+       }
+
+       eloop_cancel_timeout(wpas_rrm_neighbor_rep_timeout_handler, &wpa_s->rrm,
+                            NULL);
+
+       if (!wpa_s->rrm.notify_neighbor_rep) {
+               wpa_printf(MSG_ERROR, "RRM: Unexpected neighbor report");
+               return;
+       }
+
+       /* skipping the first byte, which is only an id (dialog token) */
+       neighbor_rep = wpabuf_alloc(report_len - 1);
+       if (neighbor_rep == NULL)
+               return;
+       wpabuf_put_data(neighbor_rep, report + 1, report_len - 1);
+       wpa_printf(MSG_DEBUG, "RRM: Notifying neighbor report (token = %d)",
+                  report[0]);
+       wpa_s->rrm.notify_neighbor_rep(wpa_s->rrm.neighbor_rep_cb_ctx,
+                                      neighbor_rep);
+       wpa_s->rrm.notify_neighbor_rep = NULL;
+       wpa_s->rrm.neighbor_rep_cb_ctx = NULL;
+}
+
+
+#if defined(__CYGWIN__) || defined(CONFIG_NATIVE_WINDOWS)
+/* Workaround different, undefined for Windows, error codes used here */
+#define ENOTCONN -1
+#define EOPNOTSUPP -1
+#define ECANCELED -1
+#endif
+
+/* Measurement Request element + Location Subject + Maximum Age subelement */
+#define MEASURE_REQUEST_LCI_LEN (3 + 1 + 4)
+/* Measurement Request element + Location Civic Request */
+#define MEASURE_REQUEST_CIVIC_LEN (3 + 5)
+
+
+/**
+ * wpas_rrm_send_neighbor_rep_request - Request a neighbor report from our AP
+ * @wpa_s: Pointer to wpa_supplicant
+ * @ssid: if not null, this is sent in the request. Otherwise, no SSID IE
+ *       is sent in the request.
+ * @lci: if set, neighbor request will include LCI request
+ * @civic: if set, neighbor request will include civic location request
+ * @cb: Callback function to be called once the requested report arrives, or
+ *     timed out after RRM_NEIGHBOR_REPORT_TIMEOUT seconds.
+ *     In the former case, 'neighbor_rep' is a newly allocated wpabuf, and it's
+ *     the requester's responsibility to free it.
+ *     In the latter case NULL will be sent in 'neighbor_rep'.
+ * @cb_ctx: Context value to send the callback function
+ * Returns: 0 in case of success, negative error code otherwise
+ *
+ * In case there is a previous request which has not been answered yet, the
+ * new request fails. The caller may retry after RRM_NEIGHBOR_REPORT_TIMEOUT.
+ * Request must contain a callback function.
+ */
+int wpas_rrm_send_neighbor_rep_request(struct wpa_supplicant *wpa_s,
+                                      const struct wpa_ssid_value *ssid,
+                                      int lci, int civic,
+                                      void (*cb)(void *ctx,
+                                                 struct wpabuf *neighbor_rep),
+                                      void *cb_ctx)
+{
+       struct wpabuf *buf;
+       const u8 *rrm_ie;
+
+       if (wpa_s->wpa_state != WPA_COMPLETED || wpa_s->current_ssid == NULL) {
+               wpa_printf(MSG_DEBUG, "RRM: No connection, no RRM.");
+               return -ENOTCONN;
+       }
+
+       if (!wpa_s->rrm.rrm_used) {
+               wpa_printf(MSG_DEBUG, "RRM: No RRM in current connection.");
+               return -EOPNOTSUPP;
+       }
+
+       rrm_ie = wpa_bss_get_ie(wpa_s->current_bss,
+                               WLAN_EID_RRM_ENABLED_CAPABILITIES);
+       if (!rrm_ie || !(wpa_s->current_bss->caps & IEEE80211_CAP_RRM) ||
+           !(rrm_ie[2] & WLAN_RRM_CAPS_NEIGHBOR_REPORT)) {
+               wpa_printf(MSG_DEBUG,
+                          "RRM: No network support for Neighbor Report.");
+               return -EOPNOTSUPP;
+       }
+
+       if (!cb) {
+               wpa_printf(MSG_DEBUG,
+                          "RRM: Neighbor Report request must provide a callback.");
+               return -EINVAL;
+       }
+
+       /* Refuse if there's a live request */
+       if (wpa_s->rrm.notify_neighbor_rep) {
+               wpa_printf(MSG_DEBUG,
+                          "RRM: Currently handling previous Neighbor Report.");
+               return -EBUSY;
+       }
+
+       /* 3 = action category + action code + dialog token */
+       buf = wpabuf_alloc(3 + (ssid ? 2 + ssid->ssid_len : 0) +
+                          (lci ? 2 + MEASURE_REQUEST_LCI_LEN : 0) +
+                          (civic ? 2 + MEASURE_REQUEST_CIVIC_LEN : 0));
+       if (buf == NULL) {
+               wpa_printf(MSG_DEBUG,
+                          "RRM: Failed to allocate Neighbor Report Request");
+               return -ENOMEM;
+       }
+
+       wpa_printf(MSG_DEBUG, "RRM: Neighbor report request (for %s), token=%d",
+                  (ssid ? wpa_ssid_txt(ssid->ssid, ssid->ssid_len) : ""),
+                  wpa_s->rrm.next_neighbor_rep_token);
+
+       wpabuf_put_u8(buf, WLAN_ACTION_RADIO_MEASUREMENT);
+       wpabuf_put_u8(buf, WLAN_RRM_NEIGHBOR_REPORT_REQUEST);
+       wpabuf_put_u8(buf, wpa_s->rrm.next_neighbor_rep_token);
+       if (ssid) {
+               wpabuf_put_u8(buf, WLAN_EID_SSID);
+               wpabuf_put_u8(buf, ssid->ssid_len);
+               wpabuf_put_data(buf, ssid->ssid, ssid->ssid_len);
+       }
+
+       if (lci) {
+               /* IEEE P802.11-REVmc/D5.0 9.4.2.21 */
+               wpabuf_put_u8(buf, WLAN_EID_MEASURE_REQUEST);
+               wpabuf_put_u8(buf, MEASURE_REQUEST_LCI_LEN);
+
+               /*
+                * Measurement token; nonzero number that is unique among the
+                * Measurement Request elements in a particular frame.
+                */
+               wpabuf_put_u8(buf, 1); /* Measurement Token */
+
+               /*
+                * Parallel, Enable, Request, and Report bits are 0, Duration is
+                * reserved.
+                */
+               wpabuf_put_u8(buf, 0); /* Measurement Request Mode */
+               wpabuf_put_u8(buf, MEASURE_TYPE_LCI); /* Measurement Type */
+
+               /* IEEE P802.11-REVmc/D5.0 9.4.2.21.10 - LCI request */
+               /* Location Subject */
+               wpabuf_put_u8(buf, LOCATION_SUBJECT_REMOTE);
+
+               /* Optional Subelements */
+               /*
+                * IEEE P802.11-REVmc/D5.0 Figure 9-170
+                * The Maximum Age subelement is required, otherwise the AP can
+                * send only data that was determined after receiving the
+                * request. Setting it here to unlimited age.
+                */
+               wpabuf_put_u8(buf, LCI_REQ_SUBELEM_MAX_AGE);
+               wpabuf_put_u8(buf, 2);
+               wpabuf_put_le16(buf, 0xffff);
+       }
+
+       if (civic) {
+               /* IEEE P802.11-REVmc/D5.0 9.4.2.21 */
+               wpabuf_put_u8(buf, WLAN_EID_MEASURE_REQUEST);
+               wpabuf_put_u8(buf, MEASURE_REQUEST_CIVIC_LEN);
+
+               /*
+                * Measurement token; nonzero number that is unique among the
+                * Measurement Request elements in a particular frame.
+                */
+               wpabuf_put_u8(buf, 2); /* Measurement Token */
+
+               /*
+                * Parallel, Enable, Request, and Report bits are 0, Duration is
+                * reserved.
+                */
+               wpabuf_put_u8(buf, 0); /* Measurement Request Mode */
+               /* Measurement Type */
+               wpabuf_put_u8(buf, MEASURE_TYPE_LOCATION_CIVIC);
+
+               /* IEEE P802.11-REVmc/D5.0 9.4.2.21.14:
+                * Location Civic request */
+               /* Location Subject */
+               wpabuf_put_u8(buf, LOCATION_SUBJECT_REMOTE);
+               wpabuf_put_u8(buf, 0); /* Civic Location Type: IETF RFC 4776 */
+               /* Location Service Interval Units: Seconds */
+               wpabuf_put_u8(buf, 0);
+               /* Location Service Interval: 0 - Only one report is requested
+                */
+               wpabuf_put_le16(buf, 0);
+               /* No optional subelements */
+       }
+
+       wpa_s->rrm.next_neighbor_rep_token++;
+
+       if (wpa_drv_send_action(wpa_s, wpa_s->assoc_freq, 0, wpa_s->bssid,
+                               wpa_s->own_addr, wpa_s->bssid,
+                               wpabuf_head(buf), wpabuf_len(buf), 0) < 0) {
+               wpa_printf(MSG_DEBUG,
+                          "RRM: Failed to send Neighbor Report Request");
+               wpabuf_free(buf);
+               return -ECANCELED;
+       }
+
+       wpa_s->rrm.neighbor_rep_cb_ctx = cb_ctx;
+       wpa_s->rrm.notify_neighbor_rep = cb;
+       eloop_register_timeout(RRM_NEIGHBOR_REPORT_TIMEOUT, 0,
+                              wpas_rrm_neighbor_rep_timeout_handler,
+                              &wpa_s->rrm, NULL);
+
+       wpabuf_free(buf);
+       return 0;
+}
+
+
+static struct wpabuf * wpas_rrm_build_lci_report(struct wpa_supplicant *wpa_s,
+                                                const u8 *request, size_t len,
+                                                struct wpabuf *report)
+{
+       u8 token, type, subject;
+       u16 max_age = 0;
+       struct os_reltime t, diff;
+       unsigned long diff_l;
+       u8 *ptoken;
+       const u8 *subelem;
+
+       if (!wpa_s->lci || len < 3 + 4)
+               return report;
+
+       token = *request++;
+       /* Measurement request mode isn't used */
+       request++;
+       type = *request++;
+       subject = *request++;
+
+       wpa_printf(MSG_DEBUG,
+                  "Measurement request token %u type %u location subject %u",
+                  token, type, subject);
+
+       if (type != MEASURE_TYPE_LCI || subject != LOCATION_SUBJECT_REMOTE) {
+               wpa_printf(MSG_INFO,
+                          "Not building LCI report - bad type or location subject");
+               return report;
+       }
+
+       /* Subelements are formatted exactly like elements */
+       subelem = get_ie(request, len, LCI_REQ_SUBELEM_MAX_AGE);
+       if (subelem && subelem[1] == 2)
+               max_age = WPA_GET_LE16(subelem + 2);
+
+       if (os_get_reltime(&t))
+               return report;
+
+       os_reltime_sub(&t, &wpa_s->lci_time, &diff);
+       /* LCI age is calculated in 10th of a second units. */
+       diff_l = diff.sec * 10 + diff.usec / 100000;
+
+       if (max_age != 0xffff && max_age < diff_l)
+               return report;
+
+       if (wpabuf_resize(&report, 2 + wpabuf_len(wpa_s->lci)))
+               return report;
+
+       wpabuf_put_u8(report, WLAN_EID_MEASURE_REPORT);
+       wpabuf_put_u8(report, wpabuf_len(wpa_s->lci));
+       /* We'll override user's measurement token */
+       ptoken = wpabuf_put(report, 0);
+       wpabuf_put_buf(report, wpa_s->lci);
+       *ptoken = token;
+
+       return report;
+}
+
+
+void wpas_rrm_handle_radio_measurement_request(struct wpa_supplicant *wpa_s,
+                                              const u8 *src,
+                                              const u8 *frame, size_t len)
+{
+       struct wpabuf *buf, *report;
+       u8 token;
+       const u8 *ie, *end;
+
+       if (wpa_s->wpa_state != WPA_COMPLETED) {
+               wpa_printf(MSG_INFO,
+                          "RRM: Ignoring radio measurement request: Not associated");
+               return;
+       }
+
+       if (!wpa_s->rrm.rrm_used) {
+               wpa_printf(MSG_INFO,
+                          "RRM: Ignoring radio measurement request: Not RRM network");
+               return;
+       }
+
+       if (len < 3) {
+               wpa_printf(MSG_INFO,
+                          "RRM: Ignoring too short radio measurement request");
+               return;
+       }
+
+       end = frame + len;
+
+       token = *frame++;
+
+       /* Ignore number of repetitions because it's not used in LCI request */
+       frame += 2;
+
+       report = NULL;
+       while ((ie = get_ie(frame, end - frame, WLAN_EID_MEASURE_REQUEST)) &&
+              ie[1] >= 3) {
+               u8 msmt_type;
+
+               msmt_type = ie[4];
+               wpa_printf(MSG_DEBUG, "RRM request %d", msmt_type);
+
+               switch (msmt_type) {
+               case MEASURE_TYPE_LCI:
+                       report = wpas_rrm_build_lci_report(wpa_s, ie + 2, ie[1],
+                                                          report);
+                       break;
+               default:
+                       wpa_printf(MSG_INFO,
+                                  "RRM: Unsupported radio measurement request %d",
+                                  msmt_type);
+                       break;
+               }
+
+               frame = ie + ie[1] + 2;
+       }
+
+       if (!report)
+               return;
+
+       buf = wpabuf_alloc(3 + wpabuf_len(report));
+       if (!buf) {
+               wpabuf_free(report);
+               return;
+       }
+
+       wpabuf_put_u8(buf, WLAN_ACTION_RADIO_MEASUREMENT);
+       wpabuf_put_u8(buf, WLAN_RRM_RADIO_MEASUREMENT_REPORT);
+       wpabuf_put_u8(buf, token);
+
+       wpabuf_put_buf(buf, report);
+       wpabuf_free(report);
+
+       if (wpa_drv_send_action(wpa_s, wpa_s->assoc_freq, 0, src,
+                               wpa_s->own_addr, wpa_s->bssid,
+                               wpabuf_head(buf), wpabuf_len(buf), 0)) {
+               wpa_printf(MSG_ERROR,
+                          "RRM: Radio measurement report failed: Sending Action frame failed");
+       }
+       wpabuf_free(buf);
+}
+
+
+void wpas_rrm_handle_link_measurement_request(struct wpa_supplicant *wpa_s,
+                                             const u8 *src,
+                                             const u8 *frame, size_t len,
+                                             int rssi)
+{
+       struct wpabuf *buf;
+       const struct rrm_link_measurement_request *req;
+       struct rrm_link_measurement_report report;
+
+       if (wpa_s->wpa_state != WPA_COMPLETED) {
+               wpa_printf(MSG_INFO,
+                          "RRM: Ignoring link measurement request. Not associated");
+               return;
+       }
+
+       if (!wpa_s->rrm.rrm_used) {
+               wpa_printf(MSG_INFO,
+                          "RRM: Ignoring link measurement request. Not RRM network");
+               return;
+       }
+
+       if (!(wpa_s->drv_rrm_flags & WPA_DRIVER_FLAGS_TX_POWER_INSERTION)) {
+               wpa_printf(MSG_INFO,
+                          "RRM: Measurement report failed. TX power insertion not supported");
+               return;
+       }
+
+       req = (const struct rrm_link_measurement_request *) frame;
+       if (len < sizeof(*req)) {
+               wpa_printf(MSG_INFO,
+                          "RRM: Link measurement report failed. Request too short");
+               return;
+       }
+
+       os_memset(&report, 0, sizeof(report));
+       report.tpc.eid = WLAN_EID_TPC_REPORT;
+       report.tpc.len = 2;
+       report.rsni = 255; /* 255 indicates that RSNI is not available */
+       report.dialog_token = req->dialog_token;
+
+       /*
+        * It's possible to estimate RCPI based on RSSI in dBm. This
+        * calculation will not reflect the correct value for high rates,
+        * but it's good enough for Action frames which are transmitted
+        * with up to 24 Mbps rates.
+        */
+       if (!rssi)
+               report.rcpi = 255; /* not available */
+       else if (rssi < -110)
+               report.rcpi = 0;
+       else if (rssi > 0)
+               report.rcpi = 220;
+       else
+               report.rcpi = (rssi + 110) * 2;
+
+       /* action_category + action_code */
+       buf = wpabuf_alloc(2 + sizeof(report));
+       if (buf == NULL) {
+               wpa_printf(MSG_ERROR,
+                          "RRM: Link measurement report failed. Buffer allocation failed");
+               return;
+       }
+
+       wpabuf_put_u8(buf, WLAN_ACTION_RADIO_MEASUREMENT);
+       wpabuf_put_u8(buf, WLAN_RRM_LINK_MEASUREMENT_REPORT);
+       wpabuf_put_data(buf, &report, sizeof(report));
+       wpa_hexdump(MSG_DEBUG, "RRM: Link measurement report:",
+                   wpabuf_head(buf), wpabuf_len(buf));
+
+       if (wpa_drv_send_action(wpa_s, wpa_s->assoc_freq, 0, src,
+                               wpa_s->own_addr, wpa_s->bssid,
+                               wpabuf_head(buf), wpabuf_len(buf), 0)) {
+               wpa_printf(MSG_ERROR,
+                          "RRM: Link measurement report failed. Send action failed");
+       }
+       wpabuf_free(buf);
+}
+
+
+struct wpa_supplicant *
+wpas_vendor_elem(struct wpa_supplicant *wpa_s, enum wpa_vendor_elem_frame frame)
+{
+       switch (frame) {
+#ifdef CONFIG_P2P
+       case VENDOR_ELEM_PROBE_REQ_P2P:
+       case VENDOR_ELEM_PROBE_RESP_P2P:
+       case VENDOR_ELEM_PROBE_RESP_P2P_GO:
+       case VENDOR_ELEM_BEACON_P2P_GO:
+       case VENDOR_ELEM_P2P_PD_REQ:
+       case VENDOR_ELEM_P2P_PD_RESP:
+       case VENDOR_ELEM_P2P_GO_NEG_REQ:
+       case VENDOR_ELEM_P2P_GO_NEG_RESP:
+       case VENDOR_ELEM_P2P_GO_NEG_CONF:
+       case VENDOR_ELEM_P2P_INV_REQ:
+       case VENDOR_ELEM_P2P_INV_RESP:
+       case VENDOR_ELEM_P2P_ASSOC_REQ:
+       case VENDOR_ELEM_P2P_ASSOC_RESP:
+               return wpa_s->p2pdev;
+#endif /* CONFIG_P2P */
+       default:
+               return wpa_s;
+       }
+}
+
+
+void wpas_vendor_elem_update(struct wpa_supplicant *wpa_s)
+{
+       unsigned int i;
+       char buf[30];
+
+       wpa_printf(MSG_DEBUG, "Update vendor elements");
+
+       for (i = 0; i < NUM_VENDOR_ELEM_FRAMES; i++) {
+               if (wpa_s->vendor_elem[i]) {
+                       int res;
+
+                       res = os_snprintf(buf, sizeof(buf), "frame[%u]", i);
+                       if (!os_snprintf_error(sizeof(buf), res)) {
+                               wpa_hexdump_buf(MSG_DEBUG, buf,
+                                               wpa_s->vendor_elem[i]);
+                       }
+               }
+       }
+
+#ifdef CONFIG_P2P
+       if (wpa_s->parent == wpa_s &&
+           wpa_s->global->p2p &&
+           !wpa_s->global->p2p_disabled)
+               p2p_set_vendor_elems(wpa_s->global->p2p, wpa_s->vendor_elem);
+#endif /* CONFIG_P2P */
+}
+
+
+int wpas_vendor_elem_remove(struct wpa_supplicant *wpa_s, int frame,
+                           const u8 *elem, size_t len)
+{
+       u8 *ie, *end;
+
+       ie = wpabuf_mhead_u8(wpa_s->vendor_elem[frame]);
+       end = ie + wpabuf_len(wpa_s->vendor_elem[frame]);
+
+       for (; ie + 1 < end; ie += 2 + ie[1]) {
+               if (ie + len > end)
+                       break;
+               if (os_memcmp(ie, elem, len) != 0)
+                       continue;
+
+               if (wpabuf_len(wpa_s->vendor_elem[frame]) == len) {
+                       wpabuf_free(wpa_s->vendor_elem[frame]);
+                       wpa_s->vendor_elem[frame] = NULL;
+               } else {
+                       os_memmove(ie, ie + len, end - (ie + len));
+                       wpa_s->vendor_elem[frame]->used -= len;
+               }
+               wpas_vendor_elem_update(wpa_s);
+               return 0;
+       }
+
+       return -1;
+}
+
+
+struct hostapd_hw_modes * get_mode(struct hostapd_hw_modes *modes,
+                                  u16 num_modes, enum hostapd_hw_mode mode)
+{
+       u16 i;
+
+       for (i = 0; i < num_modes; i++) {
+               if (modes[i].mode == mode)
+                       return &modes[i];
+       }
+
+       return NULL;
+}
+
+
+static struct
+wpa_bss_tmp_disallowed * wpas_get_disallowed_bss(struct wpa_supplicant *wpa_s,
+                                                const u8 *bssid)
+{
+       struct wpa_bss_tmp_disallowed *bss;
+
+       dl_list_for_each(bss, &wpa_s->bss_tmp_disallowed,
+                        struct wpa_bss_tmp_disallowed, list) {
+               if (os_memcmp(bssid, bss->bssid, ETH_ALEN) == 0)
+                       return bss;
+       }
+
+       return NULL;
+}
+
+
+void wpa_bss_tmp_disallow(struct wpa_supplicant *wpa_s, const u8 *bssid,
+                         unsigned int sec)
+{
+       struct wpa_bss_tmp_disallowed *bss;
+       struct os_reltime until;
+
+       os_get_reltime(&until);
+       until.sec += sec;
+
+       bss = wpas_get_disallowed_bss(wpa_s, bssid);
+       if (bss) {
+               bss->disallowed_until = until;
+               return;
+       }
+
+       bss = os_malloc(sizeof(*bss));
+       if (!bss) {
+               wpa_printf(MSG_DEBUG,
+                          "Failed to allocate memory for temp disallow BSS");
+               return;
+       }
+
+       bss->disallowed_until = until;
+       os_memcpy(bss->bssid, bssid, ETH_ALEN);
+       dl_list_add(&wpa_s->bss_tmp_disallowed, &bss->list);
+}
+
+
+int wpa_is_bss_tmp_disallowed(struct wpa_supplicant *wpa_s, const u8 *bssid)
+{
+       struct wpa_bss_tmp_disallowed *bss = NULL, *tmp, *prev;
+       struct os_reltime now, age;
+
+       os_get_reltime(&now);
+
+       dl_list_for_each_safe(tmp, prev, &wpa_s->bss_tmp_disallowed,
+                        struct wpa_bss_tmp_disallowed, list) {
+               if (!os_reltime_before(&now, &tmp->disallowed_until)) {
+                       /* This BSS is not disallowed anymore */
+                       dl_list_del(&tmp->list);
+                       os_free(tmp);
+                       continue;
+               }
+               if (os_memcmp(bssid, tmp->bssid, ETH_ALEN) == 0) {
+                       bss = tmp;
+                       break;
+               }
+       }
+       if (!bss)
+               return 0;
+
+       os_reltime_sub(&bss->disallowed_until, &now, &age);
+       wpa_printf(MSG_DEBUG,
+                  "BSS " MACSTR " disabled for %ld.%0ld seconds",
+                  MAC2STR(bss->bssid), age.sec, age.usec);
+       return 1;
+}