diff --git a/src/drivers/driver.h b/src/drivers/driver.h index 956403a15..036092489 100644 --- a/src/drivers/driver.h +++ b/src/drivers/driver.h @@ -2266,7 +2266,27 @@ enum wpa_event_type { * (e.g., based on RSSI or channel use). This information can be used * to improve channel selection for a new AP/P2P group. */ - EVENT_BEST_CHANNEL + EVENT_BEST_CHANNEL, + + /** + * EVENT_UNPROT_DEAUTH - Unprotected Deauthentication frame received + * + * This event should be called when a Deauthentication frame is dropped + * due to it not being protected (MFP/IEEE 802.11w). + * union wpa_event_data::unprot_deauth is required to provide more + * details of the frame. + */ + EVENT_UNPROT_DEAUTH, + + /** + * EVENT_UNPROT_DISASSOC - Unprotected Disassociation frame received + * + * This event should be called when a Disassociation frame is dropped + * due to it not being protected (MFP/IEEE 802.11w). + * union wpa_event_data::unprot_disassoc is required to provide more + * details of the frame. + */ + EVENT_UNPROT_DISASSOC, }; @@ -2700,6 +2720,18 @@ union wpa_event_data { int freq_5; int freq_overall; } best_chan; + + struct unprot_deauth { + const u8 *sa; + const u8 *da; + u16 reason_code; + } unprot_deauth; + + struct unprot_disassoc { + const u8 *sa; + const u8 *da; + u16 reason_code; + } unprot_disassoc; }; /** diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c index a3852249d..38c5f343b 100644 --- a/src/drivers/driver_nl80211.c +++ b/src/drivers/driver_nl80211.c @@ -835,6 +835,38 @@ static void mlme_event_deauth_disassoc(struct wpa_driver_nl80211_data *drv, } +static void mlme_event_unprot_disconnect(struct wpa_driver_nl80211_data *drv, + enum wpa_event_type type, + const u8 *frame, size_t len) +{ + const struct ieee80211_mgmt *mgmt; + union wpa_event_data event; + u16 reason_code = 0; + + if (len < 24) + return; + + mgmt = (const struct ieee80211_mgmt *) frame; + + os_memset(&event, 0, sizeof(event)); + /* Note: Same offset for Reason Code in both frame subtypes */ + if (len >= 24 + sizeof(mgmt->u.deauth)) + reason_code = le_to_host16(mgmt->u.deauth.reason_code); + + if (type == EVENT_UNPROT_DISASSOC) { + event.unprot_disassoc.sa = mgmt->sa; + event.unprot_disassoc.da = mgmt->da; + event.unprot_disassoc.reason_code = reason_code; + } else { + event.unprot_deauth.sa = mgmt->sa; + event.unprot_deauth.da = mgmt->da; + event.unprot_deauth.reason_code = reason_code; + } + + wpa_supplicant_event(drv->ctx, type, &event); +} + + static void mlme_event(struct wpa_driver_nl80211_data *drv, enum nl80211_commands cmd, struct nlattr *frame, struct nlattr *addr, struct nlattr *timed_out, @@ -878,6 +910,14 @@ static void mlme_event(struct wpa_driver_nl80211_data *drv, mlme_event_action_tx_status(drv, cookie, nla_data(frame), nla_len(frame), ack); break; + case NL80211_CMD_UNPROT_DEAUTHENTICATE: + mlme_event_unprot_disconnect(drv, EVENT_UNPROT_DEAUTH, + nla_data(frame), nla_len(frame)); + break; + case NL80211_CMD_UNPROT_DISASSOCIATE: + mlme_event_unprot_disconnect(drv, EVENT_UNPROT_DISASSOC, + nla_data(frame), nla_len(frame)); + break; default: break; } @@ -1294,6 +1334,8 @@ static int process_event(struct nl_msg *msg, void *arg) case NL80211_CMD_DISASSOCIATE: case NL80211_CMD_FRAME: case NL80211_CMD_FRAME_TX_STATUS: + case NL80211_CMD_UNPROT_DEAUTHENTICATE: + case NL80211_CMD_UNPROT_DISASSOCIATE: mlme_event(drv, gnlh->cmd, tb[NL80211_ATTR_FRAME], tb[NL80211_ATTR_MAC], tb[NL80211_ATTR_TIMED_OUT], tb[NL80211_ATTR_WIPHY_FREQ], tb[NL80211_ATTR_ACK], @@ -1873,6 +1915,11 @@ static int nl80211_register_action_frames(struct wpa_driver_nl80211_data *drv) 5) < 0) return -1; #endif /* CONFIG_P2P */ +#ifdef CONFIG_IEEE80211W + /* SA Query Response */ + if (nl80211_register_action_frame(drv, (u8 *) "\x08\x01", 2) < 0) + return -1; +#endif /* CONFIG_IEEE80211W */ /* FT Action frames */ if (nl80211_register_action_frame(drv, (u8 *) "\x06", 1) < 0) diff --git a/wpa_supplicant/events.c b/wpa_supplicant/events.c index a38d8e9e1..406535286 100644 --- a/wpa_supplicant/events.c +++ b/wpa_supplicant/events.c @@ -1641,6 +1641,32 @@ static void ft_rx_action(struct wpa_supplicant *wpa_s, const u8 *data, #endif /* CONFIG_IEEE80211R */ +static void wpa_supplicant_event_unprot_deauth(struct wpa_supplicant *wpa_s, + struct unprot_deauth *e) +{ +#ifdef CONFIG_IEEE80211W + wpa_printf(MSG_DEBUG, "Unprotected Deauthentication frame " + "dropped: " MACSTR " -> " MACSTR + " (reason code %u)", + MAC2STR(e->sa), MAC2STR(e->da), e->reason_code); + sme_event_unprot_disconnect(wpa_s, e->sa, e->da, e->reason_code); +#endif /* CONFIG_IEEE80211W */ +} + + +static void wpa_supplicant_event_unprot_disassoc(struct wpa_supplicant *wpa_s, + struct unprot_disassoc *e) +{ +#ifdef CONFIG_IEEE80211W + wpa_printf(MSG_DEBUG, "Unprotected Disassociation frame " + "dropped: " MACSTR " -> " MACSTR + " (reason code %u)", + MAC2STR(e->sa), MAC2STR(e->da), e->reason_code); + sme_event_unprot_disconnect(wpa_s, e->sa, e->da, e->reason_code); +#endif /* CONFIG_IEEE80211W */ +} + + void wpa_supplicant_event(void *ctx, enum wpa_event_type event, union wpa_event_data *data) { @@ -1886,6 +1912,16 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event, break; } #endif /* CONFIG_IEEE80211R */ +#ifdef CONFIG_IEEE80211W +#ifdef CONFIG_SME + if (data->rx_action.category == WLAN_ACTION_SA_QUERY) { + sme_sa_query_rx(wpa_s, data->rx_action.sa, + data->rx_action.data, + data->rx_action.len); + break; + } +#endif /* CONFIG_SME */ +#endif /* CONFIG_IEEE80211W */ #ifdef CONFIG_P2P wpas_p2p_rx_action(wpa_s, data->rx_action.da, data->rx_action.sa, @@ -1982,6 +2018,14 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event, data->best_chan.freq_overall); #endif /* CONFIG_P2P */ break; + case EVENT_UNPROT_DEAUTH: + wpa_supplicant_event_unprot_deauth(wpa_s, + &data->unprot_deauth); + break; + case EVENT_UNPROT_DISASSOC: + wpa_supplicant_event_unprot_disassoc(wpa_s, + &data->unprot_disassoc); + break; default: wpa_printf(MSG_INFO, "Unknown event %d", event); break; diff --git a/wpa_supplicant/sme.c b/wpa_supplicant/sme.c index 7c4ff12e4..018b37283 100644 --- a/wpa_supplicant/sme.c +++ b/wpa_supplicant/sme.c @@ -15,6 +15,7 @@ #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" @@ -500,3 +501,160 @@ void sme_event_disassoc(struct wpa_supplicant *wpa_s, WLAN_REASON_DEAUTH_LEAVING); } } + + +#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_printf(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_printf(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, wpa_s->bssid, + wpa_s->own_addr, wpa_s->bssid, + req, sizeof(req)) < 0) + wpa_printf(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(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_printf(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); +} + + +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 == 0) + 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_printf(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_printf(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_printf(MSG_DEBUG, "SME: No matching SA Query " + "transaction identifier found"); + return; + } + + wpa_printf(MSG_DEBUG, "SME: Reply to pending SA Query received from " + MACSTR, MAC2STR(sa)); + sme_stop_sa_query(wpa_s); +} + +#endif /* CONFIG_IEEE80211W */ diff --git a/wpa_supplicant/sme.h b/wpa_supplicant/sme.h index 3ec8cc936..b5f150d3e 100644 --- a/wpa_supplicant/sme.h +++ b/wpa_supplicant/sme.h @@ -32,6 +32,11 @@ void sme_event_assoc_timed_out(struct wpa_supplicant *wpa_s, union wpa_event_data *data); void sme_event_disassoc(struct wpa_supplicant *wpa_s, union wpa_event_data *data); +void sme_event_unprot_disconnect(struct wpa_supplicant *wpa_s, const u8 *sa, + const u8 *da, u16 reason_code); +void sme_stop_sa_query(struct wpa_supplicant *wpa_s); +void sme_sa_query_rx(struct wpa_supplicant *wpa_s, const u8 *sa, + const u8 *data, size_t len); #else /* CONFIG_SME */ @@ -73,6 +78,12 @@ static inline void sme_event_disassoc(struct wpa_supplicant *wpa_s, { } +static inline void sme_event_unprot_disconnect(struct wpa_supplicant *wpa_s, + const u8 *sa, const u8 *da, + u16 reason_code) +{ +} + #endif /* CONFIG_SME */ #endif /* SME_H */ diff --git a/wpa_supplicant/wpa_supplicant.c b/wpa_supplicant/wpa_supplicant.c index 7a9001df6..998be62cc 100644 --- a/wpa_supplicant/wpa_supplicant.c +++ b/wpa_supplicant/wpa_supplicant.c @@ -424,6 +424,7 @@ static void wpa_supplicant_cleanup(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; + sme_stop_sa_query(wpa_s); #endif /* CONFIG_SME */ #ifdef CONFIG_AP diff --git a/wpa_supplicant/wpa_supplicant_i.h b/wpa_supplicant/wpa_supplicant_i.h index a61d0e2eb..a6c4a9a44 100644 --- a/wpa_supplicant/wpa_supplicant_i.h +++ b/wpa_supplicant/wpa_supplicant_i.h @@ -447,6 +447,14 @@ struct wpa_supplicant { u8 prev_bssid[ETH_ALEN]; int prev_bssid_set; int auth_alg; + + int sa_query_count; /* number of pending SA Query requests; + * 0 = no SA Query in progress */ + int sa_query_timed_out; + u8 *sa_query_trans_id; /* buffer of WLAN_SA_QUERY_TR_ID_LEN * + * sa_query_count octets of pending + * SA Query transaction identifiers */ + struct os_time sa_query_start; } sme; #endif /* CONFIG_SME */