Delay STA entry removal until Deauth/Disassoc TX status in AP mode
authorJouni Malinen <j@w1.fi>
Sun, 28 Aug 2011 20:07:02 +0000 (23:07 +0300)
committerJouni Malinen <j@w1.fi>
Sun, 28 Aug 2011 20:07:02 +0000 (23:07 +0300)
This allows the driver to use PS buffering of Deauthentication and
Disassociation frames when the STA is in power save sleep. The STA
entry (and PTK) will be removed from the kernel only after the
Deauth/Disassoc has been transmitted (e.g., when the STA wakes up).
A hardcoded two second timeout is used to limit the length of this
window should the driver fail to deliver the frame (e.g., the STA
is out of range and does not wake up). The kernel STA entry is
marked unauthorized during the wait to avoid accepting Data
frames from the STA that we have decided to disconnect.

This behavior is available only with drivers that provide TX status
events for Deauth/Disassoc frames (nl80211 at this point). Other
drivers continue to use the previous behavior where the STA entry
is removed immediately.

src/ap/ieee802_11.c
src/ap/sta_info.c
src/ap/sta_info.h
src/drivers/driver.h
src/drivers/driver_nl80211.c

index c1c4314..e5a0a85 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * hostapd / IEEE 802.11 Management
- * Copyright (c) 2002-2010, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2002-2011, 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
@@ -1752,6 +1752,54 @@ static void handle_assoc_cb(struct hostapd_data *hapd,
 }
 
 
+static void handle_deauth_cb(struct hostapd_data *hapd,
+                            const struct ieee80211_mgmt *mgmt,
+                            size_t len, int ok)
+{
+       struct sta_info *sta;
+       if (mgmt->da[0] & 0x01)
+               return;
+       sta = ap_get_sta(hapd, mgmt->da);
+       if (!sta) {
+               wpa_printf(MSG_DEBUG, "handle_deauth_cb: STA " MACSTR
+                          " not found", MAC2STR(mgmt->da));
+               return;
+       }
+       if (ok)
+               wpa_printf(MSG_DEBUG, "STA " MACSTR " acknowledged deauth",
+                          MAC2STR(sta->addr));
+       else
+               wpa_printf(MSG_DEBUG, "STA " MACSTR " did not acknowledge "
+                          "deauth", MAC2STR(sta->addr));
+
+       ap_sta_deauth_cb(hapd, sta);
+}
+
+
+static void handle_disassoc_cb(struct hostapd_data *hapd,
+                              const struct ieee80211_mgmt *mgmt,
+                              size_t len, int ok)
+{
+       struct sta_info *sta;
+       if (mgmt->da[0] & 0x01)
+               return;
+       sta = ap_get_sta(hapd, mgmt->da);
+       if (!sta) {
+               wpa_printf(MSG_DEBUG, "handle_disassoc_cb: STA " MACSTR
+                          " not found", MAC2STR(mgmt->da));
+               return;
+       }
+       if (ok)
+               wpa_printf(MSG_DEBUG, "STA " MACSTR " acknowledged disassoc",
+                          MAC2STR(sta->addr));
+       else
+               wpa_printf(MSG_DEBUG, "STA " MACSTR " did not acknowledge "
+                          "disassoc", MAC2STR(sta->addr));
+
+       ap_sta_disassoc_cb(hapd, sta);
+}
+
+
 /**
  * ieee802_11_mgmt_cb - Process management frame TX status callback
  * @hapd: hostapd BSS data structure (the BSS from which the management frame
@@ -1784,7 +1832,12 @@ void ieee802_11_mgmt_cb(struct hostapd_data *hapd, const u8 *buf, size_t len,
                wpa_printf(MSG_EXCESSIVE, "mgmt::proberesp cb");
                break;
        case WLAN_FC_STYPE_DEAUTH:
-               /* ignore */
+               wpa_printf(MSG_DEBUG, "mgmt::deauth cb");
+               handle_deauth_cb(hapd, mgmt, len, ok);
+               break;
+       case WLAN_FC_STYPE_DISASSOC:
+               wpa_printf(MSG_DEBUG, "mgmt::disassoc cb");
+               handle_disassoc_cb(hapd, mgmt, len, ok);
                break;
        case WLAN_FC_STYPE_ACTION:
                wpa_printf(MSG_DEBUG, "mgmt::action cb");
