Updated to hostap_2_6
[mech_eap.git] / libeap / wpa_supplicant / events.c
index 3af1c7d..abe3b47 100644 (file)
@@ -72,6 +72,7 @@ static int wpas_temp_disabled(struct wpa_supplicant *wpa_s,
 }
 
 
+#ifndef CONFIG_NO_SCAN_PROCESSING
 /**
  * wpas_reenabled_network_time - Time until first network is re-enabled
  * @wpa_s: Pointer to wpa_supplicant data
@@ -107,6 +108,7 @@ static int wpas_reenabled_network_time(struct wpa_supplicant *wpa_s)
 
        return res;
 }
+#endif /* CONFIG_NO_SCAN_PROCESSING */
 
 
 void wpas_network_reenabled(void *eloop_ctx, void *timeout_ctx)
@@ -279,6 +281,11 @@ void wpa_supplicant_mark_disassoc(struct wpa_supplicant *wpa_s)
        wpa_supplicant_ap_deinit(wpa_s);
 #endif /* CONFIG_AP */
 
+#ifdef CONFIG_HS20
+       /* Clear possibly configured frame filters */
+       wpa_drv_configure_frame_filters(wpa_s, 0);
+#endif /* CONFIG_HS20 */
+
        if (wpa_s->wpa_state == WPA_INTERFACE_DISABLED)
                return;
 
@@ -303,6 +310,7 @@ void wpa_supplicant_mark_disassoc(struct wpa_supplicant *wpa_s)
        wpa_s->key_mgmt = 0;
 
        wpas_rrm_reset(wpa_s);
+       wpa_s->wnmsleep_used = 0;
 }
 
 
@@ -564,11 +572,36 @@ static int wpa_supplicant_ssid_bss_match(struct wpa_supplicant *wpa_s,
                        break;
                }
 #endif /* CONFIG_IEEE80211W */
+               if ((ie.capabilities & WPA_CAPABILITY_MFPR) &&
+                   wpas_get_ssid_pmf(wpa_s, ssid) ==
+                   NO_MGMT_FRAME_PROTECTION) {
+                       wpa_dbg(wpa_s, MSG_DEBUG,
+                               "   skip RSN IE - no mgmt frame protection enabled but AP requires it");
+                       break;
+               }
+#ifdef CONFIG_MBO
+               if (!(ie.capabilities & WPA_CAPABILITY_MFPC) &&
+                   wpas_mbo_get_bss_attr(bss, MBO_ATTR_ID_AP_CAPA_IND) &&
+                   wpas_get_ssid_pmf(wpa_s, ssid) !=
+                   NO_MGMT_FRAME_PROTECTION) {
+                       wpa_dbg(wpa_s, MSG_DEBUG,
+                               "   skip RSN IE - no mgmt frame protection enabled on MBO AP");
+                       break;
+               }
+#endif /* CONFIG_MBO */
 
                wpa_dbg(wpa_s, MSG_DEBUG, "   selected based on RSN IE");
                return 1;
        }
 
+#ifdef CONFIG_IEEE80211W
+       if (wpas_get_ssid_pmf(wpa_s, ssid) == MGMT_FRAME_PROTECTION_REQUIRED) {
+               wpa_dbg(wpa_s, MSG_DEBUG,
+                       "   skip - MFP Required but network not MFP Capable");
+               return 0;
+       }
+#endif /* CONFIG_IEEE80211W */
+
        wpa_ie = wpa_bss_get_vendor_ie(bss, WPA_IE_VENDOR_TYPE);
        while ((ssid->proto & WPA_PROTO_WPA) && wpa_ie) {
                proto_match++;
@@ -806,10 +839,10 @@ static int addr_in_list(const u8 *addr, const u8 *list, size_t num)
 }
 
 
