aboutsummaryrefslogtreecommitdiffstats
path: root/wpa_supplicant
diff options
context:
space:
mode:
authorJouni Malinen <j@w1.fi>2010-12-19 09:58:00 (GMT)
committerJouni Malinen <j@w1.fi>2010-12-19 09:58:00 (GMT)
commit7d878ca7698473c0974276c06ed33563b22347eb (patch)
treef982d48efd9be84b63c8fc35e86ad6839dd0c848 /wpa_supplicant
parent5efa9e2a4bc585ba3798a221d52c83d79a4659f9 (diff)
downloadhostap-7d878ca7698473c0974276c06ed33563b22347eb.zip
hostap-7d878ca7698473c0974276c06ed33563b22347eb.tar.gz
hostap-7d878ca7698473c0974276c06ed33563b22347eb.tar.bz2
Use SA Query procedure to recovery from AP/STA state mismatch
If a station received unprotected Deauthentication or Disassociation frame with reason code 6 or 7 from the current AP, there may be a mismatch in association state between the AP and STA. Verify whether this is the case by using SA Query procedure. If not response is received from the AP, deauthenticate. This implementation is only for user space SME with driver_nl80211.c.
Diffstat (limited to 'wpa_supplicant')
-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
5 files changed, 222 insertions, 0 deletions
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 */