index e829447..b42eafb 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * hostapd / Station table
- * Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2002-2011, 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
 static void ap_sta_remove_in_other_bss(struct hostapd_data *hapd,
                                       struct sta_info *sta);
 static void ap_handle_session_timer(void *eloop_ctx, void *timeout_ctx);
+static void ap_sta_deauth_cb_timeout(void *eloop_ctx, void *timeout_ctx);
+static void ap_sta_disassoc_cb_timeout(void *eloop_ctx, void *timeout_ctx);
 #ifdef CONFIG_IEEE80211W
 static void ap_sa_query_timer(void *eloop_ctx, void *timeout_ctx);
 #endif /* CONFIG_IEEE80211W */
+static int ap_sta_remove(struct hostapd_data *hapd, struct sta_info *sta);
 
 int ap_for_each_sta(struct hostapd_data *hapd,
                    int (*cb)(struct hostapd_data *hapd, struct sta_info *sta,
@@ -198,6 +201,8 @@ void ap_free_sta(struct hostapd_data *hapd, struct sta_info *sta)
 
        eloop_cancel_timeout(ap_handle_timer, hapd, sta);
        eloop_cancel_timeout(ap_handle_session_timer, hapd, sta);
+       eloop_cancel_timeout(ap_sta_deauth_cb_timeout, hapd, sta);
+       eloop_cancel_timeout(ap_sta_disassoc_cb_timeout, hapd, sta);
 
        ieee802_1x_free_station(sta);
        wpa_auth_sta_deinit(sta->wpa_sm);
@@ -525,13 +530,23 @@ static void ap_sta_remove_in_other_bss(struct hostapd_data *hapd,
 }
 
 
+static void ap_sta_disassoc_cb_timeout(void *eloop_ctx, void *timeout_ctx)
+{
+       struct hostapd_data *hapd = eloop_ctx;
+       struct sta_info *sta = timeout_ctx;
+
+       ap_sta_remove(hapd, sta);
+       mlme_disassociate_indication(hapd, sta, sta->disassoc_reason);
+}
+
+
 void ap_sta_disassociate(struct hostapd_data *hapd, struct sta_info *sta,
                         u16 reason)
 {
        wpa_printf(MSG_DEBUG, "%s: disassociate STA " MACSTR,
                   hapd->conf->iface, MAC2STR(sta->addr));
        sta->flags &= ~WLAN_STA_ASSOC;
-       ap_sta_remove(hapd, sta);
+       ap_sta_set_authorized(hapd, sta, 0);
        sta->timeout_next = STA_DEAUTH;
        eloop_cancel_timeout(ap_handle_timer, hapd, sta);
        eloop_register_timeout(AP_MAX_INACTIVITY_AFTER_DISASSOC, 0,
@@ -539,7 +554,21 @@ void ap_sta_disassociate(struct hostapd_data *hapd, struct sta_info *sta,
        accounting_sta_stop(hapd, sta);
        ieee802_1x_free_station(sta);
 
-       mlme_disassociate_indication(hapd, sta, reason);
+       sta->disassoc_reason = reason;
+       eloop_cancel_timeout(ap_sta_disassoc_cb_timeout, hapd, sta);
+       eloop_register_timeout(hapd->iface->drv_flags &
+                              WPA_DRIVER_FLAGS_DEAUTH_TX_STATUS ? 2 : 0, 0,
+                              ap_sta_disassoc_cb_timeout, hapd, sta);
+}
+
+
+static void ap_sta_deauth_cb_timeout(void *eloop_ctx, void *timeout_ctx)
+{
+       struct hostapd_data *hapd = eloop_ctx;
+       struct sta_info *sta = timeout_ctx;
+
+       ap_sta_remove(hapd, sta);
+       mlme_deauthenticate_indication(hapd, sta, sta->deauth_reason);
 }
 
 
@@ -549,7 +578,7 @@ void ap_sta_deauthenticate(struct hostapd_data *hapd, struct sta_info *sta,
        wpa_printf(MSG_DEBUG, "%s: deauthenticate STA " MACSTR,
                   hapd->conf->iface, MAC2STR(sta->addr));
        sta->flags &= ~(WLAN_STA_AUTH | WLAN_STA_ASSOC);
-       ap_sta_remove(hapd, sta);
+       ap_sta_set_authorized(hapd, sta, 0);
        sta->timeout_next = STA_REMOVE;
        eloop_cancel_timeout(ap_handle_timer, hapd, sta);
        eloop_register_timeout(AP_MAX_INACTIVITY_AFTER_DEAUTH, 0,
@@ -557,7 +586,11 @@ void ap_sta_deauthenticate(struct hostapd_data *hapd, struct sta_info *sta,
        accounting_sta_stop(hapd, sta);
        ieee802_1x_free_station(sta);
 
-       mlme_deauthenticate_indication(hapd, sta, reason);
+       sta->deauth_reason = reason;
+       eloop_cancel_timeout(ap_sta_deauth_cb_timeout, hapd, sta);
+       eloop_register_timeout(hapd->iface->drv_flags &
+                              WPA_DRIVER_FLAGS_DEAUTH_TX_STATUS ? 2 : 0, 0,
+                              ap_sta_deauth_cb_timeout, hapd, sta);
 }
 
 
@@ -791,6 +824,25 @@ void ap_sta_disconnect(struct hostapd_data *hapd, struct sta_info *sta,
        ap_sta_set_authorized(hapd, sta, 0);
        sta->flags &= ~(WLAN_STA_AUTH | WLAN_STA_ASSOC);
        eloop_cancel_timeout(ap_handle_timer, hapd, sta);
-       eloop_register_timeout(0, 0, ap_handle_timer, hapd, sta);
+       eloop_register_timeout(AP_MAX_INACTIVITY_AFTER_DEAUTH, 0,
+                              ap_handle_timer, hapd, sta);
        sta->timeout_next = STA_REMOVE;
+
+       sta->deauth_reason = reason;
+       eloop_cancel_timeout(ap_sta_deauth_cb_timeout, hapd, sta);
+       eloop_register_timeout(hapd->iface->drv_flags &
+                              WPA_DRIVER_FLAGS_DEAUTH_TX_STATUS ? 2 : 0, 0,
+                              ap_sta_deauth_cb_timeout, hapd, sta);
+}
+
+
+void ap_sta_deauth_cb(struct hostapd_data *hapd, struct sta_info *sta)
+{
+       eloop_cancel_timeout(ap_sta_deauth_cb_timeout, hapd, sta);
+       ap_sta_deauth_cb_timeout(hapd, sta);
+}
+
+
+void ap_sta_disassoc_cb(struct hostapd_data *hapd, struct sta_info *sta)
+{
 }
index 318481f..c5e916f 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * hostapd / Station table
- * Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2002-2011, 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
@@ -66,6 +66,9 @@ struct sta_info {
                STA_NULLFUNC = 0, STA_DISASSOC, STA_DEAUTH, STA_REMOVE
        } timeout_next;
 
+       u16 deauth_reason;
+       u16 disassoc_reason;
+
        /* IEEE 802.1X related data */
        struct eapol_state_machine *eapol_sm;
 
@@ -163,4 +166,7 @@ static inline int ap_sta_is_authorized(struct sta_info *sta)
        return sta->flags & WLAN_STA_AUTHORIZED;
 }
 
+void ap_sta_deauth_cb(struct hostapd_data *hapd, struct sta_info *sta);
+void ap_sta_disassoc_cb(struct hostapd_data *hapd, struct sta_info *sta);
+
 #endif /* STA_INFO_H */
index 39a6ae6..67c5631 100644 (file)
@@ -673,6 +673,8 @@ struct wpa_driver_capa {
 #define WPA_DRIVER_FLAGS_OFFCHANNEL_TX                 0x00008000
 /* Driver indicates TX status events for EAPOL Data frames */
 #define WPA_DRIVER_FLAGS_EAPOL_TX_STATUS               0x00010000
+/* Driver indicates TX status events for Deauth/Disassoc frames */
+#define WPA_DRIVER_FLAGS_DEAUTH_TX_STATUS              0x00020000
        unsigned int flags;
 
        int max_scan_ssids;
index 9959974..e49e714 100644 (file)
@@ -1773,6 +1773,7 @@ static int wpa_driver_nl80211_capa(struct wpa_driver_nl80211_data *drv)
        if (info.p2p_supported)
                drv->capa.flags |= WPA_DRIVER_FLAGS_P2P_CAPABLE;
        drv->capa.flags |= WPA_DRIVER_FLAGS_EAPOL_TX_STATUS;
+       drv->capa.flags |= WPA_DRIVER_FLAGS_DEAUTH_TX_STATUS;
        drv->capa.max_remain_on_chan = info.max_remain_on_chan;
 
        return 0;