-static struct wpa_ssid * wpa_scan_res_match(struct wpa_supplicant *wpa_s,
-                                           int i, struct wpa_bss *bss,
-                                           struct wpa_ssid *group,
-                                           int only_first_ssid)
+struct wpa_ssid * wpa_scan_res_match(struct wpa_supplicant *wpa_s,
+                                    int i, struct wpa_bss *bss,
+                                    struct wpa_ssid *group,
+                                    int only_first_ssid)
 {
        u8 wpa_ie_len, rsn_ie_len;
        int wpa;
@@ -817,6 +850,9 @@ static struct wpa_ssid * wpa_scan_res_match(struct wpa_supplicant *wpa_s,
        const u8 *ie;
        struct wpa_ssid *ssid;
        int osen;
+#ifdef CONFIG_MBO
+       const u8 *assoc_disallow;
+#endif /* CONFIG_MBO */
 
        ie = wpa_bss_get_vendor_ie(bss, WPA_IE_VENDOR_TYPE);
        wpa_ie_len = ie ? ie[1] : 0;
@@ -978,8 +1014,16 @@ static struct wpa_ssid * wpa_scan_res_match(struct wpa_supplicant *wpa_s,
                        continue;
                }
 
-               if (!bss_is_ess(bss)) {
-                       wpa_dbg(wpa_s, MSG_DEBUG, "   skip - not ESS network");
+               if (ssid->mode != IEEE80211_MODE_MESH && !bss_is_ess(bss) &&
+                   !bss_is_pbss(bss)) {
+                       wpa_dbg(wpa_s, MSG_DEBUG,
+                               "   skip - not ESS, PBSS, or MBSS");
+                       continue;
+               }
+
+               if (ssid->pbss != 2 && ssid->pbss != bss_is_pbss(bss)) {
+                       wpa_dbg(wpa_s, MSG_DEBUG, "   skip - PBSS mismatch (ssid %d bss %d)",
+                               ssid->pbss, bss_is_pbss(bss));
                        continue;
                }
 
@@ -989,6 +1033,14 @@ static struct wpa_ssid * wpa_scan_res_match(struct wpa_supplicant *wpa_s,
                        continue;
                }
 
+#ifdef CONFIG_MESH
+               if (ssid->mode == IEEE80211_MODE_MESH && ssid->frequency > 0 &&
+                   ssid->frequency != bss->freq) {
+                       wpa_dbg(wpa_s, MSG_DEBUG, "   skip - frequency not allowed (mesh)");
+                       continue;
+               }
+#endif /* CONFIG_MESH */
+
                if (!rate_match(wpa_s, bss)) {
                        wpa_dbg(wpa_s, MSG_DEBUG, "   skip - rate sets do "
                                "not match");
@@ -1048,6 +1100,29 @@ static struct wpa_ssid * wpa_scan_res_match(struct wpa_supplicant *wpa_s,
                                (unsigned int) diff.usec);
                        continue;
                }
+#ifdef CONFIG_MBO
+#ifdef CONFIG_TESTING_OPTIONS
+               if (wpa_s->ignore_assoc_disallow)
+                       goto skip_assoc_disallow;
+#endif /* CONFIG_TESTING_OPTIONS */
+               assoc_disallow = wpas_mbo_get_bss_attr(
+                       bss, MBO_ATTR_ID_ASSOC_DISALLOW);
+               if (assoc_disallow && assoc_disallow[1] >= 1) {
+                       wpa_dbg(wpa_s, MSG_DEBUG,
+                               "   skip - MBO association disallowed (reason %u)",
+                               assoc_disallow[2]);
+                       continue;
+               }
+
+               if (wpa_is_bss_tmp_disallowed(wpa_s, bss->bssid)) {
+                       wpa_dbg(wpa_s, MSG_DEBUG,
+                               "   skip - MBO retry delay has not passed yet");
+                       continue;
+               }
+#ifdef CONFIG_TESTING_OPTIONS
+       skip_assoc_disallow:
+#endif /* CONFIG_TESTING_OPTIONS */
+#endif /* CONFIG_MBO */
 
                /* Matching configuration found */
                return ssid;
@@ -1301,6 +1376,7 @@ static int wpa_supplicant_need_to_roam(struct wpa_supplicant *wpa_s,
        struct wpa_bss *current_bss = NULL;
 #ifndef CONFIG_NO_ROAMING
        int min_diff;
+       int to_5ghz;
 #endif /* CONFIG_NO_ROAMING */
 
        if (wpa_s->reassociate)
@@ -1356,7 +1432,10 @@ static int wpa_supplicant_need_to_roam(struct wpa_supplicant *wpa_s,
                return 1;
        }
 
-       if (current_bss->level < 0 && current_bss->level > selected->level) {
+       to_5ghz = selected->freq > 4000 && current_bss->freq < 4000;
+
+       if (current_bss->level < 0 &&
+           current_bss->level > selected->level + to_5ghz * 2) {
                wpa_dbg(wpa_s, MSG_DEBUG, "Skip roam - Current BSS has better "
                        "signal level");
                return 0;
@@ -1375,6 +1454,13 @@ static int wpa_supplicant_need_to_roam(struct wpa_supplicant *wpa_s,
                else
                        min_diff = 5;
        }
+       if (to_5ghz) {
+               /* Make it easier to move to 5 GHz band */
+               if (min_diff > 2)
+                       min_diff -= 2;
+               else
+                       min_diff = 0;
+       }
        if (abs(current_bss->level - selected->level) < min_diff) {
                wpa_dbg(wpa_s, MSG_DEBUG, "Skip roam - too small difference "
                        "in signal level");
@@ -1417,6 +1503,8 @@ static int _wpa_supplicant_event_scan_results(struct wpa_supplicant *wpa_s,
                        return -1;
                if (!own_request)
                        return -1;
+               if (data && data->scan_info.external_scan)
+                       return -1;
                wpa_dbg(wpa_s, MSG_DEBUG, "Failed to get scan results - try "
                        "scanning again");
                wpa_supplicant_req_new_scan(wpa_s, 1, 0);
@@ -1441,7 +1529,7 @@ static int _wpa_supplicant_event_scan_results(struct wpa_supplicant *wpa_s,
 #endif /* CONFIG_NO_RANDOM_POOL */
 
        if (own_request && wpa_s->scan_res_handler &&
-           (wpa_s->own_scan_running || !wpa_s->radio->external_scan_running)) {
+           !(data && data->scan_info.external_scan)) {
                void (*scan_res_handler)(struct wpa_supplicant *wpa_s,
                                         struct wpa_scan_results *scan_res);
 
@@ -1462,9 +1550,11 @@ static int _wpa_supplicant_event_scan_results(struct wpa_supplicant *wpa_s,
        }
 
        wpa_dbg(wpa_s, MSG_DEBUG, "New scan results available (own=%u ext=%u)",
-               wpa_s->own_scan_running, wpa_s->radio->external_scan_running);
+               wpa_s->own_scan_running,
+               data ? data->scan_info.external_scan : 0);
        if (wpa_s->last_scan_req == MANUAL_SCAN_REQ &&
-           wpa_s->manual_scan_use_id && wpa_s->own_scan_running) {
+           wpa_s->manual_scan_use_id && wpa_s->own_scan_running &&
+           own_request && !(data && data->scan_info.external_scan)) {
                wpa_msg_ctrl(wpa_s, MSG_INFO, WPA_EVENT_SCAN_RESULTS "id=%u",
                             wpa_s->manual_scan_id);
                wpa_s->manual_scan_use_id = 0;
@@ -1475,7 +1565,7 @@ static int _wpa_supplicant_event_scan_results(struct wpa_supplicant *wpa_s,
 
        wpas_notify_scan_done(wpa_s, 1);
 
-       if (!wpa_s->own_scan_running && wpa_s->radio->external_scan_running) {
+       if (data && data->scan_info.external_scan) {
                wpa_dbg(wpa_s, MSG_DEBUG, "Do not use results from externally requested scan operation for network selection");
                wpa_scan_results_free(scan_res);
                return 0;
@@ -1504,9 +1594,13 @@ static int _wpa_supplicant_event_scan_results(struct wpa_supplicant *wpa_s,
 
        wpas_wps_update_ap_info(wpa_s, scan_res);
 
+       if (wpa_s->wpa_state >= WPA_AUTHENTICATING &&
+           wpa_s->wpa_state < WPA_COMPLETED)
+               goto scan_work_done;
+
        wpa_scan_results_free(scan_res);
 
-       if (wpa_s->scan_work) {
+       if (own_request && wpa_s->scan_work) {
                struct wpa_radio_work *work = wpa_s->scan_work;
                wpa_s->scan_work = NULL;
                radio_work_done(work);
@@ -1516,7 +1610,7 @@ static int _wpa_supplicant_event_scan_results(struct wpa_supplicant *wpa_s,
 
 scan_work_done:
        wpa_scan_results_free(scan_res);
-       if (wpa_s->scan_work) {
+       if (own_request && wpa_s->scan_work) {
                struct wpa_radio_work *work = wpa_s->scan_work;
                wpa_s->scan_work = NULL;
                radio_work_done(work);
@@ -1547,6 +1641,14 @@ static int wpas_select_network_from_last_scan(struct wpa_supplicant *wpa_s,
 
        selected = wpa_supplicant_pick_network(wpa_s, &ssid);
 
+#ifdef CONFIG_MESH
+       if (wpa_s->ifmsh) {
+               wpa_msg(wpa_s, MSG_INFO,
+                       "Avoiding join because we already joined a mesh group");
+               return 0;
+       }
+#endif /* CONFIG_MESH */
+
        if (selected) {
                int skip;
                skip = !wpa_supplicant_need_to_roam(wpa_s, selected, ssid);
@@ -1556,6 +1658,13 @@ static int wpas_select_network_from_last_scan(struct wpa_supplicant *wpa_s,
                        return 0;
                }
 
+               if (ssid != wpa_s->current_ssid &&
+                   wpa_s->wpa_state >= WPA_AUTHENTICATING) {
+                       wpa_s->own_disconnect_req = 1;
+                       wpa_supplicant_deauthenticate(
+                               wpa_s, WLAN_REASON_DEAUTH_LEAVING);
+               }
+
                if (wpa_supplicant_connect(wpa_s, selected, ssid) < 0) {
                        wpa_dbg(wpa_s, MSG_DEBUG, "Connect failed");
                        return -1;
@@ -1568,13 +1677,6 @@ static int wpas_select_network_from_last_scan(struct wpa_supplicant *wpa_s,
                 */
                return 1;
        } else {
-#ifdef CONFIG_MESH
-               if (wpa_s->ifmsh) {
-                       wpa_msg(wpa_s, MSG_INFO,
-                               "Avoiding join because we already joined a mesh group");
-                       return 0;
-               }
-#endif /* CONFIG_MESH */
                wpa_dbg(wpa_s, MSG_DEBUG, "No suitable network found");
                ssid = wpa_supplicant_pick_new_network(wpa_s);
                if (ssid) {
@@ -1830,6 +1932,50 @@ static void interworking_process_assoc_resp(struct wpa_supplicant *wpa_s,
 #endif /* CONFIG_INTERWORKING */
 
 
+#ifdef CONFIG_FST
+static int wpas_fst_update_mbie(struct wpa_supplicant *wpa_s,
+                               const u8 *ie, size_t ie_len)
+{
+       struct mb_ies_info mb_ies;
+
+       if (!ie || !ie_len || !wpa_s->fst)
+           return -ENOENT;
+
+       os_memset(&mb_ies, 0, sizeof(mb_ies));
+
+       while (ie_len >= 2 && mb_ies.nof_ies < MAX_NOF_MB_IES_SUPPORTED) {
+               size_t len;
+
+               len = 2 + ie[1];
+               if (len > ie_len) {
+                       wpa_hexdump(MSG_DEBUG, "FST: Truncated IE found",
+                                   ie, ie_len);
+                       break;
+               }
+
+               if (ie[0] == WLAN_EID_MULTI_BAND) {
+                       wpa_printf(MSG_DEBUG, "MB IE of %u bytes found",
+                                  (unsigned int) len);
+                       mb_ies.ies[mb_ies.nof_ies].ie = ie + 2;
+                       mb_ies.ies[mb_ies.nof_ies].ie_len = len - 2;
+                       mb_ies.nof_ies++;
+               }
+
+               ie_len -= len;
+               ie += len;
+       }
+
+       if (mb_ies.nof_ies > 0) {
+               wpabuf_free(wpa_s->received_mb_ies);
+               wpa_s->received_mb_ies = mb_ies_by_info(&mb_ies);
+               return 0;
+       }
+
+       return -ENOENT;
+}
+#endif /* CONFIG_FST */
+
+
 static int wpa_supplicant_event_associnfo(struct wpa_supplicant *wpa_s,
                                          union wpa_event_data *data)
 {
@@ -1880,6 +2026,8 @@ static int wpa_supplicant_event_associnfo(struct wpa_supplicant *wpa_s,
                }
                if ((p[0] == WLAN_EID_VENDOR_SPECIFIC && p[1] >= 6 &&
                     (os_memcmp(&p[2], "\x00\x50\xF2\x01\x01\x00", 6) == 0)) ||
+                   (p[0] == WLAN_EID_VENDOR_SPECIFIC && p[1] >= 4 &&
+                    (os_memcmp(&p[2], "\x50\x6F\x9A\x12", 4) == 0)) ||
                    (p[0] == WLAN_EID_RSN && p[1] >= 2)) {
                        if (wpa_sm_set_assoc_wpa_ie(wpa_s->wpa, p, len))
                                break;
@@ -2012,19 +2160,6 @@ static int wpa_supplicant_event_associnfo(struct wpa_supplicant *wpa_s,
        if (wpa_found || rsn_found)
                wpa_s->ap_ies_from_associnfo = 1;
 
-#ifdef CONFIG_FST
-       wpabuf_free(wpa_s->received_mb_ies);
-       wpa_s->received_mb_ies = NULL;
-       if (wpa_s->fst) {
-               struct mb_ies_info mb_ies;
-
-               wpa_printf(MSG_DEBUG, "Looking for MB IE");
-               if (!mb_ies_info_by_ies(&mb_ies, data->assoc_info.resp_ies,
-                                       data->assoc_info.resp_ies_len))
-                       wpa_s->received_mb_ies = mb_ies_by_info(&mb_ies);
-       }
-#endif /* CONFIG_FST */
-
        if (wpa_s->assoc_freq && data->assoc_info.freq &&
            wpa_s->assoc_freq != data->assoc_info.freq) {
                wpa_printf(MSG_DEBUG, "Operating frequency changed from "
@@ -2063,11 +2198,50 @@ static int wpa_supplicant_assoc_update_ie(struct wpa_supplicant *wpa_s)
 }
 
 
+static void wpas_fst_update_mb_assoc(struct wpa_supplicant *wpa_s,
+                                    union wpa_event_data *data)
+{
+#ifdef CONFIG_FST
+       struct assoc_info *ai = data ? &data->assoc_info : NULL;
+       struct wpa_bss *bss = wpa_s->current_bss;
+       const u8 *ieprb, *iebcn;
+
+       wpabuf_free(wpa_s->received_mb_ies);
+       wpa_s->received_mb_ies = NULL;
+
+       if (ai &&
+           !wpas_fst_update_mbie(wpa_s, ai->resp_ies, ai->resp_ies_len)) {
+               wpa_printf(MSG_DEBUG,
+                          "FST: MB IEs updated from Association Response frame");
+               return;
+       }
+
+       if (ai &&
+           !wpas_fst_update_mbie(wpa_s, ai->beacon_ies, ai->beacon_ies_len)) {
+               wpa_printf(MSG_DEBUG,
+                          "FST: MB IEs updated from association event Beacon IEs");
+               return;
+       }
+
+       if (!bss)
+               return;
+
+       ieprb = (const u8 *) (bss + 1);
+       iebcn = ieprb + bss->ie_len;
+
+       if (!wpas_fst_update_mbie(wpa_s, ieprb, bss->ie_len))
+               wpa_printf(MSG_DEBUG, "FST: MB IEs updated from bss IE");
+       else if (!wpas_fst_update_mbie(wpa_s, iebcn, bss->beacon_ie_len))
+               wpa_printf(MSG_DEBUG, "FST: MB IEs updated from bss beacon IE");
+#endif /* CONFIG_FST */
+}
+
+
 static void wpa_supplicant_event_assoc(struct wpa_supplicant *wpa_s,
                                       union wpa_event_data *data)
 {
        u8 bssid[ETH_ALEN];
-       int ft_completed;
+       int ft_completed, already_authorized;
        int new_bss = 0;
 
 #ifdef CONFIG_AP
@@ -2123,6 +2297,8 @@ static void wpa_supplicant_event_assoc(struct wpa_supplicant *wpa_s,
                                "WPA/RSN IEs not updated");
        }
 
+       wpas_fst_update_mb_assoc(wpa_s, data);
+
 #ifdef CONFIG_SME
        os_memcpy(wpa_s->sme.prev_bssid, bssid, ETH_ALEN);
        wpa_s->sme.prev_bssid_set = 1;
@@ -2141,6 +2317,8 @@ static void wpa_supplicant_event_assoc(struct wpa_supplicant *wpa_s,
        if (wpa_s->l2)
                l2_packet_notify_auth_start(wpa_s->l2);
 
+       already_authorized = data && data->assoc_info.authorized;
+
        /*
         * Set portEnabled first to FALSE in order to get EAP state machine out
         * of the SUCCESS state and eapSuccess cleared. Without this, EAPOL PAE
@@ -2149,11 +2327,12 @@ static void wpa_supplicant_event_assoc(struct wpa_supplicant *wpa_s,
         * AUTHENTICATED without ever giving chance to EAP state machine to
         * reset the state.
         */
-       if (!ft_completed) {
+       if (!ft_completed && !already_authorized) {
                eapol_sm_notify_portEnabled(wpa_s->eapol, FALSE);
                eapol_sm_notify_portValid(wpa_s->eapol, FALSE);
        }
-       if (wpa_key_mgmt_wpa_psk(wpa_s->key_mgmt) || ft_completed)
+       if (wpa_key_mgmt_wpa_psk(wpa_s->key_mgmt) || ft_completed ||
+           already_authorized)
                eapol_sm_notify_eap_success(wpa_s->eapol, FALSE);
        /* 802.1X::portControl = Auto */
        eapol_sm_notify_portEnabled(wpa_s->eapol, TRUE);
@@ -2245,7 +2424,7 @@ static void wpa_supplicant_event_assoc(struct wpa_supplicant *wpa_s,
            wpa_s->key_mgmt != WPA_KEY_MGMT_NONE &&
            wpa_s->key_mgmt != WPA_KEY_MGMT_WPA_NONE &&
            wpa_s->ibss_rsn == NULL) {
-               wpa_s->ibss_rsn = ibss_rsn_init(wpa_s);
+               wpa_s->ibss_rsn = ibss_rsn_init(wpa_s, wpa_s->current_ssid);
                if (!wpa_s->ibss_rsn) {
                        wpa_msg(wpa_s, MSG_INFO, "Failed to init IBSS RSN");
                        wpa_supplicant_deauthenticate(
@@ -2339,6 +2518,7 @@ static void wpa_supplicant_event_disassoc_finish(struct wpa_supplicant *wpa_s,
        struct wpa_bss *fast_reconnect = NULL;
        struct wpa_ssid *fast_reconnect_ssid = NULL;
        struct wpa_ssid *last_ssid;
+       struct wpa_bss *curr = NULL;
 
        authenticating = wpa_s->wpa_state == WPA_AUTHENTICATING;
        os_memcpy(prev_pending_bssid, wpa_s->pending_bssid, ETH_ALEN);
@@ -2354,6 +2534,19 @@ static void wpa_supplicant_event_disassoc_finish(struct wpa_supplicant *wpa_s,
                return;
        }
 
+       if (!wpa_s->disconnected && wpa_s->wpa_state >= WPA_AUTHENTICATING &&
+           reason_code == WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY &&
+           locally_generated)
+               /*
+                * Remove the inactive AP (which is probably out of range) from
+                * the BSS list after marking disassociation. In particular
+                * mac80211-based drivers use the
+                * WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY reason code in
+                * locally generated disconnection events for cases where the
+                * AP does not reply anymore.
+                */
+               curr = wpa_s->current_bss;
+
        if (could_be_psk_mismatch(wpa_s, reason_code, locally_generated)) {
                wpa_msg(wpa_s, MSG_INFO, "WPA: 4-Way Handshake failed - "
                        "pre-shared key may be incorrect");
@@ -2364,7 +2557,8 @@ static void wpa_supplicant_event_disassoc_finish(struct wpa_supplicant *wpa_s,
        if (!wpa_s->disconnected &&
            (!wpa_s->auto_reconnect_disabled ||
             wpa_s->key_mgmt == WPA_KEY_MGMT_WPS ||
-            wpas_wps_searching(wpa_s))) {
+            wpas_wps_searching(wpa_s) ||
+            wpas_wps_reenable_networks_pending(wpa_s))) {
                wpa_dbg(wpa_s, MSG_DEBUG, "Auto connect enabled: try to "
                        "reconnect (wps=%d/%d wpa_state=%d)",
                        wpa_s->key_mgmt == WPA_KEY_MGMT_WPS,
@@ -2414,6 +2608,9 @@ static void wpa_supplicant_event_disassoc_finish(struct wpa_supplicant *wpa_s,
        last_ssid = wpa_s->current_ssid;
        wpa_supplicant_mark_disassoc(wpa_s);
 
+       if (curr)
+               wpa_bss_remove(wpa_s, curr, "Connection to AP lost");
+
        if (authenticating && (wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME)) {
                sme_disassoc_while_authenticating(wpa_s, prev_pending_bssid);
                wpa_s->current_ssid = last_ssid;
@@ -2424,7 +2621,8 @@ static void wpa_supplicant_event_disassoc_finish(struct wpa_supplicant *wpa_s,
            !disallowed_bssid(wpa_s, fast_reconnect->bssid) &&
            !disallowed_ssid(wpa_s, fast_reconnect->ssid,
                             fast_reconnect->ssid_len) &&
-           !wpas_temp_disabled(wpa_s, fast_reconnect_ssid)) {
+           !wpas_temp_disabled(wpa_s, fast_reconnect_ssid) &&
+           !wpa_is_bss_tmp_disallowed(wpa_s, fast_reconnect->bssid)) {
 #ifndef CONFIG_NO_SCAN_PROCESSING
                wpa_dbg(wpa_s, MSG_DEBUG, "Try to reconnect to the same BSS");
                if (wpa_supplicant_connect(wpa_s, fast_reconnect,
@@ -2622,6 +2820,13 @@ wpa_supplicant_event_interface_status(struct wpa_supplicant *wpa_s,
                }
 #endif /* CONFIG_P2P */
 
+#ifdef CONFIG_MATCH_IFACE
+               if (wpa_s->matched) {
+                       wpa_supplicant_remove_iface(wpa_s->global, wpa_s, 0);
+                       break;
+               }
+#endif /* CONFIG_MATCH_IFACE */
+
 #ifdef CONFIG_TERMINATE_ONLASTIF
                /* check if last interface */
                if (!any_interfaces(wpa_s->global->ifaces))
@@ -3007,7 +3212,16 @@ static void wpa_supplicant_update_channel_list(
 {
        struct wpa_supplicant *ifs;
 
-       wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_REGDOM_CHANGE "init=%s type=%s%s%s",
+       /*
+        * To allow backwards compatibility with higher level layers that
+        * assumed the REGDOM_CHANGE event is sent over the initially added
+        * interface. Find the highest parent of this interface and use it to
+        * send the event.
+        */
+       for (ifs = wpa_s; ifs->parent && ifs != ifs->parent; ifs = ifs->parent)
+               ;
+
+       wpa_msg(ifs, MSG_INFO, WPA_EVENT_REGDOM_CHANGE "init=%s type=%s%s%s",
                reg_init_str(info->initiator), reg_type_str(info->type),
                info->alpha2[0] ? " alpha2=" : "",
                info->alpha2[0] ? info->alpha2 : "");
@@ -3022,14 +3236,16 @@ static void wpa_supplicant_update_channel_list(
                free_hw_features(ifs);
                ifs->hw.modes = wpa_drv_get_hw_feature_data(
                        ifs, &ifs->hw.num_modes, &ifs->hw.flags);
-       }
 
-       /* Restart sched_scan with updated channel list */
-       if (wpa_s->sched_scanning) {
-               wpa_dbg(wpa_s, MSG_DEBUG,
-                       "Channel list changed restart sched scan.");
-               wpa_supplicant_cancel_sched_scan(wpa_s);
-               wpa_supplicant_req_scan(wpa_s, 0, 0);
+               /* Restart PNO/sched_scan with updated channel list */
+               if (ifs->pno) {
+                       wpas_stop_pno(ifs);
+                       wpas_start_pno(ifs);
+               } else if (ifs->sched_scanning && !ifs->pno_sched_pending) {
+                       wpa_dbg(ifs, MSG_DEBUG,
+                               "Channel list changed - restart sched_scan");
+                       wpas_scan_restart_sched_scan(ifs);
+               }
        }
 
        wpas_p2p_update_channel_list(wpa_s, WPAS_P2P_CHANNEL_UPDATE_DRIVER);
@@ -3120,6 +3336,14 @@ static void wpas_event_rx_mgmt_action(struct wpa_supplicant *wpa_s,
 #endif /* CONFIG_INTERWORKING */
 
        if (category == WLAN_ACTION_RADIO_MEASUREMENT &&
+           payload[0] == WLAN_RRM_RADIO_MEASUREMENT_REQUEST) {
+               wpas_rrm_handle_radio_measurement_request(wpa_s, mgmt->sa,
+                                                         payload + 1,
+                                                         plen - 1);
+               return;
+       }
+
+       if (category == WLAN_ACTION_RADIO_MEASUREMENT &&
            payload[0] == WLAN_RRM_NEIGHBOR_REPORT_RESPONSE) {
                wpas_rrm_process_neighbor_rep(wpa_s, payload + 1, plen - 1);
                return;
@@ -3209,6 +3433,7 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
        if (wpa_s->wpa_state == WPA_INTERFACE_DISABLED &&
            event != EVENT_INTERFACE_ENABLED &&
            event != EVENT_INTERFACE_STATUS &&
+           event != EVENT_SCAN_RESULTS &&
            event != EVENT_SCHED_SCAN_STOPPED) {
                wpa_dbg(wpa_s, MSG_DEBUG,
                        "Ignore event %s (%d) while interface is disabled",
@@ -3237,18 +3462,43 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
 
        switch (event) {
        case EVENT_AUTH:
+#ifdef CONFIG_FST
+               if (!wpas_fst_update_mbie(wpa_s, data->auth.ies,
+                                         data->auth.ies_len))
+                       wpa_printf(MSG_DEBUG,
+                                  "FST: MB IEs updated from auth IE");
+#endif /* CONFIG_FST */
                sme_event_auth(wpa_s, data);
                break;
        case EVENT_ASSOC:
+#ifdef CONFIG_TESTING_OPTIONS
+               if (wpa_s->ignore_auth_resp) {
+                       wpa_printf(MSG_INFO,
+                                  "EVENT_ASSOC - ignore_auth_resp active!");
+                       break;
+               }
+#endif /* CONFIG_TESTING_OPTIONS */
                wpa_supplicant_event_assoc(wpa_s, data);
                if (data && data->assoc_info.authorized)
                        wpa_supplicant_event_assoc_auth(wpa_s, data);
+               if (data) {
+                       wpa_msg(wpa_s, MSG_INFO,
+                               WPA_EVENT_SUBNET_STATUS_UPDATE "status=%u",
+                               data->assoc_info.subnet_status);
+               }
                break;
        case EVENT_DISASSOC:
                wpas_event_disassoc(wpa_s,
                                    data ? &data->disassoc_info : NULL);
                break;
        case EVENT_DEAUTH:
+#ifdef CONFIG_TESTING_OPTIONS
+               if (wpa_s->ignore_auth_resp) {
+                       wpa_printf(MSG_INFO,
+                                  "EVENT_DEAUTH - ignore_auth_resp active!");
+                       break;
+               }
+#endif /* CONFIG_TESTING_OPTIONS */
                wpas_event_deauth(wpa_s,
                                  data ? &data->deauth_info : NULL);
                break;
@@ -3257,10 +3507,11 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
                break;
 #ifndef CONFIG_NO_SCAN_PROCESSING
        case EVENT_SCAN_STARTED:
-               os_get_reltime(&wpa_s->scan_start_time);
-               if (wpa_s->own_scan_requested) {
+               if (wpa_s->own_scan_requested ||
+                   (data && !data->scan_info.external_scan)) {
                        struct os_reltime diff;
 
+                       os_get_reltime(&wpa_s->scan_start_time);
                        os_reltime_sub(&wpa_s->scan_start_time,
                                       &wpa_s->scan_trigger_time, &diff);
                        wpa_dbg(wpa_s, MSG_DEBUG, "Own scan request started a scan in %ld.%06ld seconds",
@@ -3283,7 +3534,16 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
                }
                break;
        case EVENT_SCAN_RESULTS:
-               if (os_reltime_initialized(&wpa_s->scan_start_time)) {
+               if (wpa_s->wpa_state == WPA_INTERFACE_DISABLED) {
+                       wpa_s->scan_res_handler = NULL;
+                       wpa_s->own_scan_running = 0;
+                       wpa_s->radio->external_scan_running = 0;
+                       wpa_s->last_scan_req = NORMAL_SCAN_REQ;
+                       break;
+               }
+
+               if (!(data && data->scan_info.external_scan) &&
+                   os_reltime_initialized(&wpa_s->scan_start_time)) {
                        struct os_reltime now, diff;
                        os_get_reltime(&now);
                        os_reltime_sub(&now, &wpa_s->scan_start_time, &diff);
@@ -3294,8 +3554,10 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
                }
                if (wpa_supplicant_event_scan_results(wpa_s, data))
                        break; /* interface may have been removed */
-               wpa_s->own_scan_running = 0;
-               wpa_s->radio->external_scan_running = 0;
+               if (!(data && data->scan_info.external_scan))
+                       wpa_s->own_scan_running = 0;
+               if (data && data->scan_info.nl_scan_event)
+                       wpa_s->radio->external_scan_running = 0;
                radio_work_check_next(wpa_s);
                break;
 #endif /* CONFIG_NO_SCAN_PROCESSING */
@@ -3336,13 +3598,17 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
        case EVENT_ASSOC_REJECT:
                if (data->assoc_reject.bssid)
                        wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_ASSOC_REJECT
-                               "bssid=" MACSTR " status_code=%u",
+                               "bssid=" MACSTR " status_code=%u%s",
                                MAC2STR(data->assoc_reject.bssid),
-                               data->assoc_reject.status_code);
+                               data->assoc_reject.status_code,
+                               data->assoc_reject.timed_out ? " timeout" : "");
                else
                        wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_ASSOC_REJECT
-                               "status_code=%u",
-                               data->assoc_reject.status_code);
+                               "status_code=%u%s",
+                               data->assoc_reject.status_code,
+                               data->assoc_reject.timed_out ? " timeout" : "");
+               wpa_s->assoc_status_code = data->assoc_reject.status_code;
+               wpas_notify_assoc_status_code(wpa_s);
                if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME)
                        sme_event_assoc_reject(wpa_s, data);
                else {
@@ -3398,17 +3664,20 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
 #endif /* CONFIG_AP */
 #ifdef CONFIG_OFFCHANNEL
                wpa_dbg(wpa_s, MSG_DEBUG, "EVENT_TX_STATUS pending_dst="
-                       MACSTR, MAC2STR(wpa_s->parent->pending_action_dst));
+                       MACSTR, MAC2STR(wpa_s->p2pdev->pending_action_dst));
                /*
                 * Catch TX status events for Action frames we sent via group
-                * interface in GO mode.
+                * interface in GO mode, or via standalone AP interface.
+                * Note, wpa_s->p2pdev will be the same as wpa_s->parent,
+                * except when the primary interface is used as a GO interface
+                * (for drivers which do not have group interface concurrency)
                 */
                if (data->tx_status.type == WLAN_FC_TYPE_MGMT &&
                    data->tx_status.stype == WLAN_FC_STYPE_ACTION &&
-                   os_memcmp(wpa_s->parent->pending_action_dst,
+                   os_memcmp(wpa_s->p2pdev->pending_action_dst,
                              data->tx_status.dst, ETH_ALEN) == 0) {
                        offchannel_send_action_tx_status(
-                               wpa_s->parent, data->tx_status.dst,
+                               wpa_s->p2pdev, data->tx_status.dst,
                                data->tx_status.data,
                                data->tx_status.data_len,
                                data->tx_status.ack ?
@@ -3451,20 +3720,34 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
                                       data->rx_from_unknown.wds);
                break;
        case EVENT_CH_SWITCH:
-               if (!data)
-                       break;
-               if (!wpa_s->ap_iface) {
-                       wpa_dbg(wpa_s, MSG_DEBUG, "AP: Ignore channel switch "
-                               "event in non-AP mode");
+               if (!data || !wpa_s->current_ssid)
                        break;
+
+               wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_CHANNEL_SWITCH
+                       "freq=%d ht_enabled=%d ch_offset=%d ch_width=%s cf1=%d cf2=%d",
+                       data->ch_switch.freq,
+                       data->ch_switch.ht_enabled,
+                       data->ch_switch.ch_offset,
+                       channel_width_to_string(data->ch_switch.ch_width),
+                       data->ch_switch.cf1,
+                       data->ch_switch.cf2);
+
+               wpa_s->assoc_freq = data->ch_switch.freq;
+               wpa_s->current_ssid->frequency = data->ch_switch.freq;
+
+               if (wpa_s->current_ssid->mode == WPAS_MODE_AP ||
+                   wpa_s->current_ssid->mode == WPAS_MODE_P2P_GO ||
+                   wpa_s->current_ssid->mode ==
+                   WPAS_MODE_P2P_GROUP_FORMATION) {
+                       wpas_ap_ch_switch(wpa_s, data->ch_switch.freq,
+                                         data->ch_switch.ht_enabled,
+                                         data->ch_switch.ch_offset,
+                                         data->ch_switch.ch_width,
+                                         data->ch_switch.cf1,
+                                         data->ch_switch.cf2);
                }
 
-               wpas_ap_ch_switch(wpa_s, data->ch_switch.freq,
-                                 data->ch_switch.ht_enabled,
-                                 data->ch_switch.ch_offset,
-                                 data->ch_switch.ch_width,
-                                 data->ch_switch.cf1,
-                                 data->ch_switch.cf2);
+               wpas_p2p_update_channel_list(wpa_s, WPAS_P2P_CHANNEL_UPDATE_CS);
                break;
 #ifdef NEED_AP_MLME
        case EVENT_DFS_RADAR_DETECTED:
@@ -3521,12 +3804,14 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
 #endif /* CONFIG_AP */
 #ifdef CONFIG_P2P
                        if (stype == WLAN_FC_STYPE_PROBE_REQ &&
-                           data->rx_mgmt.frame_len > 24) {
+                           data->rx_mgmt.frame_len > IEEE80211_HDRLEN) {
                                const u8 *src = mgmt->sa;
-                               const u8 *ie = mgmt->u.probe_req.variable;
-                               size_t ie_len = data->rx_mgmt.frame_len -
-                                       (mgmt->u.probe_req.variable -
-                                        data->rx_mgmt.frame);
+                               const u8 *ie;
+                               size_t ie_len;
+
+                               ie = data->rx_mgmt.frame + IEEE80211_HDRLEN;
+                               ie_len = data->rx_mgmt.frame_len -
+                                       IEEE80211_HDRLEN;
                                wpas_p2p_probe_req_rx(
                                        wpa_s, src, mgmt->da,
                                        mgmt->bssid, ie, ie_len,
@@ -3566,11 +3851,12 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
                }
 
                if (stype == WLAN_FC_STYPE_PROBE_REQ &&
-                   data->rx_mgmt.frame_len > 24) {
-                       const u8 *ie = mgmt->u.probe_req.variable;
-                       size_t ie_len = data->rx_mgmt.frame_len -
-                               (mgmt->u.probe_req.variable -
-                                data->rx_mgmt.frame);
+                   data->rx_mgmt.frame_len > IEEE80211_HDRLEN) {
+                       const u8 *ie;
+                       size_t ie_len;
+
+                       ie = data->rx_mgmt.frame + IEEE80211_HDRLEN;
+                       ie_len = data->rx_mgmt.frame_len - IEEE80211_HDRLEN;
 
                        wpas_notify_preq(wpa_s, mgmt->sa, mgmt->da,
                                         mgmt->bssid, ie, ie_len,
@@ -3713,6 +3999,7 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
                                wpa_s, WLAN_REASON_DEAUTH_LEAVING, 1);
                }
                wpa_supplicant_mark_disassoc(wpa_s);
+               wpa_bss_flush(wpa_s);
                radio_remove_works(wpa_s, NULL, 0);
 
                wpa_supplicant_set_state(wpa_s, WPA_INTERFACE_DISABLED);
@@ -3771,15 +4058,28 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
                                         data->driver_gtk_rekey.replay_ctr);
                break;
        case EVENT_SCHED_SCAN_STOPPED:
-               wpa_s->pno = 0;
                wpa_s->sched_scanning = 0;
-               resched = wpa_s->scanning;
+               resched = wpa_s->scanning && wpas_scan_scheduled(wpa_s);
                wpa_supplicant_notify_scanning(wpa_s, 0);
 
                if (wpa_s->wpa_state == WPA_INTERFACE_DISABLED)
                        break;
 
                /*
+                * If the driver stopped scanning without being requested to,
+                * request a new scan to continue scanning for networks.
+                */
+               if (!wpa_s->sched_scan_stop_req &&
+                   wpa_s->wpa_state == WPA_SCANNING) {
+                       wpa_dbg(wpa_s, MSG_DEBUG,
+                               "Restart scanning after unexpected sched_scan stop event");
+                       wpa_supplicant_req_scan(wpa_s, 1, 0);
+                       break;
+               }
+
+               wpa_s->sched_scan_stop_req = 0;
+
+               /*
                 * Start a new sched scan to continue searching for more SSIDs
                 * either if timed out or PNO schedule scan is pending.
                 */
@@ -3820,8 +4120,72 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
                                     data->mesh_peer.ie_len);
 #endif /* CONFIG_MESH */
                break;
+       case EVENT_SURVEY:
+#ifdef CONFIG_AP
+               if (!wpa_s->ap_iface)
+                       break;
+               hostapd_event_get_survey(wpa_s->ap_iface,
+                                        &data->survey_results);
+#endif /* CONFIG_AP */
+               break;
+       case EVENT_ACS_CHANNEL_SELECTED:
+#ifdef CONFIG_ACS
+               if (!wpa_s->ap_iface)
+                       break;
+               hostapd_acs_channel_selected(wpa_s->ap_iface->bss[0],
+                                            &data->acs_selected_channels);
+#endif /* CONFIG_ACS */
+               break;
+       case EVENT_P2P_LO_STOP:
+#ifdef CONFIG_P2P
+               wpa_s->p2p_lo_started = 0;
+               wpa_msg(wpa_s, MSG_INFO, P2P_EVENT_LISTEN_OFFLOAD_STOP
+                       P2P_LISTEN_OFFLOAD_STOP_REASON "reason=%d",
+                       data->p2p_lo_stop.reason_code);
+#endif /* CONFIG_P2P */
+               break;
        default:
                wpa_msg(wpa_s, MSG_INFO, "Unknown event %d", event);
                break;
        }
 }
+
+
+void wpa_supplicant_event_global(void *ctx, enum wpa_event_type event,
+                                union wpa_event_data *data)
+{
+       struct wpa_supplicant *wpa_s;
+
+       if (event != EVENT_INTERFACE_STATUS)
+               return;
+
+       wpa_s = wpa_supplicant_get_iface(ctx, data->interface_status.ifname);
+       if (wpa_s && wpa_s->driver->get_ifindex) {
+               unsigned int ifindex;
+
+               ifindex = wpa_s->driver->get_ifindex(wpa_s->drv_priv);
+               if (ifindex != data->interface_status.ifindex) {
+                       wpa_dbg(wpa_s, MSG_DEBUG,
+                               "interface status ifindex %d mismatch (%d)",
+                               ifindex, data->interface_status.ifindex);
+                       return;
+               }
+       }
+#ifdef CONFIG_MATCH_IFACE
+       else if (data->interface_status.ievent == EVENT_INTERFACE_ADDED) {
+               struct wpa_interface *wpa_i;
+
+               wpa_i = wpa_supplicant_match_iface(
+                       ctx, data->interface_status.ifname);
+               if (!wpa_i)
+                       return;
+               wpa_s = wpa_supplicant_add_iface(ctx, wpa_i, NULL);
+               os_free(wpa_i);
+               if (wpa_s)
+                       wpa_s->matched = 1;
+       }
+#endif /* CONFIG_MATCH_IFACE */
+
+       if (wpa_s)
+               wpa_supplicant_event(wpa_s, event, data);
+}