aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/drivers/driver.h34
-rw-r--r--src/drivers/driver_nl80211.c47
-rw-r--r--wpa_supplicant/events.c44
-rw-r--r--wpa_supplicant/sme.c158
-rw-r--r--wpa_supplicant/sme.h11
-rw-r--r--wpa_supplicant/wpa_supplicant.c1
-rw-r--r--wpa_supplicant/wpa_supplicant_i.h8
7 files changed, 302 insertions, 1 deletions
diff --git a/src/drivers/driver.h b/src/drivers/driver.h
index 956403a..0360924 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 a385224..38c5f34 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 a38d8e9..4065352 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 7c4ff12..018b372 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 3ec8cc9..b5f150d 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 7a9001d..998be62 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 a61d0e2..a6c4a9a 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 */