Interworking: Skip extra scan after network auto-select
[mech_eap.git] / wpa_supplicant / events.c
index cddcce7..6dd5787 100644 (file)
@@ -41,6 +41,7 @@
 #include "bss.h"
 #include "scan.h"
 #include "offchannel.h"
+#include "interworking.h"
 
 
 static int wpas_temp_disabled(struct wpa_supplicant *wpa_s,
@@ -320,7 +321,7 @@ int wpa_supplicant_scard_init(struct wpa_supplicant *wpa_s,
 
 
 #ifndef CONFIG_NO_SCAN_PROCESSING
-static int wpa_supplicant_match_privacy(struct wpa_scan_res *bss,
+static int wpa_supplicant_match_privacy(struct wpa_bss *bss,
                                        struct wpa_ssid *ssid)
 {
        int i, privacy = 0;
@@ -357,7 +358,7 @@ static int wpa_supplicant_match_privacy(struct wpa_scan_res *bss,
 
 static int wpa_supplicant_ssid_bss_match(struct wpa_supplicant *wpa_s,
                                         struct wpa_ssid *ssid,
-                                        struct wpa_scan_res *bss)
+                                        struct wpa_bss *bss)
 {
        struct wpa_ie_data ie;
        int proto_match = 0;
@@ -375,7 +376,7 @@ static int wpa_supplicant_ssid_bss_match(struct wpa_supplicant *wpa_s,
                  ssid->wep_key_len[ssid->wep_tx_keyidx] > 0) ||
                 (ssid->key_mgmt & WPA_KEY_MGMT_IEEE8021X_NO_WPA));
 
-       rsn_ie = wpa_scan_get_ie(bss, WLAN_EID_RSN);
+       rsn_ie = wpa_bss_get_ie(bss, WLAN_EID_RSN);
        while ((ssid->proto & WPA_PROTO_RSN) && rsn_ie) {
                proto_match++;
 
@@ -430,7 +431,7 @@ static int wpa_supplicant_ssid_bss_match(struct wpa_supplicant *wpa_s,
                return 1;
        }
 
-       wpa_ie = wpa_scan_get_vendor_ie(bss, WPA_IE_VENDOR_TYPE);
+       wpa_ie = wpa_bss_get_vendor_ie(bss, WPA_IE_VENDOR_TYPE);
        while ((ssid->proto & WPA_PROTO_WPA) && wpa_ie) {
                proto_match++;
 
@@ -532,7 +533,7 @@ static int ht_supported(const struct hostapd_hw_modes *mode)
 }
 
 
-static int rate_match(struct wpa_supplicant *wpa_s, struct wpa_scan_res *bss)
+static int rate_match(struct wpa_supplicant *wpa_s, struct wpa_bss *bss)
 {
        const struct hostapd_hw_modes *mode = NULL, *modes;
        const u8 scan_ie[2] = { WLAN_EID_SUPP_RATES, WLAN_EID_EXT_SUPP_RATES };
@@ -570,7 +571,7 @@ static int rate_match(struct wpa_supplicant *wpa_s, struct wpa_scan_res *bss)
                return 0;
 
        for (i = 0; i < (int) sizeof(scan_ie); i++) {
-               rate_ie = wpa_scan_get_ie(bss, scan_ie[i]);
+               rate_ie = wpa_bss_get_ie(bss, scan_ie[i]);
                if (rate_ie == NULL)
                        continue;
 
@@ -624,31 +625,26 @@ static int rate_match(struct wpa_supplicant *wpa_s, struct wpa_scan_res *bss)
 
 
 static struct wpa_ssid * wpa_scan_res_match(struct wpa_supplicant *wpa_s,
-                                           int i, struct wpa_scan_res *bss,
+                                           int i, struct wpa_bss *bss,
                                            struct wpa_ssid *group)
 {
-       const u8 *ssid_;
-       u8 wpa_ie_len, rsn_ie_len, ssid_len;
+       u8 wpa_ie_len, rsn_ie_len;
        int wpa;
        struct wpa_blacklist *e;
        const u8 *ie;
        struct wpa_ssid *ssid;
 
-       ie = wpa_scan_get_ie(bss, WLAN_EID_SSID);
-       ssid_ = ie ? ie + 2 : (u8 *) "";
-       ssid_len = ie ? ie[1] : 0;
-
-       ie = wpa_scan_get_vendor_ie(bss, WPA_IE_VENDOR_TYPE);
+       ie = wpa_bss_get_vendor_ie(bss, WPA_IE_VENDOR_TYPE);
        wpa_ie_len = ie ? ie[1] : 0;
 
-       ie = wpa_scan_get_ie(bss, WLAN_EID_RSN);
+       ie = wpa_bss_get_ie(bss, WLAN_EID_RSN);
        rsn_ie_len = ie ? ie[1] : 0;
 
        wpa_dbg(wpa_s, MSG_DEBUG, "%d: " MACSTR " ssid='%s' "
                "wpa_ie_len=%u rsn_ie_len=%u caps=0x%x level=%d%s",
-               i, MAC2STR(bss->bssid), wpa_ssid_txt(ssid_, ssid_len),
+               i, MAC2STR(bss->bssid), wpa_ssid_txt(bss->ssid, bss->ssid_len),
                wpa_ie_len, rsn_ie_len, bss->caps, bss->level,
-               wpa_scan_get_vendor_ie(bss, WPS_IE_VENDOR_TYPE) ? " wps" : "");
+               wpa_bss_get_vendor_ie(bss, WPS_IE_VENDOR_TYPE) ? " wps" : "");
 
        e = wpa_blacklist_get(wpa_s, bss->bssid);
        if (e) {
@@ -671,7 +667,7 @@ static struct wpa_ssid * wpa_scan_res_match(struct wpa_supplicant *wpa_s,
                }
        }
 
-       if (ssid_len == 0) {
+       if (bss->ssid_len == 0) {
                wpa_dbg(wpa_s, MSG_DEBUG, "   skip - SSID not known");
                return NULL;
        }
@@ -721,8 +717,8 @@ static struct wpa_ssid * wpa_scan_res_match(struct wpa_supplicant *wpa_s,
                        check_ssid = 0;
 
                if (check_ssid &&
-                   (ssid_len != ssid->ssid_len ||
-                    os_memcmp(ssid_, ssid->ssid, ssid_len) != 0)) {
+                   (bss->ssid_len != ssid->ssid_len ||
+                    os_memcmp(bss->ssid, ssid->ssid, bss->ssid_len) != 0)) {
                        wpa_dbg(wpa_s, MSG_DEBUG, "   skip - SSID mismatch");
                        continue;
                }
@@ -788,32 +784,24 @@ static struct wpa_ssid * wpa_scan_res_match(struct wpa_supplicant *wpa_s,
 
 static struct wpa_bss *
 wpa_supplicant_select_bss(struct wpa_supplicant *wpa_s,
-                         struct wpa_scan_results *scan_res,
                          struct wpa_ssid *group,
                          struct wpa_ssid **selected_ssid)
 {
-       size_t i;
+       unsigned int i;
 
        wpa_dbg(wpa_s, MSG_DEBUG, "Selecting BSS from priority group %d",
                group->priority);
 
-       for (i = 0; i < scan_res->num; i++) {
-               struct wpa_scan_res *bss = scan_res->res[i];
-               const u8 *ie, *ssid;
-               u8 ssid_len;
-
+       for (i = 0; i < wpa_s->last_scan_res_used; i++) {
+               struct wpa_bss *bss = wpa_s->last_scan_res[i];
                *selected_ssid = wpa_scan_res_match(wpa_s, i, bss, group);
                if (!*selected_ssid)
                        continue;
-
-               ie = wpa_scan_get_ie(bss, WLAN_EID_SSID);
-               ssid = ie ? ie + 2 : (u8 *) "";
-               ssid_len = ie ? ie[1] : 0;
-
                wpa_dbg(wpa_s, MSG_DEBUG, "   selected BSS " MACSTR
                        " ssid='%s'",
-                       MAC2STR(bss->bssid), wpa_ssid_txt(ssid, ssid_len));
-               return wpa_bss_get(wpa_s, bss->bssid, ssid, ssid_len);
+                       MAC2STR(bss->bssid),
+                       wpa_ssid_txt(bss->ssid, bss->ssid_len));
+               return bss;
        }
 
        return NULL;
@@ -822,16 +810,19 @@ wpa_supplicant_select_bss(struct wpa_supplicant *wpa_s,
 
 static struct wpa_bss *
 wpa_supplicant_pick_network(struct wpa_supplicant *wpa_s,
-                           struct wpa_scan_results *scan_res,
                            struct wpa_ssid **selected_ssid)
 {
        struct wpa_bss *selected = NULL;
        int prio;
 
+       if (wpa_s->last_scan_res == NULL ||
+           wpa_s->last_scan_res_used == 0)
+               return NULL; /* no scan results from last update */
+
        while (selected == NULL) {
                for (prio = 0; prio < wpa_s->conf->num_prio; prio++) {
                        selected = wpa_supplicant_select_bss(
-                               wpa_s, scan_res, wpa_s->conf->pssid[prio],
+                               wpa_s, wpa_s->conf->pssid[prio],
                                selected_ssid);
                        if (selected)
                                break;
@@ -965,11 +956,9 @@ static void wpa_supplicant_rsn_preauth_scan_results(
 
 static int wpa_supplicant_need_to_roam(struct wpa_supplicant *wpa_s,
                                       struct wpa_bss *selected,
-                                      struct wpa_ssid *ssid,
-                                      struct wpa_scan_results *scan_res)
+                                      struct wpa_ssid *ssid)
 {
-       size_t i;
-       struct wpa_scan_res *current_bss = NULL;
+       struct wpa_bss *current_bss = NULL;
        int min_diff;
 
        if (wpa_s->reassociate)
@@ -984,25 +973,22 @@ static int wpa_supplicant_need_to_roam(struct wpa_supplicant *wpa_s,
        if (wpas_driver_bss_selection(wpa_s))
                return 0; /* Driver-based roaming */
 
-       for (i = 0; i < scan_res->num; i++) {
-               struct wpa_scan_res *res = scan_res->res[i];
-               const u8 *ie;
-               if (os_memcmp(res->bssid, wpa_s->bssid, ETH_ALEN) != 0)
-                       continue;
-
-               ie = wpa_scan_get_ie(res, WLAN_EID_SSID);
-               if (ie == NULL)
-                       continue;
-               if (ie[1] != wpa_s->current_ssid->ssid_len ||
-                   os_memcmp(ie + 2, wpa_s->current_ssid->ssid, ie[1]) != 0)
-                       continue;
-               current_bss = res;
-               break;
-       }
+       if (wpa_s->current_ssid->ssid)
+               current_bss = wpa_bss_get(wpa_s, wpa_s->bssid,
+                                         wpa_s->current_ssid->ssid,
+                                         wpa_s->current_ssid->ssid_len);
+       if (!current_bss)
+               current_bss = wpa_bss_get_bssid(wpa_s, wpa_s->bssid);
 
        if (!current_bss)
                return 1; /* current BSS not seen in scan results */
 
+       if (current_bss == selected)
+               return 0;
+
+       if (selected->last_update_idx > current_bss->last_update_idx)
+               return 1; /* current BSS not seen in the last scan */
+
 #ifndef CONFIG_NO_ROAMING
        wpa_dbg(wpa_s, MSG_DEBUG, "Considering within-ESS reassociation");
        wpa_dbg(wpa_s, MSG_DEBUG, "Current BSS: " MACSTR " level=%d",
@@ -1049,10 +1035,11 @@ static int wpa_supplicant_need_to_roam(struct wpa_supplicant *wpa_s,
 static int _wpa_supplicant_event_scan_results(struct wpa_supplicant *wpa_s,
                                              union wpa_event_data *data)
 {
-       struct wpa_bss *selected;
-       struct wpa_ssid *ssid = NULL;
        struct wpa_scan_results *scan_res;
        int ap = 0;
+#ifndef CONFIG_NO_RANDOM_POOL
+       size_t i, num;
+#endif /* CONFIG_NO_RANDOM_POOL */
 
 #ifdef CONFIG_AP
        if (wpa_s->ap_iface)
@@ -1087,7 +1074,6 @@ static int _wpa_supplicant_event_scan_results(struct wpa_supplicant *wpa_s,
        }
 
 #ifndef CONFIG_NO_RANDOM_POOL
-       size_t i, num;
        num = scan_res->num;
        if (num > 10)
                num = 10;
@@ -1160,13 +1146,22 @@ static int _wpa_supplicant_event_scan_results(struct wpa_supplicant *wpa_s,
 
        wpas_wps_update_ap_info(wpa_s, scan_res);
 
-       selected = wpa_supplicant_pick_network(wpa_s, scan_res, &ssid);
+       wpa_scan_results_free(scan_res);
+
+       return wpas_select_network_from_last_scan(wpa_s);
+}
+
+
+int wpas_select_network_from_last_scan(struct wpa_supplicant *wpa_s)
+{
+       struct wpa_bss *selected;
+       struct wpa_ssid *ssid = NULL;
+
+       selected = wpa_supplicant_pick_network(wpa_s, &ssid);
 
        if (selected) {
                int skip;
-               skip = !wpa_supplicant_need_to_roam(wpa_s, selected, ssid,
-                                                   scan_res);
-               wpa_scan_results_free(scan_res);
+               skip = !wpa_supplicant_need_to_roam(wpa_s, selected, ssid);
                if (skip) {
                        wpa_supplicant_rsn_preauth_scan_results(wpa_s);
                        return 0;
@@ -1178,7 +1173,6 @@ static int _wpa_supplicant_event_scan_results(struct wpa_supplicant *wpa_s,
                }
                wpa_supplicant_rsn_preauth_scan_results(wpa_s);
        } else {
-               wpa_scan_results_free(scan_res);
                wpa_dbg(wpa_s, MSG_DEBUG, "No suitable network found");
                ssid = wpa_supplicant_pick_new_network(wpa_s);
                if (ssid) {
@@ -1204,6 +1198,19 @@ static int _wpa_supplicant_event_scan_results(struct wpa_supplicant *wpa_s,
                                return 0;
                        }
 #endif /* CONFIG_P2P */
+#ifdef CONFIG_INTERWORKING
+                       if (wpa_s->conf->auto_interworking &&
+                           wpa_s->conf->interworking &&
+                           wpa_s->conf->cred) {
+                               wpa_dbg(wpa_s, MSG_DEBUG, "Interworking: "
+                                       "start ANQP fetch since no matching "
+                                       "networks found");
+                               wpa_s->network_select = 1;
+                               wpa_s->auto_network_select = 1;
+                               interworking_start_fetch_anqp(wpa_s);
+                               return 0;
+                       }
+#endif /* CONFIG_INTERWORKING */
                        if (wpa_supplicant_req_sched_scan(wpa_s))
                                wpa_supplicant_req_new_scan(wpa_s, timeout_sec,
                                                            timeout_usec);
@@ -1752,10 +1759,40 @@ static void wpa_supplicant_event_disassoc(struct wpa_supplicant *wpa_s,
                                          int locally_generated)
 {
        const u8 *bssid;
+
+       if (wpa_s->key_mgmt == WPA_KEY_MGMT_WPA_NONE) {
+               /*
+                * At least Host AP driver and a Prism3 card seemed to be
+                * generating streams of disconnected events when configuring
+                * IBSS for WPA-None. Ignore them for now.
+                */
+               return;
+       }
+
+       bssid = wpa_s->bssid;
+       if (is_zero_ether_addr(bssid))
+               bssid = wpa_s->pending_bssid;
+
+       if (!is_zero_ether_addr(bssid) ||
+           wpa_s->wpa_state >= WPA_AUTHENTICATING) {
+               wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_DISCONNECTED "bssid=" MACSTR
+                       " reason=%d%s",
+                       MAC2STR(bssid), reason_code,
+                       locally_generated ? " locally_generated=1" : "");
+       }
+}
+
+
+static void wpa_supplicant_event_disassoc_finish(struct wpa_supplicant *wpa_s,
+                                                u16 reason_code,
+                                                int locally_generated)
+{
+       const u8 *bssid;
        int authenticating;
        u8 prev_pending_bssid[ETH_ALEN];
        struct wpa_bss *fast_reconnect = NULL;
        struct wpa_ssid *fast_reconnect_ssid = NULL;
+       struct wpa_ssid *last_ssid;
 
        authenticating = wpa_s->wpa_state == WPA_AUTHENTICATING;
        os_memcpy(prev_pending_bssid, wpa_s->pending_bssid, ETH_ALEN);
@@ -1819,22 +1856,18 @@ static void wpa_supplicant_event_disassoc(struct wpa_supplicant *wpa_s,
        else
                wpa_s->disconnect_reason = reason_code;
        wpas_notify_disconnect_reason(wpa_s);
-       if (!is_zero_ether_addr(bssid) ||
-           wpa_s->wpa_state >= WPA_AUTHENTICATING) {
-               wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_DISCONNECTED "bssid=" MACSTR
-                       " reason=%d%s",
-                       MAC2STR(bssid), reason_code,
-                       locally_generated ? " locally_generated=1" : "");
-       }
        if (wpa_supplicant_dynamic_keys(wpa_s)) {
                wpa_dbg(wpa_s, MSG_DEBUG, "Disconnect event - remove keys");
                wpa_s->keys_cleared = 0;
                wpa_clear_keys(wpa_s, wpa_s->bssid);
        }
+       last_ssid = wpa_s->current_ssid;
        wpa_supplicant_mark_disassoc(wpa_s);
 
-       if (authenticating && (wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME))
+       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;
+       }
 
        if (fast_reconnect) {
 #ifndef CONFIG_NO_SCAN_PROCESSING
@@ -2354,13 +2387,23 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
                        wpas_auth_failed(wpa_s);
 #ifdef CONFIG_P2P
                if (event == EVENT_DEAUTH && data) {
-                       wpas_p2p_deauth_notif(wpa_s, data->deauth_info.addr,
-                                             reason_code,
-                                             data->deauth_info.ie,
-                                             data->deauth_info.ie_len,
-                                             locally_generated);
+                       if (wpas_p2p_deauth_notif(wpa_s,
+                                                 data->deauth_info.addr,
+                                                 reason_code,
+                                                 data->deauth_info.ie,
+                                                 data->deauth_info.ie_len,
+                                                 locally_generated) > 0) {
+                               /*
+                                * The interface was removed, so cannot
+                                * continue processing any additional
+                                * operations after this.
+                                */
+                               break;
+                       }
                }
 #endif /* CONFIG_P2P */
+               wpa_supplicant_event_disassoc_finish(wpa_s, reason_code,
+                                                    locally_generated);
                break;
        case EVENT_MICHAEL_MIC_FAILURE:
                wpa_supplicant_event_michael_mic_failure(wpa_s, data);