Delay STA entry removal until Deauth/Disassoc TX status in AP mode

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.
This commit is contained in:
Jouni Malinen 2011-08-28 23:07:02 +03:00
parent 49a191a142
commit 4dc03726de
5 changed files with 123 additions and 9 deletions

View 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");

View 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
@ -38,9 +38,12 @@
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)
{
}

View 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 */

View 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;

View 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;