diff options
author | Rui Paulo <rpaulo@FreeBSD.org> | 2013-07-04 21:12:58 +0000 |
---|---|---|
committer | Rui Paulo <rpaulo@FreeBSD.org> | 2013-07-04 21:12:58 +0000 |
commit | f05cddf940dbfc5b657f5e9beb9de2c31e509e5b (patch) | |
tree | fabd7f6454b47c2dac03bf9badf207872ea2410a /contrib/wpa/wpa_supplicant/sme.c | |
parent | 2173825bacf87fd3499817865adca2c790ac252e (diff) | |
parent | 5e2639d568f6bb660501a77cc83413c3412562e3 (diff) | |
download | src-f05cddf940dbfc5b657f5e9beb9de2c31e509e5b.tar.gz src-f05cddf940dbfc5b657f5e9beb9de2c31e509e5b.zip |
Merge hostapd / wpa_supplicant 2.0.
Reviewed by: adrian (driver_bsd + usr.sbin/wpa)
Notes
Notes:
svn path=/head/; revision=252726
Diffstat (limited to 'contrib/wpa/wpa_supplicant/sme.c')
-rw-r--r-- | contrib/wpa/wpa_supplicant/sme.c | 971 |
1 files changed, 861 insertions, 110 deletions
diff --git a/contrib/wpa/wpa_supplicant/sme.c b/contrib/wpa/wpa_supplicant/sme.c index 5604e97cb3ee..77ad1d2e1e2c 100644 --- a/contrib/wpa/wpa_supplicant/sme.c +++ b/contrib/wpa/wpa_supplicant/sme.c @@ -2,19 +2,14 @@ * wpa_supplicant - SME * Copyright (c) 2009-2010, Jouni Malinen <j@w1.fi> * - * 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 - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" #include "common.h" +#include "utils/eloop.h" #include "common/ieee802_11_defs.h" #include "common/ieee802_11_common.h" #include "eapol_supp/eapol_supp_sm.h" @@ -26,14 +21,68 @@ #include "driver_i.h" #include "wpas_glue.h" #include "wps_supplicant.h" +#include "p2p_supplicant.h" #include "notify.h" -#include "blacklist.h" #include "bss.h" #include "scan.h" #include "sme.h" +#include "hs20_supplicant.h" -void sme_authenticate(struct wpa_supplicant *wpa_s, - struct wpa_bss *bss, struct wpa_ssid *ssid) +#define SME_AUTH_TIMEOUT 5 +#define SME_ASSOC_TIMEOUT 5 + +static void sme_auth_timer(void *eloop_ctx, void *timeout_ctx); +static void sme_assoc_timer(void *eloop_ctx, void *timeout_ctx); +static void sme_obss_scan_timeout(void *eloop_ctx, void *timeout_ctx); +#ifdef CONFIG_IEEE80211W +static void sme_stop_sa_query(struct wpa_supplicant *wpa_s); +#endif /* CONFIG_IEEE80211W */ + + +#ifdef CONFIG_SAE + +static struct wpabuf * sme_auth_build_sae_commit(struct wpa_supplicant *wpa_s) +{ + struct wpabuf *buf; + + buf = wpabuf_alloc(4 + 2); + if (buf == NULL) + return NULL; + + wpabuf_put_le16(buf, 1); /* Transaction seq# */ + wpabuf_put_le16(buf, WLAN_STATUS_SUCCESS); + wpabuf_put_le16(buf, 19); /* Finite Cyclic Group */ + /* TODO: Anti-Clogging Token (if requested) */ + /* TODO: Scalar */ + /* TODO: Element */ + + return buf; +} + + +static struct wpabuf * sme_auth_build_sae_confirm(struct wpa_supplicant *wpa_s) +{ + struct wpabuf *buf; + + buf = wpabuf_alloc(4 + 2); + if (buf == NULL) + return NULL; + + wpabuf_put_le16(buf, 2); /* Transaction seq# */ + wpabuf_put_le16(buf, WLAN_STATUS_SUCCESS); + wpabuf_put_le16(buf, wpa_s->sme.sae_send_confirm); + wpa_s->sme.sae_send_confirm++; + /* TODO: Confirm */ + + return buf; +} + +#endif /* CONFIG_SAE */ + + +static void sme_send_authentication(struct wpa_supplicant *wpa_s, + struct wpa_bss *bss, struct wpa_ssid *ssid, + int start) { struct wpa_driver_auth_params params; struct wpa_ssid *old_ssid; @@ -44,10 +93,13 @@ void sme_authenticate(struct wpa_supplicant *wpa_s, const u8 *md = NULL; #endif /* CONFIG_IEEE80211R */ int i, bssid_changed; + struct wpabuf *resp = NULL; + u8 ext_capab[10]; + int ext_capab_len; if (bss == NULL) { - wpa_printf(MSG_ERROR, "SME: No scan result available for the " - "network"); + wpa_msg(wpa_s, MSG_ERROR, "SME: No scan result available for " + "the network"); return; } @@ -60,6 +112,7 @@ void sme_authenticate(struct wpa_supplicant *wpa_s, params.bssid = bss->bssid; params.ssid = bss->ssid; params.ssid_len = bss->ssid_len; + params.p2p = ssid->p2p_group; if (wpa_s->sme.ssid_len != params.ssid_len || os_memcmp(wpa_s->sme.ssid, params.ssid, params.ssid_len) != 0) @@ -80,13 +133,28 @@ void sme_authenticate(struct wpa_supplicant *wpa_s, } } #endif /* IEEE8021X_EAPOL */ - wpa_printf(MSG_DEBUG, "Automatic auth_alg selection: 0x%x", - params.auth_alg); + wpa_dbg(wpa_s, MSG_DEBUG, "Automatic auth_alg selection: 0x%x", + params.auth_alg); if (ssid->auth_alg) { params.auth_alg = ssid->auth_alg; - wpa_printf(MSG_DEBUG, "Overriding auth_alg selection: 0x%x", - params.auth_alg); + wpa_dbg(wpa_s, MSG_DEBUG, "Overriding auth_alg selection: " + "0x%x", params.auth_alg); } +#ifdef CONFIG_SAE + if (wpa_key_mgmt_sae(ssid->key_mgmt)) { + const u8 *rsn; + struct wpa_ie_data ied; + + rsn = wpa_bss_get_ie(bss, WLAN_EID_RSN); + if (rsn && + wpa_parse_wpa_ie(rsn, 2 + rsn[1], &ied) == 0) { + if (wpa_key_mgmt_sae(ied.key_mgmt)) { + wpa_dbg(wpa_s, MSG_DEBUG, "Using SAE auth_alg"); + params.auth_alg = WPA_AUTH_ALG_SAE; + } + } + } +#endif /* CONFIG_SAE */ for (i = 0; i < NUM_WEP_KEYS; i++) { if (ssid->wep_key_len[i]) @@ -103,13 +171,11 @@ void sme_authenticate(struct wpa_supplicant *wpa_s, if ((wpa_bss_get_vendor_ie(bss, WPA_IE_VENDOR_TYPE) || wpa_bss_get_ie(bss, WLAN_EID_RSN)) && - (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_key_mgmt_wpa(ssid->key_mgmt)) { int try_opportunistic; - try_opportunistic = ssid->proactive_key_caching && + try_opportunistic = (ssid->proactive_key_caching < 0 ? + wpa_s->conf->okc : + ssid->proactive_key_caching) && (ssid->proto & WPA_PROTO_RSN); if (pmksa_cache_set_current(wpa_s->wpa, NULL, bss->bssid, wpa_s->current_ssid, @@ -119,22 +185,27 @@ void sme_authenticate(struct wpa_supplicant *wpa_s, if (wpa_supplicant_set_suites(wpa_s, bss, ssid, wpa_s->sme.assoc_req_ie, &wpa_s->sme.assoc_req_ie_len)) { - wpa_printf(MSG_WARNING, "SME: Failed to set WPA key " - "management and encryption suites"); + wpa_msg(wpa_s, MSG_WARNING, "SME: Failed to set WPA " + "key management and encryption suites"); return; } - } else if (ssid->key_mgmt & - (WPA_KEY_MGMT_PSK | WPA_KEY_MGMT_IEEE8021X | - WPA_KEY_MGMT_WPA_NONE | WPA_KEY_MGMT_FT_PSK | - WPA_KEY_MGMT_FT_IEEE8021X | WPA_KEY_MGMT_PSK_SHA256 | - WPA_KEY_MGMT_IEEE8021X_SHA256)) { + } else if ((ssid->key_mgmt & WPA_KEY_MGMT_IEEE8021X_NO_WPA) && + wpa_key_mgmt_wpa_ieee8021x(ssid->key_mgmt)) { + /* + * Both WPA and non-WPA IEEE 802.1X enabled in configuration - + * use non-WPA since the scan results did not indicate that the + * AP is using WPA or WPA2. + */ + wpa_supplicant_set_non_wpa_policy(wpa_s, ssid); + wpa_s->sme.assoc_req_ie_len = 0; + } else if (wpa_key_mgmt_wpa_any(ssid->key_mgmt)) { wpa_s->sme.assoc_req_ie_len = sizeof(wpa_s->sme.assoc_req_ie); if (wpa_supplicant_set_suites(wpa_s, NULL, ssid, wpa_s->sme.assoc_req_ie, &wpa_s->sme.assoc_req_ie_len)) { - wpa_printf(MSG_WARNING, "SME: Failed to set WPA key " - "management and encryption suites (no scan " - "results)"); + wpa_msg(wpa_s, MSG_WARNING, "SME: Failed to set WPA " + "key management and encryption suites (no " + "scan results)"); return; } #ifdef CONFIG_WPS @@ -166,8 +237,7 @@ void sme_authenticate(struct wpa_supplicant *wpa_s, wpa_ft_prepare_auth_request(wpa_s->wpa, ie); } - if (md && ssid->key_mgmt & (WPA_KEY_MGMT_FT_PSK | - WPA_KEY_MGMT_FT_IEEE8021X)) { + if (md && wpa_key_mgmt_ft(ssid->key_mgmt)) { if (wpa_s->sme.assoc_req_ie_len + 5 < sizeof(wpa_s->sme.assoc_req_ie)) { struct rsn_mdie *mdie; @@ -185,8 +255,8 @@ void sme_authenticate(struct wpa_supplicant *wpa_s, if (wpa_s->sme.ft_used && os_memcmp(md, wpa_s->sme.mobility_domain, 2) == 0 && wpa_sm_has_ptk(wpa_s->wpa)) { - wpa_printf(MSG_DEBUG, "SME: Trying to use FT " - "over-the-air"); + wpa_dbg(wpa_s, MSG_DEBUG, "SME: Trying to use FT " + "over-the-air"); params.auth_alg = WPA_AUTH_ALG_FT; params.ie = wpa_s->sme.ft_ies; params.ie_len = wpa_s->sme.ft_ies_len; @@ -195,23 +265,81 @@ void sme_authenticate(struct wpa_supplicant *wpa_s, #endif /* CONFIG_IEEE80211R */ #ifdef CONFIG_IEEE80211W - wpa_s->sme.mfp = ssid->ieee80211w; - if (ssid->ieee80211w != NO_MGMT_FRAME_PROTECTION) { + wpa_s->sme.mfp = ssid->ieee80211w == MGMT_FRAME_PROTECTION_DEFAULT ? + wpa_s->conf->pmf : ssid->ieee80211w; + if (wpa_s->sme.mfp != NO_MGMT_FRAME_PROTECTION) { const u8 *rsn = wpa_bss_get_ie(bss, WLAN_EID_RSN); struct wpa_ie_data _ie; if (rsn && wpa_parse_wpa_ie(rsn, 2 + rsn[1], &_ie) == 0 && _ie.capabilities & (WPA_CAPABILITY_MFPC | WPA_CAPABILITY_MFPR)) { - wpa_printf(MSG_DEBUG, "WPA: Selected AP supports MFP: " - "require MFP"); + wpa_dbg(wpa_s, MSG_DEBUG, "SME: Selected AP supports " + "MFP: require MFP"); wpa_s->sme.mfp = MGMT_FRAME_PROTECTION_REQUIRED; } } #endif /* CONFIG_IEEE80211W */ +#ifdef CONFIG_P2P + if (wpa_s->global->p2p) { + u8 *pos; + size_t len; + int res; + pos = wpa_s->sme.assoc_req_ie + wpa_s->sme.assoc_req_ie_len; + len = sizeof(wpa_s->sme.assoc_req_ie) - + wpa_s->sme.assoc_req_ie_len; + res = wpas_p2p_assoc_req_ie(wpa_s, bss, pos, len, + ssid->p2p_group); + if (res >= 0) + wpa_s->sme.assoc_req_ie_len += res; + } +#endif /* CONFIG_P2P */ + +#ifdef CONFIG_HS20 + if (wpa_s->conf->hs20) { + struct wpabuf *hs20; + hs20 = wpabuf_alloc(20); + if (hs20) { + wpas_hs20_add_indication(hs20); + os_memcpy(wpa_s->sme.assoc_req_ie + + wpa_s->sme.assoc_req_ie_len, + wpabuf_head(hs20), wpabuf_len(hs20)); + wpa_s->sme.assoc_req_ie_len += wpabuf_len(hs20); + wpabuf_free(hs20); + } + } +#endif /* CONFIG_HS20 */ + + ext_capab_len = wpas_build_ext_capab(wpa_s, ext_capab); + if (ext_capab_len > 0) { + u8 *pos = wpa_s->sme.assoc_req_ie; + if (wpa_s->sme.assoc_req_ie_len > 0 && pos[0] == WLAN_EID_RSN) + pos += 2 + pos[1]; + os_memmove(pos + ext_capab_len, pos, + wpa_s->sme.assoc_req_ie_len - + (pos - wpa_s->sme.assoc_req_ie)); + wpa_s->sme.assoc_req_ie_len += ext_capab_len; + os_memcpy(pos, ext_capab, ext_capab_len); + } + +#ifdef CONFIG_SAE + if (params.auth_alg == WPA_AUTH_ALG_SAE) { + if (start) + resp = sme_auth_build_sae_commit(wpa_s); + else + resp = sme_auth_build_sae_confirm(wpa_s); + if (resp == NULL) + return; + params.sae_data = wpabuf_head(resp); + params.sae_data_len = wpabuf_len(resp); + wpa_s->sme.sae_state = start ? SME_SAE_COMMIT : SME_SAE_CONFIRM; + } +#endif /* CONFIG_SAE */ + + wpa_supplicant_cancel_sched_scan(wpa_s); wpa_supplicant_cancel_scan(wpa_s); - wpa_msg(wpa_s, MSG_INFO, "Trying to authenticate with " MACSTR + wpa_msg(wpa_s, MSG_INFO, "SME: Trying to authenticate with " MACSTR " (SSID='%s' freq=%d MHz)", MAC2STR(params.bssid), wpa_ssid_txt(params.ssid, params.ssid_len), params.freq); @@ -226,19 +354,104 @@ void sme_authenticate(struct wpa_supplicant *wpa_s, wpa_s->sme.auth_alg = params.auth_alg; if (wpa_drv_authenticate(wpa_s, ¶ms) < 0) { - wpa_msg(wpa_s, MSG_INFO, "Authentication request to the " + wpa_msg(wpa_s, MSG_INFO, "SME: Authentication request to the " "driver failed"); - wpa_supplicant_req_scan(wpa_s, 1, 0); + wpas_connection_failed(wpa_s, bss->bssid); + wpa_supplicant_mark_disassoc(wpa_s); + wpabuf_free(resp); return; } - /* TODO: add timeout on authentication */ + eloop_register_timeout(SME_AUTH_TIMEOUT, 0, sme_auth_timer, wpa_s, + NULL); /* * Association will be started based on the authentication event from * the driver. */ + + wpabuf_free(resp); +} + + +void sme_authenticate(struct wpa_supplicant *wpa_s, + struct wpa_bss *bss, struct wpa_ssid *ssid) +{ + wpa_s->sme.sae_state = SME_SAE_INIT; + wpa_s->sme.sae_send_confirm = 0; + sme_send_authentication(wpa_s, bss, ssid, 1); +} + + +#ifdef CONFIG_SAE + +static int sme_sae_process_commit(struct wpa_supplicant *wpa_s, const u8 *data, + size_t len) +{ + /* Check Finite Cyclic Group */ + if (len < 2) + return -1; + if (WPA_GET_LE16(data) != 19) { + wpa_printf(MSG_DEBUG, "SAE: Unsupported Finite Cyclic Group %u", + WPA_GET_LE16(data)); + return -1; + } + + /* TODO */ + + return 0; +} + + +static int sme_sae_process_confirm(struct wpa_supplicant *wpa_s, const u8 *data, + size_t len) +{ + u16 rc; + + if (len < 2) + return -1; + rc = WPA_GET_LE16(data); + wpa_printf(MSG_DEBUG, "SAE: peer-send-confirm %u", rc); + + /* TODO */ + return 0; +} + + +static int sme_sae_auth(struct wpa_supplicant *wpa_s, u16 auth_transaction, + u16 status_code, const u8 *data, size_t len) +{ + wpa_dbg(wpa_s, MSG_DEBUG, "SME: SAE authentication transaction %u " + "status code %u", auth_transaction, status_code); + wpa_hexdump(MSG_DEBUG, "SME: SAE fields", data, len); + + if (status_code != WLAN_STATUS_SUCCESS) + return -1; + + if (auth_transaction == 1) { + wpa_dbg(wpa_s, MSG_DEBUG, "SME SAE commit"); + if (wpa_s->current_bss == NULL || + wpa_s->current_ssid == NULL) + return -1; + if (wpa_s->sme.sae_state != SME_SAE_COMMIT) + return -1; + if (sme_sae_process_commit(wpa_s, data, len) < 0) + return -1; + sme_send_authentication(wpa_s, wpa_s->current_bss, + wpa_s->current_ssid, 0); + return 0; + } else if (auth_transaction == 2) { + wpa_dbg(wpa_s, MSG_DEBUG, "SME SAE confirm"); + if (wpa_s->sme.sae_state != SME_SAE_CONFIRM) + return -1; + if (sme_sae_process_confirm(wpa_s, data, len) < 0) + return -1; + return 1; + } + + return -1; } +#endif /* CONFIG_SAE */ void sme_event_auth(struct wpa_supplicant *wpa_s, union wpa_event_data *data) @@ -246,46 +459,67 @@ void sme_event_auth(struct wpa_supplicant *wpa_s, union wpa_event_data *data) struct wpa_ssid *ssid = wpa_s->current_ssid; if (ssid == NULL) { - wpa_printf(MSG_DEBUG, "SME: Ignore authentication event when " - "network is not selected"); + wpa_dbg(wpa_s, MSG_DEBUG, "SME: Ignore authentication event " + "when network is not selected"); return; } if (wpa_s->wpa_state != WPA_AUTHENTICATING) { - wpa_printf(MSG_DEBUG, "SME: Ignore authentication event when " - "not in authenticating state"); + wpa_dbg(wpa_s, MSG_DEBUG, "SME: Ignore authentication event " + "when not in authenticating state"); return; } if (os_memcmp(wpa_s->pending_bssid, data->auth.peer, ETH_ALEN) != 0) { - wpa_printf(MSG_DEBUG, "SME: Ignore authentication with " - "unexpected peer " MACSTR, - MAC2STR(data->auth.peer)); + wpa_dbg(wpa_s, MSG_DEBUG, "SME: Ignore authentication with " + "unexpected peer " MACSTR, + MAC2STR(data->auth.peer)); return; } - wpa_printf(MSG_DEBUG, "SME: Authentication response: peer=" MACSTR - " auth_type=%d status_code=%d", - MAC2STR(data->auth.peer), data->auth.auth_type, - data->auth.status_code); + wpa_dbg(wpa_s, MSG_DEBUG, "SME: Authentication response: peer=" MACSTR + " auth_type=%d auth_transaction=%d status_code=%d", + MAC2STR(data->auth.peer), data->auth.auth_type, + data->auth.auth_transaction, data->auth.status_code); wpa_hexdump(MSG_MSGDUMP, "SME: Authentication response IEs", data->auth.ies, data->auth.ies_len); + eloop_cancel_timeout(sme_auth_timer, wpa_s, NULL); + +#ifdef CONFIG_SAE + if (data->auth.auth_type == WLAN_AUTH_SAE) { + int res; + res = sme_sae_auth(wpa_s, data->auth.auth_transaction, + data->auth.status_code, data->auth.ies, + data->auth.ies_len); + if (res < 0) { + wpas_connection_failed(wpa_s, wpa_s->pending_bssid); + wpa_supplicant_set_state(wpa_s, WPA_DISCONNECTED); + + } + if (res != 1) + return; + } +#endif /* CONFIG_SAE */ + if (data->auth.status_code != WLAN_STATUS_SUCCESS) { - wpa_printf(MSG_DEBUG, "SME: Authentication failed (status " - "code %d)", data->auth.status_code); + wpa_dbg(wpa_s, MSG_DEBUG, "SME: Authentication failed (status " + "code %d)", data->auth.status_code); if (data->auth.status_code != WLAN_STATUS_NOT_SUPPORTED_AUTH_ALG || wpa_s->sme.auth_alg == data->auth.auth_type || - wpa_s->current_ssid->auth_alg == WPA_AUTH_ALG_LEAP) + wpa_s->current_ssid->auth_alg == WPA_AUTH_ALG_LEAP) { + wpas_connection_failed(wpa_s, wpa_s->pending_bssid); + wpa_supplicant_set_state(wpa_s, WPA_DISCONNECTED); return; + } switch (data->auth.auth_type) { case WLAN_AUTH_OPEN: wpa_s->current_ssid->auth_alg = WPA_AUTH_ALG_SHARED; - wpa_printf(MSG_DEBUG, "SME: Trying SHARED auth"); + wpa_dbg(wpa_s, MSG_DEBUG, "SME: Trying SHARED auth"); wpa_supplicant_associate(wpa_s, wpa_s->current_bss, wpa_s->current_ssid); return; @@ -293,7 +527,7 @@ void sme_event_auth(struct wpa_supplicant *wpa_s, union wpa_event_data *data) case WLAN_AUTH_SHARED_KEY: wpa_s->current_ssid->auth_alg = WPA_AUTH_ALG_LEAP; - wpa_printf(MSG_DEBUG, "SME: Trying LEAP auth"); + wpa_dbg(wpa_s, MSG_DEBUG, "SME: Trying LEAP auth"); wpa_supplicant_associate(wpa_s, wpa_s->current_bss, wpa_s->current_ssid); return; @@ -324,15 +558,30 @@ void sme_associate(struct wpa_supplicant *wpa_s, enum wpas_mode mode, { struct wpa_driver_associate_params params; struct ieee802_11_elems elems; +#ifdef CONFIG_HT_OVERRIDES + struct ieee80211_ht_capabilities htcaps; + struct ieee80211_ht_capabilities htcaps_mask; +#endif /* CONFIG_HT_OVERRIDES */ os_memset(¶ms, 0, sizeof(params)); params.bssid = bssid; params.ssid = wpa_s->sme.ssid; params.ssid_len = wpa_s->sme.ssid_len; params.freq = wpa_s->sme.freq; + params.bg_scan_period = wpa_s->current_ssid ? + wpa_s->current_ssid->bg_scan_period : -1; params.wpa_ie = wpa_s->sme.assoc_req_ie_len ? wpa_s->sme.assoc_req_ie : NULL; params.wpa_ie_len = wpa_s->sme.assoc_req_ie_len; + params.pairwise_suite = cipher_suite2driver(wpa_s->pairwise_cipher); + params.group_suite = cipher_suite2driver(wpa_s->group_cipher); +#ifdef CONFIG_HT_OVERRIDES + os_memset(&htcaps, 0, sizeof(htcaps)); + os_memset(&htcaps_mask, 0, sizeof(htcaps_mask)); + params.htcaps = (u8 *) &htcaps; + params.htcaps_mask = (u8 *) &htcaps_mask; + wpa_supplicant_apply_ht_overrides(wpa_s, wpa_s->current_ssid, ¶ms); +#endif /* CONFIG_HT_OVERRIDES */ #ifdef CONFIG_IEEE80211R if (auth_type == WLAN_AUTH_FT && wpa_s->sme.ft_ies) { params.wpa_ie = wpa_s->sme.ft_ies; @@ -354,26 +603,38 @@ void sme_associate(struct wpa_supplicant *wpa_s, enum wpas_mode mode, if (params.wpa_ie == NULL || ieee802_11_parse_elems(params.wpa_ie, params.wpa_ie_len, &elems, 0) < 0) { - wpa_printf(MSG_DEBUG, "SME: Could not parse own IEs?!"); + wpa_dbg(wpa_s, MSG_DEBUG, "SME: Could not parse own IEs?!"); os_memset(&elems, 0, sizeof(elems)); } - if (elems.rsn_ie) + if (elems.rsn_ie) { + params.wpa_proto = WPA_PROTO_RSN; wpa_sm_set_assoc_wpa_ie(wpa_s->wpa, elems.rsn_ie - 2, elems.rsn_ie_len + 2); - else if (elems.wpa_ie) + } else if (elems.wpa_ie) { + params.wpa_proto = WPA_PROTO_WPA; wpa_sm_set_assoc_wpa_ie(wpa_s->wpa, elems.wpa_ie - 2, elems.wpa_ie_len + 2); - else + } else wpa_sm_set_assoc_wpa_ie(wpa_s->wpa, NULL, 0); + if (wpa_s->current_ssid && wpa_s->current_ssid->p2p_group) + params.p2p = 1; + + if (wpa_s->parent->set_sta_uapsd) + params.uapsd = wpa_s->parent->sta_uapsd; + else + params.uapsd = -1; if (wpa_drv_associate(wpa_s, ¶ms) < 0) { - wpa_msg(wpa_s, MSG_INFO, "Association request to the driver " - "failed"); - wpa_supplicant_req_scan(wpa_s, 5, 0); + wpa_msg(wpa_s, MSG_INFO, "SME: Association request to the " + "driver failed"); + wpas_connection_failed(wpa_s, wpa_s->pending_bssid); + wpa_supplicant_set_state(wpa_s, WPA_DISCONNECTED); + os_memset(wpa_s->pending_bssid, 0, ETH_ALEN); return; } - /* TODO: add timeout on association */ + eloop_register_timeout(SME_ASSOC_TIMEOUT, 0, sme_assoc_timer, wpa_s, + NULL); } @@ -381,7 +642,7 @@ int sme_update_ft_ies(struct wpa_supplicant *wpa_s, const u8 *md, const u8 *ies, size_t ies_len) { if (md == NULL || ies == NULL) { - wpa_printf(MSG_DEBUG, "SME: Remove mobility domain"); + wpa_dbg(wpa_s, MSG_DEBUG, "SME: Remove mobility domain"); os_free(wpa_s->sme.ft_ies); wpa_s->sme.ft_ies = NULL; wpa_s->sme.ft_ies_len = 0; @@ -401,90 +662,580 @@ int sme_update_ft_ies(struct wpa_supplicant *wpa_s, const u8 *md, } -void sme_event_assoc_reject(struct wpa_supplicant *wpa_s, - union wpa_event_data *data) +static void sme_deauth(struct wpa_supplicant *wpa_s) { int bssid_changed; - int timeout = 5000; - - wpa_printf(MSG_DEBUG, "SME: Association with " MACSTR " failed: " - "status code %d", MAC2STR(wpa_s->pending_bssid), - data->assoc_reject.status_code); bssid_changed = !is_zero_ether_addr(wpa_s->bssid); - /* - * For now, unconditionally terminate the previous authentication. In - * theory, this should not be needed, but mac80211 gets quite confused - * if the authentication is left pending.. Some roaming cases might - * benefit from using the previous authentication, so this could be - * optimized in the future. - */ if (wpa_drv_deauthenticate(wpa_s, wpa_s->pending_bssid, WLAN_REASON_DEAUTH_LEAVING) < 0) { - wpa_msg(wpa_s, MSG_INFO, - "Deauth request to the driver failed"); + wpa_msg(wpa_s, MSG_INFO, "SME: Deauth request to the driver " + "failed"); } wpa_s->sme.prev_bssid_set = 0; - if (wpa_blacklist_add(wpa_s, wpa_s->pending_bssid) == 0) { - struct wpa_blacklist *b; - b = wpa_blacklist_get(wpa_s, wpa_s->pending_bssid); - if (b && b->count < 3) { - /* - * Speed up next attempt if there could be other APs - * that could accept association. - */ - timeout = 100; - } - } + wpas_connection_failed(wpa_s, wpa_s->pending_bssid); wpa_supplicant_set_state(wpa_s, WPA_DISCONNECTED); os_memset(wpa_s->bssid, 0, ETH_ALEN); os_memset(wpa_s->pending_bssid, 0, ETH_ALEN); if (bssid_changed) wpas_notify_bssid_changed(wpa_s); +} + + +void sme_event_assoc_reject(struct wpa_supplicant *wpa_s, + union wpa_event_data *data) +{ + wpa_dbg(wpa_s, MSG_DEBUG, "SME: Association with " MACSTR " failed: " + "status code %d", MAC2STR(wpa_s->pending_bssid), + data->assoc_reject.status_code); + + eloop_cancel_timeout(sme_assoc_timer, wpa_s, NULL); /* - * TODO: if more than one possible AP is available in scan results, - * could try the other ones before requesting a new scan. + * For now, unconditionally terminate the previous authentication. In + * theory, this should not be needed, but mac80211 gets quite confused + * if the authentication is left pending.. Some roaming cases might + * benefit from using the previous authentication, so this could be + * optimized in the future. */ - wpa_supplicant_req_scan(wpa_s, timeout / 1000, - 1000 * (timeout % 1000)); + sme_deauth(wpa_s); } void sme_event_auth_timed_out(struct wpa_supplicant *wpa_s, union wpa_event_data *data) { - wpa_printf(MSG_DEBUG, "SME: Authentication timed out"); - wpa_supplicant_req_scan(wpa_s, 5, 0); + wpa_dbg(wpa_s, MSG_DEBUG, "SME: Authentication timed out"); + wpas_connection_failed(wpa_s, wpa_s->pending_bssid); + wpa_supplicant_mark_disassoc(wpa_s); } void sme_event_assoc_timed_out(struct wpa_supplicant *wpa_s, union wpa_event_data *data) { - wpa_printf(MSG_DEBUG, "SME: Association timed out"); + wpa_dbg(wpa_s, MSG_DEBUG, "SME: Association timed out"); + wpas_connection_failed(wpa_s, wpa_s->pending_bssid); wpa_supplicant_mark_disassoc(wpa_s); - wpa_supplicant_req_scan(wpa_s, 5, 0); } void sme_event_disassoc(struct wpa_supplicant *wpa_s, union wpa_event_data *data) { - wpa_printf(MSG_DEBUG, "SME: Disassociation event received"); - if (wpa_s->sme.prev_bssid_set && - !(wpa_s->drv_flags & WPA_DRIVER_FLAGS_USER_SPACE_MLME)) { + wpa_dbg(wpa_s, MSG_DEBUG, "SME: Disassociation event received"); + if (wpa_s->sme.prev_bssid_set) { /* * cfg80211/mac80211 can get into somewhat confused state if * the AP only disassociates us and leaves us in authenticated * state. For now, force the state to be cleared to avoid * confusing errors if we try to associate with the AP again. */ - wpa_printf(MSG_DEBUG, "SME: Deauthenticate to clear driver " - "state"); + wpa_dbg(wpa_s, MSG_DEBUG, "SME: Deauthenticate to clear " + "driver state"); wpa_drv_deauthenticate(wpa_s, wpa_s->sme.prev_bssid, WLAN_REASON_DEAUTH_LEAVING); } } + + +static void sme_auth_timer(void *eloop_ctx, void *timeout_ctx) +{ + struct wpa_supplicant *wpa_s = eloop_ctx; + if (wpa_s->wpa_state == WPA_AUTHENTICATING) { + wpa_msg(wpa_s, MSG_DEBUG, "SME: Authentication timeout"); + sme_deauth(wpa_s); + } +} + + +static void sme_assoc_timer(void *eloop_ctx, void *timeout_ctx) +{ + struct wpa_supplicant *wpa_s = eloop_ctx; + if (wpa_s->wpa_state == WPA_ASSOCIATING) { + wpa_msg(wpa_s, MSG_DEBUG, "SME: Association timeout"); + sme_deauth(wpa_s); + } +} + + +void sme_state_changed(struct wpa_supplicant *wpa_s) +{ + /* Make sure timers are cleaned up appropriately. */ + if (wpa_s->wpa_state != WPA_ASSOCIATING) + eloop_cancel_timeout(sme_assoc_timer, wpa_s, NULL); + if (wpa_s->wpa_state != WPA_AUTHENTICATING) + eloop_cancel_timeout(sme_auth_timer, wpa_s, NULL); +} + + +void sme_disassoc_while_authenticating(struct wpa_supplicant *wpa_s, + const u8 *prev_pending_bssid) +{ + /* + * 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_dbg(wpa_s, 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); + + /* + * Re-arm authentication timer in case auth fails for whatever reason. + */ + eloop_cancel_timeout(sme_auth_timer, wpa_s, NULL); + eloop_register_timeout(SME_AUTH_TIMEOUT, 0, sme_auth_timer, wpa_s, + NULL); +} + + +void sme_deinit(struct wpa_supplicant *wpa_s) +{ + os_free(wpa_s->sme.ft_ies); + wpa_s->sme.ft_ies = NULL; + wpa_s->sme.ft_ies_len = 0; +#ifdef CONFIG_IEEE80211W + sme_stop_sa_query(wpa_s); +#endif /* CONFIG_IEEE80211W */ + + eloop_cancel_timeout(sme_assoc_timer, wpa_s, NULL); + eloop_cancel_timeout(sme_auth_timer, wpa_s, NULL); + eloop_cancel_timeout(sme_obss_scan_timeout, wpa_s, NULL); +} + + +static void sme_send_2040_bss_coex(struct wpa_supplicant *wpa_s, + const u8 *chan_list, u8 num_channels, + u8 num_intol) +{ + struct ieee80211_2040_bss_coex_ie *bc_ie; + struct ieee80211_2040_intol_chan_report *ic_report; + struct wpabuf *buf; + + wpa_printf(MSG_DEBUG, "SME: Send 20/40 BSS Coexistence to " MACSTR, + MAC2STR(wpa_s->bssid)); + + buf = wpabuf_alloc(2 + /* action.category + action_code */ + sizeof(struct ieee80211_2040_bss_coex_ie) + + sizeof(struct ieee80211_2040_intol_chan_report) + + num_channels); + if (buf == NULL) + return; + + wpabuf_put_u8(buf, WLAN_ACTION_PUBLIC); + wpabuf_put_u8(buf, WLAN_PA_20_40_BSS_COEX); + + bc_ie = wpabuf_put(buf, sizeof(*bc_ie)); + bc_ie->element_id = WLAN_EID_20_40_BSS_COEXISTENCE; + bc_ie->length = 1; + if (num_intol) + bc_ie->coex_param |= WLAN_20_40_BSS_COEX_20MHZ_WIDTH_REQ; + + if (num_channels > 0) { + ic_report = wpabuf_put(buf, sizeof(*ic_report)); + ic_report->element_id = WLAN_EID_20_40_BSS_INTOLERANT; + ic_report->length = num_channels + 1; + ic_report->op_class = 0; + os_memcpy(wpabuf_put(buf, num_channels), chan_list, + num_channels); + } + + if (wpa_drv_send_action(wpa_s, wpa_s->assoc_freq, 0, wpa_s->bssid, + wpa_s->own_addr, wpa_s->bssid, + wpabuf_head(buf), wpabuf_len(buf), 0) < 0) { + wpa_msg(wpa_s, MSG_INFO, + "SME: Failed to send 20/40 BSS Coexistence frame"); + } + + wpabuf_free(buf); +} + + +/** + * enum wpas_band - Frequency band + * @WPAS_BAND_2GHZ: 2.4 GHz ISM band + * @WPAS_BAND_5GHZ: around 5 GHz band (4.9 - 5.7 GHz) + */ +enum wpas_band { + WPAS_BAND_2GHZ, + WPAS_BAND_5GHZ, + WPAS_BAND_INVALID +}; + +/** + * freq_to_channel - Convert frequency into channel info + * @channel: Buffer for returning channel number + * Returns: Band (2 or 5 GHz) + */ +static enum wpas_band freq_to_channel(int freq, u8 *channel) +{ + enum wpas_band band = (freq <= 2484) ? WPAS_BAND_2GHZ : WPAS_BAND_5GHZ; + u8 chan = 0; + + if (freq >= 2412 && freq <= 2472) + chan = (freq - 2407) / 5; + else if (freq == 2484) + chan = 14; + else if (freq >= 5180 && freq <= 5805) + chan = (freq - 5000) / 5; + + *channel = chan; + return band; +} + + +int sme_proc_obss_scan(struct wpa_supplicant *wpa_s) +{ + struct wpa_bss *bss; + const u8 *ie; + u16 ht_cap; + u8 chan_list[P2P_MAX_CHANNELS], channel; + u8 num_channels = 0, num_intol = 0, i; + + if (!wpa_s->sme.sched_obss_scan) + return 0; + + wpa_s->sme.sched_obss_scan = 0; + if (!wpa_s->current_bss || wpa_s->wpa_state != WPA_COMPLETED) + return 1; + + /* + * Check whether AP uses regulatory triplet or channel triplet in + * country info. Right now the operating class of the BSS channel + * width trigger event is "unknown" (IEEE Std 802.11-2012 10.15.12), + * based on the assumption that operating class triplet is not used in + * beacon frame. If the First Channel Number/Operating Extension + * Identifier octet has a positive integer value of 201 or greater, + * then its operating class triplet. + * + * TODO: If Supported Operating Classes element is present in beacon + * frame, have to lookup operating class in Annex E and fill them in + * 2040 coex frame. + */ + ie = wpa_bss_get_ie(wpa_s->current_bss, WLAN_EID_COUNTRY); + if (ie && (ie[1] >= 6) && (ie[5] >= 201)) + return 1; + + os_memset(chan_list, 0, sizeof(chan_list)); + + dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) { + /* Skip other band bss */ + if (freq_to_channel(bss->freq, &channel) != WPAS_BAND_2GHZ) + continue; + + ie = wpa_bss_get_ie(bss, WLAN_EID_HT_CAP); + ht_cap = (ie && (ie[1] == 26)) ? WPA_GET_LE16(ie + 2) : 0; + + if (!ht_cap || (ht_cap & HT_CAP_INFO_40MHZ_INTOLERANT)) { + /* Check whether the channel is already considered */ + for (i = 0; i < num_channels; i++) { + if (channel == chan_list[i]) + break; + } + if (i != num_channels) + continue; + + if (ht_cap & HT_CAP_INFO_40MHZ_INTOLERANT) + num_intol++; + + chan_list[num_channels++] = channel; + } + } + + sme_send_2040_bss_coex(wpa_s, chan_list, num_channels, num_intol); + return 1; +} + + +static struct hostapd_hw_modes * get_mode(struct hostapd_hw_modes *modes, + u16 num_modes, + enum hostapd_hw_mode mode) +{ + u16 i; + + for (i = 0; i < num_modes; i++) { + if (modes[i].mode == mode) + return &modes[i]; + } + + return NULL; +} + + +static void wpa_setband_scan_freqs_list(struct wpa_supplicant *wpa_s, + enum hostapd_hw_mode band, + struct wpa_driver_scan_params *params) +{ + /* Include only supported channels for the specified band */ + struct hostapd_hw_modes *mode; + int count, i; + + mode = get_mode(wpa_s->hw.modes, wpa_s->hw.num_modes, band); + if (mode == NULL) { + /* No channels supported in this band - use empty list */ + params->freqs = os_zalloc(sizeof(int)); + return; + } + + params->freqs = os_calloc(mode->num_channels + 1, sizeof(int)); + if (params->freqs == NULL) + return; + for (count = 0, i = 0; i < mode->num_channels; i++) { + if (mode->channels[i].flag & HOSTAPD_CHAN_DISABLED) + continue; + params->freqs[count++] = mode->channels[i].freq; + } +} + + +static void sme_obss_scan_timeout(void *eloop_ctx, void *timeout_ctx) +{ + struct wpa_supplicant *wpa_s = eloop_ctx; + struct wpa_driver_scan_params params; + + if (!wpa_s->current_bss) { + wpa_printf(MSG_DEBUG, "SME OBSS: Ignore scan request"); + return; + } + + os_memset(¶ms, 0, sizeof(params)); + wpa_setband_scan_freqs_list(wpa_s, HOSTAPD_MODE_IEEE80211G, ¶ms); + wpa_printf(MSG_DEBUG, "SME OBSS: Request an OBSS scan"); + + if (wpa_supplicant_trigger_scan(wpa_s, ¶ms)) + wpa_printf(MSG_DEBUG, "SME OBSS: Failed to trigger scan"); + else + wpa_s->sme.sched_obss_scan = 1; + os_free(params.freqs); + + eloop_register_timeout(wpa_s->sme.obss_scan_int, 0, + sme_obss_scan_timeout, wpa_s, NULL); +} + + +void sme_sched_obss_scan(struct wpa_supplicant *wpa_s, int enable) +{ + const u8 *ie; + struct wpa_bss *bss = wpa_s->current_bss; + struct wpa_ssid *ssid = wpa_s->current_ssid; + struct hostapd_hw_modes *hw_mode = NULL; + int i; + + eloop_cancel_timeout(sme_obss_scan_timeout, wpa_s, NULL); + wpa_s->sme.sched_obss_scan = 0; + if (!enable) + return; + + /* + * Schedule OBSS scan if driver is using station SME in wpa_supplicant + * or it expects OBSS scan to be performed by wpa_supplicant. + */ + if (!((wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME) || + (wpa_s->drv_flags & WPA_DRIVER_FLAGS_OBSS_SCAN)) || + ssid == NULL || ssid->mode != IEEE80211_MODE_INFRA) + return; + + if (!wpa_s->hw.modes) + return; + + /* only HT caps in 11g mode are relevant */ + for (i = 0; i < wpa_s->hw.num_modes; i++) { + hw_mode = &wpa_s->hw.modes[i]; + if (hw_mode->mode == HOSTAPD_MODE_IEEE80211G) + break; + } + + /* Driver does not support HT40 for 11g or doesn't have 11g. */ + if (i == wpa_s->hw.num_modes || !hw_mode || + !(hw_mode->ht_capab & HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET)) + return; + + if (bss == NULL || bss->freq < 2400 || bss->freq > 2500) + return; /* Not associated on 2.4 GHz band */ + + /* Check whether AP supports HT40 */ + ie = wpa_bss_get_ie(wpa_s->current_bss, WLAN_EID_HT_CAP); + if (!ie || ie[1] < 2 || + !(WPA_GET_LE16(ie + 2) & HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET)) + return; /* AP does not support HT40 */ + + ie = wpa_bss_get_ie(wpa_s->current_bss, + WLAN_EID_OVERLAPPING_BSS_SCAN_PARAMS); + if (!ie || ie[1] < 14) + return; /* AP does not request OBSS scans */ + + wpa_s->sme.obss_scan_int = WPA_GET_LE16(ie + 6); + if (wpa_s->sme.obss_scan_int < 10) { + wpa_printf(MSG_DEBUG, "SME: Invalid OBSS Scan Interval %u " + "replaced with the minimum 10 sec", + wpa_s->sme.obss_scan_int); + wpa_s->sme.obss_scan_int = 10; + } + wpa_printf(MSG_DEBUG, "SME: OBSS Scan Interval %u sec", + wpa_s->sme.obss_scan_int); + eloop_register_timeout(wpa_s->sme.obss_scan_int, 0, + sme_obss_scan_timeout, wpa_s, NULL); +} + + +#ifdef CONFIG_IEEE80211W + +static const unsigned int sa_query_max_timeout = 1000; +static const unsigned int sa_query_retry_timeout = 201; + +static int sme_check_sa_query_timeout(struct wpa_supplicant *wpa_s) +{ + u32 tu; + struct os_time now, passed; + os_get_time(&now); + os_time_sub(&now, &wpa_s->sme.sa_query_start, &passed); + tu = (passed.sec * 1000000 + passed.usec) / 1024; + if (sa_query_max_timeout < tu) { + wpa_dbg(wpa_s, MSG_DEBUG, "SME: SA Query timed out"); + sme_stop_sa_query(wpa_s); + wpa_supplicant_deauthenticate( + wpa_s, WLAN_REASON_PREV_AUTH_NOT_VALID); + return 1; + } + + return 0; +} + + +static void sme_send_sa_query_req(struct wpa_supplicant *wpa_s, + const u8 *trans_id) +{ + u8 req[2 + WLAN_SA_QUERY_TR_ID_LEN]; + wpa_dbg(wpa_s, MSG_DEBUG, "SME: Sending SA Query Request to " + MACSTR, MAC2STR(wpa_s->bssid)); + wpa_hexdump(MSG_DEBUG, "SME: SA Query Transaction ID", + trans_id, WLAN_SA_QUERY_TR_ID_LEN); + req[0] = WLAN_ACTION_SA_QUERY; + req[1] = WLAN_SA_QUERY_REQUEST; + os_memcpy(req + 2, trans_id, WLAN_SA_QUERY_TR_ID_LEN); + if (wpa_drv_send_action(wpa_s, wpa_s->assoc_freq, 0, wpa_s->bssid, + wpa_s->own_addr, wpa_s->bssid, + req, sizeof(req), 0) < 0) + wpa_msg(wpa_s, MSG_INFO, "SME: Failed to send SA Query " + "Request"); +} + + +static void sme_sa_query_timer(void *eloop_ctx, void *timeout_ctx) +{ + struct wpa_supplicant *wpa_s = eloop_ctx; + unsigned int timeout, sec, usec; + u8 *trans_id, *nbuf; + + if (wpa_s->sme.sa_query_count > 0 && + sme_check_sa_query_timeout(wpa_s)) + return; + + nbuf = os_realloc_array(wpa_s->sme.sa_query_trans_id, + wpa_s->sme.sa_query_count + 1, + WLAN_SA_QUERY_TR_ID_LEN); + if (nbuf == NULL) + return; + if (wpa_s->sme.sa_query_count == 0) { + /* Starting a new SA Query procedure */ + os_get_time(&wpa_s->sme.sa_query_start); + } + trans_id = nbuf + wpa_s->sme.sa_query_count * WLAN_SA_QUERY_TR_ID_LEN; + wpa_s->sme.sa_query_trans_id = nbuf; + wpa_s->sme.sa_query_count++; + + os_get_random(trans_id, WLAN_SA_QUERY_TR_ID_LEN); + + timeout = sa_query_retry_timeout; + sec = ((timeout / 1000) * 1024) / 1000; + usec = (timeout % 1000) * 1024; + eloop_register_timeout(sec, usec, sme_sa_query_timer, wpa_s, NULL); + + wpa_dbg(wpa_s, MSG_DEBUG, "SME: Association SA Query attempt %d", + wpa_s->sme.sa_query_count); + + sme_send_sa_query_req(wpa_s, trans_id); +} + + +static void sme_start_sa_query(struct wpa_supplicant *wpa_s) +{ + sme_sa_query_timer(wpa_s, NULL); +} + + +static void sme_stop_sa_query(struct wpa_supplicant *wpa_s) +{ + eloop_cancel_timeout(sme_sa_query_timer, wpa_s, NULL); + os_free(wpa_s->sme.sa_query_trans_id); + wpa_s->sme.sa_query_trans_id = NULL; + wpa_s->sme.sa_query_count = 0; +} + + +void sme_event_unprot_disconnect(struct wpa_supplicant *wpa_s, const u8 *sa, + const u8 *da, u16 reason_code) +{ + struct wpa_ssid *ssid; + + if (!(wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME)) + return; + if (wpa_s->wpa_state != WPA_COMPLETED) + return; + ssid = wpa_s->current_ssid; + if (ssid == NULL || + (ssid->ieee80211w == MGMT_FRAME_PROTECTION_DEFAULT ? + wpa_s->conf->pmf : ssid->ieee80211w) == NO_MGMT_FRAME_PROTECTION) + return; + if (os_memcmp(sa, wpa_s->bssid, ETH_ALEN) != 0) + return; + if (reason_code != WLAN_REASON_CLASS2_FRAME_FROM_NONAUTH_STA && + reason_code != WLAN_REASON_CLASS3_FRAME_FROM_NONASSOC_STA) + return; + if (wpa_s->sme.sa_query_count > 0) + return; + + wpa_dbg(wpa_s, MSG_DEBUG, "SME: Unprotected disconnect dropped - " + "possible AP/STA state mismatch - trigger SA Query"); + sme_start_sa_query(wpa_s); +} + + +void sme_sa_query_rx(struct wpa_supplicant *wpa_s, const u8 *sa, + const u8 *data, size_t len) +{ + int i; + + if (wpa_s->sme.sa_query_trans_id == NULL || + len < 1 + WLAN_SA_QUERY_TR_ID_LEN || + data[0] != WLAN_SA_QUERY_RESPONSE) + return; + wpa_dbg(wpa_s, MSG_DEBUG, "SME: Received SA Query response from " + MACSTR " (trans_id %02x%02x)", MAC2STR(sa), data[1], data[2]); + + if (os_memcmp(sa, wpa_s->bssid, ETH_ALEN) != 0) + return; + + for (i = 0; i < wpa_s->sme.sa_query_count; i++) { + if (os_memcmp(wpa_s->sme.sa_query_trans_id + + i * WLAN_SA_QUERY_TR_ID_LEN, + data + 1, WLAN_SA_QUERY_TR_ID_LEN) == 0) + break; + } + + if (i >= wpa_s->sme.sa_query_count) { + wpa_dbg(wpa_s, MSG_DEBUG, "SME: No matching SA Query " + "transaction identifier found"); + return; + } + + wpa_dbg(wpa_s, MSG_DEBUG, "SME: Reply to pending SA Query received " + "from " MACSTR, MAC2STR(sa)); + sme_stop_sa_query(wpa_s); +} + +#endif /* CONFIG_IEEE80211W */ |