X-Git-Url: http://www.project-moonshot.org/gitweb/?a=blobdiff_plain;f=wpa_supplicant%2Fevents.c;h=76ee93b225d29a65c4eb5b7cd90dbe26eea32e05;hb=HEAD;hp=a1bd2ba17e1da33194f50401cd6924ce067fb26c;hpb=351f09a2218263ccee52c0f73ab7863a7364cf42;p=libeap.git diff --git a/wpa_supplicant/events.c b/wpa_supplicant/events.c index a1bd2ba..76ee93b 100644 --- a/wpa_supplicant/events.c +++ b/wpa_supplicant/events.c @@ -1,6 +1,6 @@ /* * WPA Supplicant - Driver event processing - * Copyright (c) 2003-2008, Jouni Malinen + * Copyright (c) 2003-2010, Jouni Malinen * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -16,27 +16,37 @@ #include "common.h" #include "eapol_supp/eapol_supp_sm.h" -#include "wpa.h" +#include "rsn_supp/wpa.h" #include "eloop.h" -#include "drivers/driver.h" #include "config.h" #include "l2_packet/l2_packet.h" #include "wpa_supplicant_i.h" +#include "driver_i.h" #include "pcsc_funcs.h" -#include "preauth.h" -#include "pmksa_cache.h" -#include "wpa_ctrl.h" +#include "rsn_supp/preauth.h" +#include "rsn_supp/pmksa_cache.h" +#include "common/wpa_ctrl.h" #include "eap_peer/eap.h" -#include "ctrl_iface_dbus.h" -#include "ieee802_11_defs.h" +#include "ap/hostapd.h" +#include "notify.h" +#include "common/ieee802_11_defs.h" +#include "common/ieee802_11_common.h" #include "blacklist.h" #include "wpas_glue.h" #include "wps_supplicant.h" +#include "ibss_rsn.h" +#include "sme.h" +#include "p2p_supplicant.h" +#include "bgscan.h" +#include "ap.h" +#include "bss.h" +#include "mlme.h" +#include "scan.h" static int wpa_supplicant_select_config(struct wpa_supplicant *wpa_s) { - struct wpa_ssid *ssid; + struct wpa_ssid *ssid, *old_ssid; if (wpa_s->conf->ap_scan == 1 && wpa_s->current_ssid) return 0; @@ -72,9 +82,12 @@ static int wpa_supplicant_select_config(struct wpa_supplicant *wpa_s) if (wpa_s->current_ssid && wpa_s->current_ssid != ssid) eapol_sm_invalidate_cached_session(wpa_s->eapol); + old_ssid = wpa_s->current_ssid; wpa_s->current_ssid = ssid; 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) + wpas_notify_network_changed(wpa_s); return 0; } @@ -96,9 +109,19 @@ static void wpa_supplicant_stop_countermeasures(void *eloop_ctx, void wpa_supplicant_mark_disassoc(struct wpa_supplicant *wpa_s) { + int bssid_changed; + + if (wpa_s->wpa_state == WPA_INTERFACE_DISABLED) + return; + wpa_supplicant_set_state(wpa_s, WPA_DISCONNECTED); + bssid_changed = !is_zero_ether_addr(wpa_s->bssid); os_memset(wpa_s->bssid, 0, ETH_ALEN); os_memset(wpa_s->pending_bssid, 0, ETH_ALEN); + wpa_s->current_bss = NULL; + if (bssid_changed) + wpas_notify_bssid_changed(wpa_s); + 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)) @@ -251,6 +274,11 @@ static int wpa_supplicant_match_privacy(struct wpa_scan_res *bss, if (ssid->mixed_cell) return 1; +#ifdef CONFIG_WPS + if (ssid->key_mgmt & WPA_KEY_MGMT_WPS) + return 1; +#endif /* CONFIG_WPS */ + for (i = 0; i < NUM_WEP_KEYS; i++) { if (ssid->wep_key_len[i]) { privacy = 1; @@ -270,7 +298,8 @@ static int wpa_supplicant_match_privacy(struct wpa_scan_res *bss, } -static int wpa_supplicant_ssid_bss_match(struct wpa_ssid *ssid, +static int wpa_supplicant_ssid_bss_match(struct wpa_supplicant *wpa_s, + struct wpa_ssid *ssid, struct wpa_scan_res *bss) { struct wpa_ie_data ie; @@ -278,7 +307,7 @@ static int wpa_supplicant_ssid_bss_match(struct wpa_ssid *ssid, const u8 *rsn_ie, *wpa_ie; int ret; - ret = wpas_wps_ssid_bss_match(ssid, bss); + ret = wpas_wps_ssid_bss_match(wpa_s, ssid, bss); if (ret >= 0) return ret; @@ -316,7 +345,7 @@ static int wpa_supplicant_ssid_bss_match(struct wpa_ssid *ssid, #ifdef CONFIG_IEEE80211W if (!(ie.capabilities & WPA_CAPABILITY_MFPC) && - ssid->ieee80211w == IEEE80211W_REQUIRED) { + ssid->ieee80211w == MGMT_FRAME_PROTECTION_REQUIRED) { wpa_printf(MSG_DEBUG, " skip RSN IE - no mgmt frame " "protection"); break; @@ -370,338 +399,499 @@ static int wpa_supplicant_ssid_bss_match(struct wpa_ssid *ssid, } -static struct wpa_scan_res * -wpa_supplicant_select_bss_wpa(struct wpa_supplicant *wpa_s, - struct wpa_ssid *group, - struct wpa_ssid **selected_ssid) +static int freq_allowed(int *freqs, int freq) { - struct wpa_ssid *ssid; - struct wpa_scan_res *bss; - size_t i; + int i; + + if (freqs == NULL) + return 1; + + for (i = 0; freqs[i]; i++) + if (freqs[i] == freq) + return 1; + return 0; +} + + +static struct wpa_ssid * wpa_scan_res_match(struct wpa_supplicant *wpa_s, + int i, struct wpa_scan_res *bss, + struct wpa_ssid *group) +{ + const u8 *ssid_; + u8 wpa_ie_len, rsn_ie_len, ssid_len; + int wpa; struct wpa_blacklist *e; const u8 *ie; + struct wpa_ssid *ssid; - wpa_printf(MSG_DEBUG, "Try to find WPA-enabled AP"); - for (i = 0; i < wpa_s->scan_res->num; i++) { - const u8 *ssid_; - u8 wpa_ie_len, rsn_ie_len, ssid_len; - bss = wpa_s->scan_res->res[i]; + ie = wpa_scan_get_ie(bss, WLAN_EID_SSID); + ssid_ = ie ? ie + 2 : (u8 *) ""; + ssid_len = ie ? ie[1] : 0; - 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); + wpa_ie_len = ie ? ie[1] : 0; + + ie = wpa_scan_get_ie(bss, WLAN_EID_RSN); + rsn_ie_len = ie ? ie[1] : 0; + + wpa_printf(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), + wpa_ie_len, rsn_ie_len, bss->caps, bss->level, + wpa_scan_get_vendor_ie(bss, WPS_IE_VENDOR_TYPE) ? + " wps" : ""); + + e = wpa_blacklist_get(wpa_s, bss->bssid); + if (e && e->count > 1) { + wpa_printf(MSG_DEBUG, " skip - blacklisted"); + return 0; + } - ie = wpa_scan_get_vendor_ie(bss, WPA_IE_VENDOR_TYPE); - wpa_ie_len = ie ? ie[1] : 0; + if (ssid_len == 0) { + wpa_printf(MSG_DEBUG, " skip - SSID not known"); + return 0; + } - ie = wpa_scan_get_ie(bss, WLAN_EID_RSN); - rsn_ie_len = ie ? ie[1] : 0; + wpa = wpa_ie_len > 0 || rsn_ie_len > 0; - wpa_printf(MSG_DEBUG, "%d: " MACSTR " ssid='%s' " - "wpa_ie_len=%u rsn_ie_len=%u caps=0x%x", - (int) i, MAC2STR(bss->bssid), - wpa_ssid_txt(ssid_, ssid_len), - wpa_ie_len, rsn_ie_len, bss->caps); + for (ssid = group; ssid; ssid = ssid->pnext) { + int check_ssid = wpa ? 1 : (ssid->ssid_len != 0); - e = wpa_blacklist_get(wpa_s, bss->bssid); - if (e && e->count > 1) { - wpa_printf(MSG_DEBUG, " skip - blacklisted"); + if (ssid->disabled) { + wpa_printf(MSG_DEBUG, " skip - disabled"); continue; } - if (wpa_ie_len == 0 && rsn_ie_len == 0) { - wpa_printf(MSG_DEBUG, " skip - no WPA/RSN IE"); +#ifdef CONFIG_WPS + if ((ssid->key_mgmt & WPA_KEY_MGMT_WPS) && e && e->count > 0) { + wpa_printf(MSG_DEBUG, " skip - blacklisted (WPS)"); continue; } - for (ssid = group; ssid; ssid = ssid->pnext) { - int check_ssid = 1; - - if (ssid->disabled) { - wpa_printf(MSG_DEBUG, " skip - disabled"); - continue; - } + if (wpa && ssid->ssid_len == 0 && + wpas_wps_ssid_wildcard_ok(wpa_s, ssid, bss)) + check_ssid = 0; -#ifdef CONFIG_WPS + if (!wpa && (ssid->key_mgmt & WPA_KEY_MGMT_WPS)) { + /* Only allow wildcard SSID match if an AP + * advertises active WPS operation that matches + * with our mode. */ + check_ssid = 1; if (ssid->ssid_len == 0 && - wpas_wps_ssid_wildcard_ok(ssid, bss)) + wpas_wps_ssid_wildcard_ok(wpa_s, ssid, bss)) check_ssid = 0; + } #endif /* CONFIG_WPS */ - if (check_ssid && - (ssid_len != ssid->ssid_len || - os_memcmp(ssid_, ssid->ssid, ssid_len) != 0)) { - wpa_printf(MSG_DEBUG, " skip - " - "SSID mismatch"); - continue; - } + if (check_ssid && + (ssid_len != ssid->ssid_len || + os_memcmp(ssid_, ssid->ssid, ssid_len) != 0)) { + wpa_printf(MSG_DEBUG, " skip - SSID mismatch"); + continue; + } - if (ssid->bssid_set && - os_memcmp(bss->bssid, ssid->bssid, ETH_ALEN) != 0) - { - wpa_printf(MSG_DEBUG, " skip - " - "BSSID mismatch"); - continue; - } + if (ssid->bssid_set && + os_memcmp(bss->bssid, ssid->bssid, ETH_ALEN) != 0) { + wpa_printf(MSG_DEBUG, " skip - BSSID mismatch"); + continue; + } - if (!wpa_supplicant_ssid_bss_match(ssid, bss)) - continue; + if (wpa && !wpa_supplicant_ssid_bss_match(wpa_s, ssid, bss)) + continue; - wpa_printf(MSG_DEBUG, " selected WPA AP " - MACSTR " ssid='%s'", - MAC2STR(bss->bssid), - wpa_ssid_txt(ssid_, ssid_len)); - *selected_ssid = ssid; - return bss; + if (!wpa && + !(ssid->key_mgmt & WPA_KEY_MGMT_NONE) && + !(ssid->key_mgmt & WPA_KEY_MGMT_WPS) && + !(ssid->key_mgmt & WPA_KEY_MGMT_IEEE8021X_NO_WPA)) { + wpa_printf(MSG_DEBUG, " skip - non-WPA network not " + "allowed"); + continue; + } + + if (!wpa && !wpa_supplicant_match_privacy(bss, ssid)) { + wpa_printf(MSG_DEBUG, " skip - privacy mismatch"); + continue; + } + + if (!wpa && (bss->caps & IEEE80211_CAP_IBSS)) { + wpa_printf(MSG_DEBUG, " skip - IBSS (adhoc) " + "network"); + continue; } + + if (!freq_allowed(ssid->freq_list, bss->freq)) { + wpa_printf(MSG_DEBUG, " skip - frequency not " + "allowed"); + continue; + } + +#ifdef CONFIG_P2P + /* + * TODO: skip the AP if its P2P IE has Group Formation + * bit set in the P2P Group Capability Bitmap and we + * are not in Group Formation with that device. + */ +#endif /* CONFIG_P2P */ + + /* Matching configuration found */ + return ssid; } - return NULL; + /* No matching configuration found */ + return 0; } -static struct wpa_scan_res * -wpa_supplicant_select_bss_non_wpa(struct wpa_supplicant *wpa_s, - struct wpa_ssid *group, - struct wpa_ssid **selected_ssid) +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) { - struct wpa_ssid *ssid; - struct wpa_scan_res *bss; size_t i; - struct wpa_blacklist *e; - const u8 *ie; - wpa_printf(MSG_DEBUG, "Try to find non-WPA AP"); - for (i = 0; i < wpa_s->scan_res->num; i++) { - const u8 *ssid_; - u8 wpa_ie_len, rsn_ie_len, ssid_len; - bss = wpa_s->scan_res->res[i]; + wpa_printf(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; + + *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 = ie ? ie + 2 : (u8 *) ""; ssid_len = ie ? ie[1] : 0; - ie = wpa_scan_get_vendor_ie(bss, WPA_IE_VENDOR_TYPE); - wpa_ie_len = ie ? ie[1] : 0; + wpa_printf(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); + } + + return NULL; +} - ie = wpa_scan_get_ie(bss, WLAN_EID_RSN); - rsn_ie_len = ie ? ie[1] : 0; - wpa_printf(MSG_DEBUG, "%d: " MACSTR " ssid='%s' " - "wpa_ie_len=%u rsn_ie_len=%u caps=0x%x", - (int) i, MAC2STR(bss->bssid), - wpa_ssid_txt(ssid_, ssid_len), - wpa_ie_len, rsn_ie_len, bss->caps); +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; - e = wpa_blacklist_get(wpa_s, bss->bssid); - if (e && e->count > 1) { - wpa_printf(MSG_DEBUG, " skip - blacklisted"); - continue; + 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], + selected_ssid); + if (selected) + break; } - for (ssid = group; ssid; ssid = ssid->pnext) { - int check_ssid = ssid->ssid_len != 0; + if (selected == NULL && wpa_s->blacklist) { + wpa_printf(MSG_DEBUG, "No APs found - clear blacklist " + "and try again"); + wpa_blacklist_clear(wpa_s); + wpa_s->blacklist_cleared++; + } else if (selected == NULL) + break; + } + + return selected; +} - if (ssid->disabled) { - wpa_printf(MSG_DEBUG, " skip - disabled"); - continue; - } + +static void wpa_supplicant_req_new_scan(struct wpa_supplicant *wpa_s, + int timeout_sec, int timeout_usec) +{ + if (!wpa_supplicant_enabled_networks(wpa_s->conf)) { + /* + * No networks are enabled; short-circuit request so + * we don't wait timeout seconds before transitioning + * to INACTIVE state. + */ + wpa_supplicant_set_state(wpa_s, WPA_INACTIVE); + return; + } + wpa_supplicant_req_scan(wpa_s, timeout_sec, timeout_usec); +} + + +void wpa_supplicant_connect(struct wpa_supplicant *wpa_s, + struct wpa_bss *selected, + struct wpa_ssid *ssid) +{ + if (wpas_wps_scan_pbc_overlap(wpa_s, selected, ssid)) { + wpa_msg(wpa_s, MSG_INFO, WPS_EVENT_OVERLAP + "PBC session overlap"); +#ifdef CONFIG_P2P + if (wpas_p2p_notif_pbc_overlap(wpa_s) == 1) + return; +#endif /* CONFIG_P2P */ #ifdef CONFIG_WPS - if (ssid->key_mgmt & WPA_KEY_MGMT_WPS) { - /* Only allow wildcard SSID match if an AP - * advertises active WPS operation that matches - * with our mode. */ - check_ssid = 1; - if (ssid->ssid_len == 0 && - wpas_wps_ssid_wildcard_ok(ssid, bss)) - check_ssid = 0; - } + wpas_wps_cancel(wpa_s); #endif /* CONFIG_WPS */ + return; + } - if (check_ssid && - (ssid_len != ssid->ssid_len || - os_memcmp(ssid_, ssid->ssid, ssid_len) != 0)) { - wpa_printf(MSG_DEBUG, " skip - " - "SSID mismatch"); - continue; - } + /* + * Do not trigger new association unless the BSSID has changed or if + * reassociation is requested. If we are in process of associating with + * the selected BSSID, do not trigger new attempt. + */ + if (wpa_s->reassociate || + (os_memcmp(selected->bssid, wpa_s->bssid, ETH_ALEN) != 0 && + (wpa_s->wpa_state != WPA_ASSOCIATING || + os_memcmp(selected->bssid, wpa_s->pending_bssid, ETH_ALEN) != + 0))) { + if (wpa_supplicant_scard_init(wpa_s, ssid)) { + wpa_supplicant_req_new_scan(wpa_s, 10, 0); + return; + } + wpa_supplicant_associate(wpa_s, selected, ssid); + } else { + wpa_printf(MSG_DEBUG, "Already associated with the selected " + "AP"); + } +} - if (ssid->bssid_set && - os_memcmp(bss->bssid, ssid->bssid, ETH_ALEN) != 0) - { - wpa_printf(MSG_DEBUG, " skip - " - "BSSID mismatch"); - continue; - } - - if (!(ssid->key_mgmt & WPA_KEY_MGMT_NONE) && - !(ssid->key_mgmt & WPA_KEY_MGMT_WPS) && - !(ssid->key_mgmt & WPA_KEY_MGMT_IEEE8021X_NO_WPA)) - { - wpa_printf(MSG_DEBUG, " skip - " - "non-WPA network not allowed"); - continue; - } - if ((ssid->key_mgmt & - (WPA_KEY_MGMT_IEEE8021X | WPA_KEY_MGMT_PSK | - WPA_KEY_MGMT_FT_IEEE8021X | WPA_KEY_MGMT_FT_PSK | - WPA_KEY_MGMT_IEEE8021X_SHA256 | - WPA_KEY_MGMT_PSK_SHA256)) && - (wpa_ie_len != 0 || rsn_ie_len != 0)) { - wpa_printf(MSG_DEBUG, " skip - " - "WPA network"); - continue; - } +static struct wpa_ssid * +wpa_supplicant_pick_new_network(struct wpa_supplicant *wpa_s) +{ + int prio; + struct wpa_ssid *ssid; - if (!wpa_supplicant_match_privacy(bss, ssid)) { - wpa_printf(MSG_DEBUG, " skip - " - "privacy mismatch"); + for (prio = 0; prio < wpa_s->conf->num_prio; prio++) { + for (ssid = wpa_s->conf->pssid[prio]; ssid; ssid = ssid->pnext) + { + if (ssid->disabled) continue; - } + if (ssid->mode == IEEE80211_MODE_IBSS || + ssid->mode == IEEE80211_MODE_AP) + return ssid; + } + } + return NULL; +} - if (bss->caps & IEEE80211_CAP_IBSS) { - wpa_printf(MSG_DEBUG, " skip - " - "IBSS (adhoc) network"); - continue; - } - wpa_printf(MSG_DEBUG, " selected non-WPA AP " - MACSTR " ssid='%s'", - MAC2STR(bss->bssid), - wpa_ssid_txt(ssid_, ssid_len)); - *selected_ssid = ssid; - return bss; - } +/* TODO: move the rsn_preauth_scan_result*() to be called from notify.c based + * on BSS added and BSS changed events */ +static void wpa_supplicant_rsn_preauth_scan_results( + struct wpa_supplicant *wpa_s, struct wpa_scan_results *scan_res) +{ + int i; + + if (rsn_preauth_scan_results(wpa_s->wpa) < 0) + return; + + for (i = scan_res->num - 1; i >= 0; i--) { + const u8 *ssid, *rsn; + struct wpa_scan_res *r; + + r = scan_res->res[i]; + + ssid = wpa_scan_get_ie(r, WLAN_EID_SSID); + if (ssid == NULL) + continue; + + rsn = wpa_scan_get_ie(r, WLAN_EID_RSN); + if (rsn == NULL) + continue; + + rsn_preauth_scan_result(wpa_s->wpa, r->bssid, ssid, rsn); } - return NULL; } -static struct wpa_scan_res * -wpa_supplicant_select_bss(struct wpa_supplicant *wpa_s, struct wpa_ssid *group, - struct wpa_ssid **selected_ssid) +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_scan_res *selected; + size_t i; + struct wpa_scan_res *current_bss = NULL; + int min_diff; + + if (wpa_s->reassociate) + return 1; /* explicit request to reassociate */ + if (wpa_s->wpa_state < WPA_ASSOCIATED) + return 1; /* we are not associated; continue */ + if (wpa_s->current_ssid == NULL) + return 1; /* unknown current SSID */ + if (wpa_s->current_ssid != ssid) + return 1; /* different network block */ + + 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; - wpa_printf(MSG_DEBUG, "Selecting BSS from priority group %d", - group->priority); + 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 (!current_bss) + return 1; /* current BSS not seen in scan results */ + + wpa_printf(MSG_DEBUG, "Considering within-ESS reassociation"); + wpa_printf(MSG_DEBUG, "Current BSS: " MACSTR " level=%d", + MAC2STR(current_bss->bssid), current_bss->level); + wpa_printf(MSG_DEBUG, "Selected BSS: " MACSTR " level=%d", + MAC2STR(selected->bssid), selected->level); + + if (wpa_s->current_ssid->bssid_set && + os_memcmp(selected->bssid, wpa_s->current_ssid->bssid, ETH_ALEN) == + 0) { + wpa_printf(MSG_DEBUG, "Allow reassociation - selected BSS has " + "preferred BSSID"); + return 1; + } - /* First, try to find WPA-enabled AP */ - selected = wpa_supplicant_select_bss_wpa(wpa_s, group, selected_ssid); - if (selected) - return selected; + min_diff = 2; + if (current_bss->level < 0) { + if (current_bss->level < -85) + min_diff = 1; + else if (current_bss->level < -80) + min_diff = 2; + else if (current_bss->level < -75) + min_diff = 3; + else if (current_bss->level < -70) + min_diff = 4; + else + min_diff = 5; + } + if (abs(current_bss->level - selected->level) < min_diff) { + wpa_printf(MSG_DEBUG, "Skip roam - too small difference in " + "signal level"); + return 0; + } - /* If no WPA-enabled AP found, try to find non-WPA AP, if configuration - * allows this. */ - return wpa_supplicant_select_bss_non_wpa(wpa_s, group, selected_ssid); + return 1; } -static void wpa_supplicant_event_scan_results(struct wpa_supplicant *wpa_s) +static void wpa_supplicant_event_scan_results(struct wpa_supplicant *wpa_s, + union wpa_event_data *data) { - int prio, timeout; - struct wpa_scan_res *selected = NULL; + struct wpa_bss *selected; struct wpa_ssid *ssid = NULL; + struct wpa_scan_results *scan_res; + int ap = 0; + +#ifdef CONFIG_AP + if (wpa_s->ap_iface) + ap = 1; +#endif /* CONFIG_AP */ + + wpa_supplicant_notify_scanning(wpa_s, 0); - if (wpa_supplicant_get_scan_results(wpa_s) < 0) { - if (wpa_s->conf->ap_scan == 2) + scan_res = wpa_supplicant_get_scan_results(wpa_s, + data ? &data->scan_info : + NULL, 1); + if (scan_res == NULL) { + if (wpa_s->conf->ap_scan == 2 || ap) return; wpa_printf(MSG_DEBUG, "Failed to get scan results - try " "scanning again"); - timeout = 1; - goto req_scan; + wpa_supplicant_req_new_scan(wpa_s, 1, 0); + return; } - /* - * Don't post the results if this was the initial cached - * and there were no results. - */ - if (wpa_s->scan_res_tried == 1 && wpa_s->conf->ap_scan == 1 && - wpa_s->scan_res->num == 0) { - wpa_msg(wpa_s, MSG_DEBUG, "Cached scan results are " - "empty - not posting"); - } else { - wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_SCAN_RESULTS); - wpa_supplicant_dbus_notify_scan_results(wpa_s); + if (wpa_s->scan_res_handler) { + wpa_s->scan_res_handler(wpa_s, scan_res); + wpa_s->scan_res_handler = NULL; + wpa_scan_results_free(scan_res); + return; } - if (wpa_s->conf->ap_scan == 2 || wpa_s->disconnected) + if (ap) { + wpa_printf(MSG_DEBUG, "Ignore scan results in AP mode"); + wpa_scan_results_free(scan_res); return; + } - while (selected == NULL) { - for (prio = 0; prio < wpa_s->conf->num_prio; prio++) { - selected = wpa_supplicant_select_bss( - wpa_s, wpa_s->conf->pssid[prio], &ssid); - if (selected) - break; - } + wpa_printf(MSG_DEBUG, "New scan results available"); + wpa_msg_ctrl(wpa_s, MSG_INFO, WPA_EVENT_SCAN_RESULTS); + wpas_notify_scan_results(wpa_s); - if (selected == NULL && wpa_s->blacklist) { - wpa_printf(MSG_DEBUG, "No APs found - clear blacklist " - "and try again"); - wpa_blacklist_clear(wpa_s); - } else if (selected == NULL) { - break; - } + wpas_notify_scan_done(wpa_s, 1); + + if ((wpa_s->conf->ap_scan == 2 && !wpas_wps_searching(wpa_s))) { + wpa_scan_results_free(scan_res); + return; } - if (selected) { - if (wpas_wps_scan_pbc_overlap(wpa_s, selected, ssid)) { - wpa_msg(wpa_s, MSG_INFO, WPS_EVENT_OVERLAP - "PBC session overlap"); - timeout = 10; - goto req_scan; - } + if (wpa_s->disconnected) { + wpa_supplicant_set_state(wpa_s, WPA_DISCONNECTED); + wpa_scan_results_free(scan_res); + return; + } - /* Do not trigger new association unless the BSSID has changed - * or if reassociation is requested. If we are in process of - * associating with the selected BSSID, do not trigger new - * attempt. */ - if (wpa_s->reassociate || - (os_memcmp(selected->bssid, wpa_s->bssid, ETH_ALEN) != 0 && - (wpa_s->wpa_state != WPA_ASSOCIATING || - os_memcmp(selected->bssid, wpa_s->pending_bssid, - ETH_ALEN) != 0))) { - if (wpa_supplicant_scard_init(wpa_s, ssid)) { - wpa_supplicant_req_scan(wpa_s, 10, 0); - return; - } - wpa_supplicant_associate(wpa_s, selected, ssid); - } else { - wpa_printf(MSG_DEBUG, "Already associated with the " - "selected AP."); - } - rsn_preauth_scan_results(wpa_s->wpa, wpa_s->scan_res); - } else { - wpa_printf(MSG_DEBUG, "No suitable AP found."); - timeout = 5; - goto req_scan; + if (bgscan_notify_scan(wpa_s, scan_res) == 1) { + wpa_scan_results_free(scan_res); + return; } - return; + wpa_supplicant_rsn_preauth_scan_results(wpa_s, scan_res); -req_scan: - if (wpa_s->scan_res_tried == 1 && wpa_s->conf->ap_scan == 1) { - /* - * Quick recovery if the initial scan results were not - * complete when fetched before the first scan request. - */ - wpa_s->scan_res_tried++; - timeout = 0; + selected = wpa_supplicant_pick_network(wpa_s, scan_res, &ssid); + + if (selected) { + int skip; + skip = !wpa_supplicant_need_to_roam(wpa_s, selected, ssid, + scan_res); + wpa_scan_results_free(scan_res); + if (skip) + return; + wpa_supplicant_connect(wpa_s, selected, ssid); + } else { + wpa_scan_results_free(scan_res); + wpa_printf(MSG_DEBUG, "No suitable network found"); + ssid = wpa_supplicant_pick_new_network(wpa_s); + if (ssid) { + wpa_printf(MSG_DEBUG, "Setup a new network"); + wpa_supplicant_associate(wpa_s, NULL, ssid); + } else { + int timeout_sec = 5; + int timeout_usec = 0; +#ifdef CONFIG_P2P + if (wpa_s->p2p_in_provisioning) { + /* + * Use shorter wait during P2P Provisioning + * state to speed up group formation. + */ + timeout_sec = 0; + timeout_usec = 250000; + } +#endif /* CONFIG_P2P */ + wpa_supplicant_req_new_scan(wpa_s, timeout_sec, + timeout_usec); + } } - wpa_supplicant_req_scan(wpa_s, timeout, 0); } #endif /* CONFIG_NO_SCAN_PROCESSING */ -static void wpa_supplicant_event_associnfo(struct wpa_supplicant *wpa_s, - union wpa_event_data *data) +static int wpa_supplicant_event_associnfo(struct wpa_supplicant *wpa_s, + union wpa_event_data *data) { int l, len, found = 0, wpa_found, rsn_found; - u8 *p; + const u8 *p; wpa_printf(MSG_DEBUG, "Association info event"); if (data->assoc_info.req_ies) @@ -714,6 +904,8 @@ static void wpa_supplicant_event_associnfo(struct wpa_supplicant *wpa_s, wpa_hexdump(MSG_DEBUG, "beacon_ies", data->assoc_info.beacon_ies, data->assoc_info.beacon_ies_len); + if (data->assoc_info.freq) + wpa_printf(MSG_DEBUG, "freq=%u MHz", data->assoc_info.freq); p = data->assoc_info.req_ies; l = data->assoc_info.req_ies_len; @@ -741,6 +933,71 @@ static void wpa_supplicant_event_associnfo(struct wpa_supplicant *wpa_s, if (!found && data->assoc_info.req_ies) wpa_sm_set_assoc_wpa_ie(wpa_s->wpa, NULL, 0); +#ifdef CONFIG_IEEE80211R +#ifdef CONFIG_SME + if (wpa_s->sme.auth_alg == WPA_AUTH_ALG_FT) { + u8 bssid[ETH_ALEN]; + if (wpa_drv_get_bssid(wpa_s, bssid) < 0 || + wpa_ft_validate_reassoc_resp(wpa_s->wpa, + data->assoc_info.resp_ies, + data->assoc_info.resp_ies_len, + bssid) < 0) { + wpa_printf(MSG_DEBUG, "FT: Validation of " + "Reassociation Response failed"); + wpa_supplicant_deauthenticate( + wpa_s, WLAN_REASON_INVALID_IE); + return -1; + } + } + + p = data->assoc_info.resp_ies; + l = data->assoc_info.resp_ies_len; + +#ifdef CONFIG_WPS_STRICT + if (wpa_s->current_ssid && + wpa_s->current_ssid->key_mgmt == WPA_KEY_MGMT_WPS) { + struct wpabuf *wps; + wps = ieee802_11_vendor_ie_concat(p, l, WPS_IE_VENDOR_TYPE); + if (wps == NULL) { + wpa_printf(MSG_INFO, "WPS-STRICT: AP did not include " + "WPS IE in (Re)Association Response"); + return -1; + } + + if (wps_validate_assoc_resp(wps) < 0) { + wpabuf_free(wps); + wpa_supplicant_deauthenticate( + wpa_s, WLAN_REASON_INVALID_IE); + return -1; + } + wpabuf_free(wps); + } +#endif /* CONFIG_WPS_STRICT */ + + /* Go through the IEs and make a copy of the MDIE, if present. */ + while (p && l >= 2) { + len = p[1] + 2; + if (len > l) { + wpa_hexdump(MSG_DEBUG, "Truncated IE in assoc_info", + p, l); + break; + } + if (p[0] == WLAN_EID_MOBILITY_DOMAIN && + p[1] >= MOBILITY_DOMAIN_ID_LEN) { + wpa_s->sme.ft_used = 1; + os_memcpy(wpa_s->sme.mobility_domain, p + 2, + MOBILITY_DOMAIN_ID_LEN); + break; + } + l -= len; + p += len; + } +#endif /* CONFIG_SME */ + + wpa_sm_set_ft_params(wpa_s->wpa, data->assoc_info.resp_ies, + data->assoc_info.resp_ies_len); +#endif /* CONFIG_IEEE80211R */ + /* WPA/RSN IE from Beacon/ProbeResp */ p = data->assoc_info.beacon_ies; l = data->assoc_info.beacon_ies_len; @@ -778,6 +1035,10 @@ static void wpa_supplicant_event_associnfo(struct wpa_supplicant *wpa_s, wpa_sm_set_ap_rsn_ie(wpa_s->wpa, NULL, 0); if (wpa_found || rsn_found) wpa_s->ap_ies_from_associnfo = 1; + + wpa_s->assoc_freq = data->assoc_info.freq; + + return 0; } @@ -785,21 +1046,38 @@ static void wpa_supplicant_event_assoc(struct wpa_supplicant *wpa_s, union wpa_event_data *data) { u8 bssid[ETH_ALEN]; - int ft_completed = wpa_ft_is_completed(wpa_s->wpa); + int ft_completed; + int bssid_changed; + struct wpa_driver_capa capa; + +#ifdef CONFIG_AP + if (wpa_s->ap_iface) { + hostapd_notif_assoc(wpa_s->ap_iface->bss[0], + data->assoc_info.addr, + data->assoc_info.req_ies, + data->assoc_info.req_ies_len); + return; + } +#endif /* CONFIG_AP */ - if (data) - wpa_supplicant_event_associnfo(wpa_s, data); + ft_completed = wpa_ft_is_completed(wpa_s->wpa); + if (data && wpa_supplicant_event_associnfo(wpa_s, data) < 0) + return; wpa_supplicant_set_state(wpa_s, WPA_ASSOCIATED); - if (wpa_s->use_client_mlme) + if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_USER_SPACE_MLME) os_memcpy(bssid, wpa_s->bssid, ETH_ALEN); - if (wpa_s->use_client_mlme || + if ((wpa_s->drv_flags & WPA_DRIVER_FLAGS_USER_SPACE_MLME) || (wpa_drv_get_bssid(wpa_s, bssid) >= 0 && os_memcmp(bssid, wpa_s->bssid, ETH_ALEN) != 0)) { wpa_msg(wpa_s, MSG_DEBUG, "Associated to a new BSS: BSSID=" MACSTR, MAC2STR(bssid)); + bssid_changed = os_memcmp(wpa_s->bssid, bssid, ETH_ALEN); os_memcpy(wpa_s->bssid, bssid, ETH_ALEN); os_memset(wpa_s->pending_bssid, 0, ETH_ALEN); + if (bssid_changed) + wpas_notify_bssid_changed(wpa_s); + if (wpa_supplicant_dynamic_keys(wpa_s) && !ft_completed) { wpa_clear_keys(wpa_s, bssid); } @@ -808,8 +1086,24 @@ static void wpa_supplicant_event_assoc(struct wpa_supplicant *wpa_s, wpa_s, WLAN_REASON_DEAUTH_LEAVING); return; } + if (wpa_s->current_ssid) { + struct wpa_bss *bss = NULL; + struct wpa_ssid *ssid = wpa_s->current_ssid; + if (ssid->ssid_len > 0) + bss = wpa_bss_get(wpa_s, bssid, + ssid->ssid, ssid->ssid_len); + if (!bss) + bss = wpa_bss_get_bssid(wpa_s, bssid); + if (bss) + wpa_s->current_bss = bss; + } } +#ifdef CONFIG_SME + os_memcpy(wpa_s->sme.prev_bssid, bssid, ETH_ALEN); + wpa_s->sme.prev_bssid_set = 1; +#endif /* CONFIG_SME */ + wpa_msg(wpa_s, MSG_INFO, "Associated with " MACSTR, MAC2STR(bssid)); if (wpa_s->current_ssid) { /* When using scanning (ap_scan=1), SIM PC/SC interface can be @@ -819,7 +1113,8 @@ static void wpa_supplicant_event_assoc(struct wpa_supplicant *wpa_s, wpa_supplicant_scard_init(wpa_s, wpa_s->current_ssid); } wpa_sm_notify_assoc(wpa_s->wpa, bssid); - l2_packet_notify_auth_start(wpa_s->l2); + if (wpa_s->l2) + l2_packet_notify_auth_start(wpa_s->l2); /* * Set portEnabled first to FALSE in order to get EAP state machine out @@ -839,7 +1134,9 @@ static void wpa_supplicant_event_assoc(struct wpa_supplicant *wpa_s, eapol_sm_notify_portEnabled(wpa_s->eapol, TRUE); wpa_s->eapol_received = 0; if (wpa_s->key_mgmt == WPA_KEY_MGMT_NONE || - wpa_s->key_mgmt == WPA_KEY_MGMT_WPA_NONE) { + wpa_s->key_mgmt == WPA_KEY_MGMT_WPA_NONE || + (wpa_s->current_ssid && + wpa_s->current_ssid->mode == IEEE80211_MODE_IBSS)) { wpa_supplicant_cancel_auth_timeout(wpa_s); wpa_supplicant_set_state(wpa_s, WPA_COMPLETED); } else if (!ft_completed) { @@ -848,7 +1145,7 @@ static void wpa_supplicant_event_assoc(struct wpa_supplicant *wpa_s, } wpa_supplicant_cancel_scan(wpa_s); - if (wpa_s->driver_4way_handshake && + if ((wpa_s->drv_flags & WPA_DRIVER_FLAGS_4WAY_HANDSHAKE) && wpa_key_mgmt_wpa_psk(wpa_s->key_mgmt)) { /* * We are done; the driver will take care of RSN 4-way @@ -859,12 +1156,66 @@ static void wpa_supplicant_event_assoc(struct wpa_supplicant *wpa_s, eapol_sm_notify_portValid(wpa_s->eapol, TRUE); eapol_sm_notify_eap_success(wpa_s->eapol, TRUE); } + + if (wpa_s->pending_eapol_rx) { + struct os_time now, age; + os_get_time(&now); + os_time_sub(&now, &wpa_s->pending_eapol_rx_time, &age); + if (age.sec == 0 && age.usec < 100000 && + os_memcmp(wpa_s->pending_eapol_rx_src, bssid, ETH_ALEN) == + 0) { + wpa_printf(MSG_DEBUG, "Process pending EAPOL frame " + "that was received just before association " + "notification"); + wpa_supplicant_rx_eapol( + wpa_s, wpa_s->pending_eapol_rx_src, + wpabuf_head(wpa_s->pending_eapol_rx), + wpabuf_len(wpa_s->pending_eapol_rx)); + } + wpabuf_free(wpa_s->pending_eapol_rx); + wpa_s->pending_eapol_rx = NULL; + } + +#ifdef CONFIG_BGSCAN + if (wpa_s->current_ssid != wpa_s->bgscan_ssid) { + bgscan_deinit(wpa_s); + if (wpa_s->current_ssid && wpa_s->current_ssid->bgscan) { + if (bgscan_init(wpa_s, wpa_s->current_ssid)) { + wpa_printf(MSG_DEBUG, "Failed to initialize " + "bgscan"); + /* + * Live without bgscan; it is only used as a + * roaming optimization, so the initial + * connection is not affected. + */ + } else + wpa_s->bgscan_ssid = wpa_s->current_ssid; + } else + wpa_s->bgscan_ssid = NULL; + } +#endif /* CONFIG_BGSCAN */ + + if ((wpa_s->key_mgmt == WPA_KEY_MGMT_NONE || + wpa_s->key_mgmt == WPA_KEY_MGMT_IEEE8021X_NO_WPA) && + wpa_s->current_ssid && wpa_drv_get_capa(wpa_s, &capa) == 0 && + capa.flags & WPA_DRIVER_FLAGS_SET_KEYS_AFTER_ASSOC_DONE) { + /* Set static WEP keys again */ + wpa_set_wep_keys(wpa_s, wpa_s->current_ssid); + } } -static void wpa_supplicant_event_disassoc(struct wpa_supplicant *wpa_s) +static void wpa_supplicant_event_disassoc(struct wpa_supplicant *wpa_s, + u16 reason_code) { const u8 *bssid; +#ifdef CONFIG_SME + int authenticating; + u8 prev_pending_bssid[ETH_ALEN]; + + authenticating = wpa_s->wpa_state == WPA_AUTHENTICATING; + os_memcpy(prev_pending_bssid, wpa_s->pending_bssid, ETH_ALEN); +#endif /* CONFIG_SME */ if (wpa_s->key_mgmt == WPA_KEY_MGMT_WPA_NONE) { /* @@ -882,20 +1233,49 @@ static void wpa_supplicant_event_disassoc(struct wpa_supplicant *wpa_s) wpa_msg(wpa_s, MSG_INFO, "WPA: 4-Way Handshake failed - " "pre-shared key may be incorrect"); } - if (wpa_s->wpa_state >= WPA_ASSOCIATED) - wpa_supplicant_req_scan(wpa_s, 0, 100000); + if (!wpa_s->auto_reconnect_disabled || + wpa_s->key_mgmt == WPA_KEY_MGMT_WPS) { + wpa_printf(MSG_DEBUG, "WPA: Auto connect enabled: try to " + "reconnect (wps=%d)", + wpa_s->key_mgmt == WPA_KEY_MGMT_WPS); + if (wpa_s->wpa_state >= WPA_ASSOCIATING) + wpa_supplicant_req_scan(wpa_s, 0, 100000); + } else { + wpa_printf(MSG_DEBUG, "WPA: Auto connect disabled: do not try " + "to re-connect"); + wpa_s->reassociate = 0; + wpa_s->disconnected = 1; + } bssid = wpa_s->bssid; if (is_zero_ether_addr(bssid)) bssid = wpa_s->pending_bssid; wpa_blacklist_add(wpa_s, bssid); wpa_sm_notify_disassoc(wpa_s->wpa); - wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_DISCONNECTED "- Disconnect event - " - "remove keys"); + wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_DISCONNECTED "bssid=" MACSTR + " reason=%d", + MAC2STR(bssid), reason_code); if (wpa_supplicant_dynamic_keys(wpa_s)) { + wpa_printf(MSG_DEBUG, "Disconnect event - remove keys"); wpa_s->keys_cleared = 0; wpa_clear_keys(wpa_s, wpa_s->bssid); } wpa_supplicant_mark_disassoc(wpa_s); + bgscan_deinit(wpa_s); + wpa_s->bgscan_ssid = NULL; +#ifdef CONFIG_SME + if (authenticating && + (wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME)) { + /* + * mac80211-workaround to force deauth on failed auth cmd, + * requires us to remain in authenticating state to allow the + * second authentication attempt to be continued properly. + */ + wpa_printf(MSG_DEBUG, "SME: Allow pending authentication to " + "proceed after disconnection event"); + wpa_supplicant_set_state(wpa_s, WPA_AUTHENTICATING); + os_memcpy(wpa_s->pending_bssid, prev_pending_bssid, ETH_ALEN); + } +#endif /* CONFIG_SME */ } @@ -1006,6 +1386,19 @@ wpa_supplicant_event_michael_mic_failure(struct wpa_supplicant *wpa_s, } +#ifdef CONFIG_TERMINATE_ONLASTIF +static int any_interfaces(struct wpa_supplicant *head) +{ + struct wpa_supplicant *wpa_s; + + for (wpa_s = head; wpa_s != NULL; wpa_s = wpa_s->next) + if (!wpa_s->interface_removed) + return 1; + return 0; +} +#endif /* CONFIG_TERMINATE_ONLASTIF */ + + static void wpa_supplicant_event_interface_status(struct wpa_supplicant *wpa_s, union wpa_event_data *data) @@ -1030,6 +1423,11 @@ wpa_supplicant_event_interface_status(struct wpa_supplicant *wpa_s, wpa_supplicant_mark_disassoc(wpa_s); l2_packet_deinit(wpa_s->l2); wpa_s->l2 = NULL; +#ifdef CONFIG_TERMINATE_ONLASTIF + /* check if last interface */ + if (!any_interfaces(wpa_s->global->ifaces)) + eloop_terminate(); +#endif /* CONFIG_TERMINATE_ONLASTIF */ break; } } @@ -1058,31 +1456,176 @@ wpa_supplicant_event_ft_response(struct wpa_supplicant *wpa_s, if (wpa_ft_process_response(wpa_s->wpa, data->ft_ies.ies, data->ft_ies.ies_len, data->ft_ies.ft_action, - data->ft_ies.target_ap) < 0) { + data->ft_ies.target_ap, + data->ft_ies.ric_ies, + data->ft_ies.ric_ies_len) < 0) { /* TODO: prevent MLME/driver from trying to associate? */ } } #endif /* CONFIG_IEEE80211R */ -void wpa_supplicant_event(void *ctx, wpa_event_type event, +#ifdef CONFIG_IBSS_RSN +static void wpa_supplicant_event_ibss_rsn_start(struct wpa_supplicant *wpa_s, + union wpa_event_data *data) +{ + if (data == NULL) + return; + ibss_rsn_start(wpa_s->ibss_rsn, data->ibss_rsn_start.peer); +} +#endif /* CONFIG_IBSS_RSN */ + + +#ifdef CONFIG_IEEE80211R +static void ft_rx_action(struct wpa_supplicant *wpa_s, const u8 *data, + size_t len) +{ + const u8 *sta_addr, *target_ap_addr; + u16 status; + + wpa_hexdump(MSG_MSGDUMP, "FT: RX Action", data, len); + if (!(wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME)) + return; /* only SME case supported for now */ + if (len < 1 + 2 * ETH_ALEN + 2) + return; + if (data[0] != 2) + return; /* Only FT Action Response is supported for now */ + sta_addr = data + 1; + target_ap_addr = data + 1 + ETH_ALEN; + status = WPA_GET_LE16(data + 1 + 2 * ETH_ALEN); + wpa_printf(MSG_DEBUG, "FT: Received FT Action Response: STA " MACSTR + " TargetAP " MACSTR " status %u", + MAC2STR(sta_addr), MAC2STR(target_ap_addr), status); + + if (os_memcmp(sta_addr, wpa_s->own_addr, ETH_ALEN) != 0) { + wpa_printf(MSG_DEBUG, "FT: Foreign STA Address " MACSTR + " in FT Action Response", MAC2STR(sta_addr)); + return; + } + + if (status) { + wpa_printf(MSG_DEBUG, "FT: FT Action Response indicates " + "failure (status code %d)", status); + /* TODO: report error to FT code(?) */ + return; + } + + if (wpa_ft_process_response(wpa_s->wpa, data + 1 + 2 * ETH_ALEN + 2, + len - (1 + 2 * ETH_ALEN + 2), 1, + target_ap_addr, NULL, 0) < 0) + return; + +#ifdef CONFIG_SME + { + struct wpa_bss *bss; + bss = wpa_bss_get_bssid(wpa_s, target_ap_addr); + if (bss) + wpa_s->sme.freq = bss->freq; + wpa_s->sme.auth_alg = WPA_AUTH_ALG_FT; + sme_associate(wpa_s, WPAS_MODE_INFRA, target_ap_addr, + WLAN_AUTH_FT); + } +#endif /* CONFIG_SME */ +} +#endif /* CONFIG_IEEE80211R */ + + +void wpa_supplicant_event(void *ctx, enum wpa_event_type event, union wpa_event_data *data) { struct wpa_supplicant *wpa_s = ctx; + u16 reason_code = 0; + + if (wpa_s->wpa_state == WPA_INTERFACE_DISABLED && + event != EVENT_INTERFACE_ENABLED && + event != EVENT_INTERFACE_STATUS) { + wpa_printf(MSG_DEBUG, "Ignore event %d while interface is " + "disabled", event); + return; + } + + wpa_printf(MSG_DEBUG, "Event %d received on interface %s", + event, wpa_s->ifname); switch (event) { + case EVENT_AUTH: + sme_event_auth(wpa_s, data); + break; case EVENT_ASSOC: wpa_supplicant_event_assoc(wpa_s, data); break; case EVENT_DISASSOC: - wpa_supplicant_event_disassoc(wpa_s); + wpa_printf(MSG_DEBUG, "Disassociation notification"); + if (data) { + wpa_printf(MSG_DEBUG, " * reason %u", + data->disassoc_info.reason_code); + if (data->disassoc_info.addr) + wpa_printf(MSG_DEBUG, " * address " MACSTR, + MAC2STR(data->disassoc_info.addr)); + } +#ifdef CONFIG_AP + if (wpa_s->ap_iface && data && data->disassoc_info.addr) { + hostapd_notif_disassoc(wpa_s->ap_iface->bss[0], + data->disassoc_info.addr); + break; + } +#endif /* CONFIG_AP */ + if (data) { + reason_code = data->disassoc_info.reason_code; + wpa_hexdump(MSG_DEBUG, "Disassociation frame IE(s)", + data->disassoc_info.ie, + data->disassoc_info.ie_len); +#ifdef CONFIG_P2P + wpas_p2p_disassoc_notif( + wpa_s, data->disassoc_info.addr, reason_code, + data->disassoc_info.ie, + data->disassoc_info.ie_len); +#endif /* CONFIG_P2P */ + } + if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME) + sme_event_disassoc(wpa_s, data); + /* fall through */ + case EVENT_DEAUTH: + if (event == EVENT_DEAUTH) { + wpa_printf(MSG_DEBUG, "Deauthentication notification"); + if (data) { + reason_code = data->deauth_info.reason_code; + wpa_printf(MSG_DEBUG, " * reason %u", + data->deauth_info.reason_code); + if (data->deauth_info.addr) { + wpa_printf(MSG_DEBUG, " * address " + MACSTR, + MAC2STR(data->deauth_info. + addr)); + } + wpa_hexdump(MSG_DEBUG, + "Deauthentication frame IE(s)", + data->deauth_info.ie, + data->deauth_info.ie_len); +#ifdef CONFIG_P2P + wpas_p2p_deauth_notif( + wpa_s, data->deauth_info.addr, + reason_code, + data->deauth_info.ie, + data->deauth_info.ie_len); +#endif /* CONFIG_P2P */ + } + } +#ifdef CONFIG_AP + if (wpa_s->ap_iface && data && data->deauth_info.addr) { + hostapd_notif_disassoc(wpa_s->ap_iface->bss[0], + data->deauth_info.addr); + break; + } +#endif /* CONFIG_AP */ + wpa_supplicant_event_disassoc(wpa_s, reason_code); break; case EVENT_MICHAEL_MIC_FAILURE: wpa_supplicant_event_michael_mic_failure(wpa_s, data); break; #ifndef CONFIG_NO_SCAN_PROCESSING case EVENT_SCAN_RESULTS: - wpa_supplicant_event_scan_results(wpa_s); + wpa_supplicant_event_scan_results(wpa_s, data); break; #endif /* CONFIG_NO_SCAN_PROCESSING */ case EVENT_ASSOCINFO: @@ -1104,6 +1647,193 @@ void wpa_supplicant_event(void *ctx, wpa_event_type event, wpa_supplicant_event_ft_response(wpa_s, data); break; #endif /* CONFIG_IEEE80211R */ +#ifdef CONFIG_IBSS_RSN + case EVENT_IBSS_RSN_START: + wpa_supplicant_event_ibss_rsn_start(wpa_s, data); + break; +#endif /* CONFIG_IBSS_RSN */ + case EVENT_ASSOC_REJECT: + sme_event_assoc_reject(wpa_s, data); + break; + case EVENT_AUTH_TIMED_OUT: + sme_event_auth_timed_out(wpa_s, data); + break; + case EVENT_ASSOC_TIMED_OUT: + sme_event_assoc_timed_out(wpa_s, data); + break; +#ifdef CONFIG_AP + case EVENT_TX_STATUS: + wpa_printf(MSG_DEBUG, "EVENT_TX_STATUS on %s dst=" MACSTR + " type=%d stype=%d pending_dst=" MACSTR, + wpa_s->ifname, MAC2STR(data->tx_status.dst), + data->tx_status.type, data->tx_status.stype, + MAC2STR(wpa_s->parent->pending_action_dst)); + if (wpa_s->ap_iface == NULL) { +#ifdef CONFIG_P2P + if (data->tx_status.type == WLAN_FC_TYPE_MGMT && + data->tx_status.stype == WLAN_FC_STYPE_ACTION) + wpas_send_action_tx_status( + wpa_s, data->tx_status.dst, + data->tx_status.data, + data->tx_status.data_len, + data->tx_status.ack); +#endif /* CONFIG_P2P */ + break; + } +#ifdef CONFIG_P2P + /* + * Catch TX status events for Action frames we sent via group + * interface in GO mode. + */ + 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, + data->tx_status.dst, ETH_ALEN) == 0) { + wpas_send_action_tx_status( + wpa_s->parent, data->tx_status.dst, + data->tx_status.data, + data->tx_status.data_len, + data->tx_status.ack); + break; + } +#endif /* CONFIG_P2P */ + switch (data->tx_status.type) { + case WLAN_FC_TYPE_MGMT: + ap_mgmt_tx_cb(wpa_s, data->tx_status.data, + data->tx_status.data_len, + data->tx_status.stype, + data->tx_status.ack); + break; + case WLAN_FC_TYPE_DATA: + ap_tx_status(wpa_s, data->tx_status.dst, + data->tx_status.data, + data->tx_status.data_len, + data->tx_status.ack); + break; + } + break; + case EVENT_RX_FROM_UNKNOWN: + if (wpa_s->ap_iface == NULL) + break; + ap_rx_from_unknown_sta(wpa_s, data->rx_from_unknown.frame, + data->rx_from_unknown.len); + break; + case EVENT_RX_MGMT: + if (wpa_s->ap_iface == NULL) { +#ifdef CONFIG_P2P + u16 fc, stype; + const struct ieee80211_mgmt *mgmt; + mgmt = (const struct ieee80211_mgmt *) + data->rx_mgmt.frame; + fc = le_to_host16(mgmt->frame_control); + stype = WLAN_FC_GET_STYPE(fc); + if (stype == WLAN_FC_STYPE_PROBE_REQ && + data->rx_mgmt.frame_len > 24) { + 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); + wpas_p2p_probe_req_rx(wpa_s, src, ie, ie_len); + break; + } +#endif /* CONFIG_P2P */ + wpa_printf(MSG_DEBUG, "AP: ignore received management " + "frame in non-AP mode"); + break; + } + ap_mgmt_rx(wpa_s, &data->rx_mgmt); + break; +#endif /* CONFIG_AP */ + case EVENT_RX_ACTION: + wpa_printf(MSG_DEBUG, "Received Action frame: SA=" MACSTR + " Category=%u DataLen=%d freq=%d MHz", + MAC2STR(data->rx_action.sa), + data->rx_action.category, (int) data->rx_action.len, + data->rx_action.freq); +#ifdef CONFIG_IEEE80211R + if (data->rx_action.category == WLAN_ACTION_FT) { + ft_rx_action(wpa_s, data->rx_action.data, + data->rx_action.len); + break; + } +#endif /* CONFIG_IEEE80211R */ +#ifdef CONFIG_P2P + wpas_p2p_rx_action(wpa_s, data->rx_action.da, + data->rx_action.sa, + data->rx_action.bssid, + data->rx_action.category, + data->rx_action.data, + data->rx_action.len, data->rx_action.freq); +#endif /* CONFIG_P2P */ + break; +#ifdef CONFIG_P2P + case EVENT_REMAIN_ON_CHANNEL: + wpas_p2p_remain_on_channel_cb( + wpa_s, data->remain_on_channel.freq, + data->remain_on_channel.duration); + break; + case EVENT_CANCEL_REMAIN_ON_CHANNEL: + wpas_p2p_cancel_remain_on_channel_cb( + wpa_s, data->remain_on_channel.freq); + break; + case EVENT_RX_PROBE_REQ: + wpas_p2p_probe_req_rx(wpa_s, data->rx_probe_req.sa, + data->rx_probe_req.ie, + data->rx_probe_req.ie_len); + break; +#endif /* CONFIG_P2P */ +#ifdef CONFIG_CLIENT_MLME + case EVENT_MLME_RX: { + struct ieee80211_rx_status rx_status; + os_memset(&rx_status, 0, sizeof(rx_status)); + rx_status.freq = data->mlme_rx.freq; + rx_status.channel = data->mlme_rx.channel; + rx_status.ssi = data->mlme_rx.ssi; + ieee80211_sta_rx(wpa_s, data->mlme_rx.buf, data->mlme_rx.len, + &rx_status); + break; + } +#endif /* CONFIG_CLIENT_MLME */ + case EVENT_EAPOL_RX: + wpa_supplicant_rx_eapol(wpa_s, data->eapol_rx.src, + data->eapol_rx.data, + data->eapol_rx.data_len); + break; + case EVENT_SIGNAL_CHANGE: + bgscan_notify_signal_change( + wpa_s, data->signal_change.above_threshold, + data->signal_change.current_signal, + data->signal_change.current_noise, + data->signal_change.current_txrate); + break; + case EVENT_INTERFACE_ENABLED: + wpa_printf(MSG_DEBUG, "Interface was enabled"); + if (wpa_s->wpa_state == WPA_INTERFACE_DISABLED) { +#ifdef CONFIG_AP + if (!wpa_s->ap_iface) { + wpa_supplicant_set_state(wpa_s, + WPA_DISCONNECTED); + wpa_supplicant_req_scan(wpa_s, 0, 0); + } else + wpa_supplicant_set_state(wpa_s, + WPA_COMPLETED); +#else /* CONFIG_AP */ + wpa_supplicant_set_state(wpa_s, WPA_DISCONNECTED); + wpa_supplicant_req_scan(wpa_s, 0, 0); +#endif /* CONFIG_AP */ + } + break; + case EVENT_INTERFACE_DISABLED: + wpa_printf(MSG_DEBUG, "Interface was disabled"); + wpa_supplicant_mark_disassoc(wpa_s); + wpa_supplicant_set_state(wpa_s, WPA_INTERFACE_DISABLED); + break; + case EVENT_CHANNEL_LIST_CHANGED: +#ifdef CONFIG_P2P + wpas_p2p_update_channel_list(wpa_s); +#endif /* CONFIG_P2P */ + break; default: wpa_printf(MSG_INFO, "Unknown event %d", event); break;