From 354c903f8e47ae0fb41fb54ecc018e61d9573506 Mon Sep 17 00:00:00 2001 From: Moshe Benji Date: Wed, 5 Mar 2014 14:55:29 +0200 Subject: [PATCH] AP/GO interface teardown optimization This commit adds an option to optimize AP teardown by leaving the deletion of keys (including group keys) and stations to the driver. This optimization option should be used if the driver supports stations and keys removal when stopping an AP. For example, the optimization option will always be used for cfg80211 drivers since cfg80211 shall always remove stations and keys when stopping an AP (in order to support cases where the AP is disabled without the knowledge of wpa_supplicant/hostapd). Signed-off-by: Moshe Benji --- hostapd/main.c | 6 +++++- src/ap/ap_mlme.c | 4 +++- src/ap/hostapd.c | 41 +++++++++++++++++++++++++++++------- src/ap/hostapd.h | 6 ++++++ src/ap/sta_info.c | 3 ++- src/drivers/driver.h | 3 ++- src/drivers/driver_nl80211.c | 9 ++++++++ wpa_supplicant/ap.c | 3 +++ 8 files changed, 63 insertions(+), 12 deletions(-) diff --git a/hostapd/main.c b/hostapd/main.c index 30269293c..68bc9b582 100644 --- a/hostapd/main.c +++ b/hostapd/main.c @@ -728,8 +728,12 @@ int main(int argc, char *argv[]) out: hostapd_global_ctrl_iface_deinit(&interfaces); /* Deinitialize all interfaces */ - for (i = 0; i < interfaces.count; i++) + for (i = 0; i < interfaces.count; i++) { + interfaces.iface[i]->driver_ap_teardown = + !!(interfaces.iface[i]->drv_flags & + WPA_DRIVER_FLAGS_AP_TEARDOWN_SUPPORT); hostapd_interface_deinit_free(interfaces.iface[i]); + } os_free(interfaces.iface); hostapd_global_deinit(pid_file); diff --git a/src/ap/ap_mlme.c b/src/ap/ap_mlme.c index a9596947f..a7129f14b 100644 --- a/src/ap/ap_mlme.c +++ b/src/ap/ap_mlme.c @@ -16,6 +16,7 @@ #include "wpa_auth.h" #include "sta_info.h" #include "ap_mlme.h" +#include "hostapd.h" #ifndef CONFIG_NO_HOSTAPD_LOGGER @@ -80,7 +81,8 @@ void mlme_deauthenticate_indication(struct hostapd_data *hapd, HOSTAPD_LEVEL_DEBUG, "MLME-DEAUTHENTICATE.indication(" MACSTR ", %d)", MAC2STR(sta->addr), reason_code); - mlme_deletekeys_request(hapd, sta); + if (!hapd->iface->driver_ap_teardown) + mlme_deletekeys_request(hapd, sta); } diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c index ad1c2d039..6ba6f984d 100644 --- a/src/ap/hostapd.c +++ b/src/ap/hostapd.c @@ -350,7 +350,7 @@ static void hostapd_cleanup_iface(struct hostapd_iface *iface) static void hostapd_clear_wep(struct hostapd_data *hapd) { - if (hapd->drv_priv) { + if (hapd->drv_priv && !hapd->iface->driver_ap_teardown) { hostapd_set_privacy(hapd, 0); hostapd_broadcast_wep_clear(hapd); } @@ -401,11 +401,15 @@ static int hostapd_flush_old_stations(struct hostapd_data *hapd, u16 reason) if (hostapd_drv_none(hapd) || hapd->drv_priv == NULL) return 0; - wpa_dbg(hapd->msg_ctx, MSG_DEBUG, "Flushing old station entries"); - if (hostapd_flush(hapd)) { - wpa_msg(hapd->msg_ctx, MSG_WARNING, "Could not connect to " - "kernel driver"); - ret = -1; + if (!hapd->iface->driver_ap_teardown) { + wpa_dbg(hapd->msg_ctx, MSG_DEBUG, + "Flushing old station entries"); + + if (hostapd_flush(hapd)) { + wpa_msg(hapd->msg_ctx, MSG_WARNING, + "Could not connect to kernel driver"); + ret = -1; + } } wpa_dbg(hapd->msg_ctx, MSG_DEBUG, "Deauthenticate all stations"); os_memset(addr, 0xff, ETH_ALEN); @@ -1009,6 +1013,15 @@ static int setup_interface(struct hostapd_iface *iface) struct hostapd_data *hapd = iface->bss[0]; size_t i; + /* + * It is possible that setup_interface() is called after the interface + * was disabled etc., in which case driver_ap_teardown is possibly set + * to 1. Clear it here so any other key/station deletion, which is not + * part of a teardown flow, would also call the relevant driver + * callbacks. + */ + iface->driver_ap_teardown = 0; + if (!iface->phy[0]) { const char *phy = hostapd_drv_get_radio_name(hapd); if (phy) { @@ -1627,7 +1640,11 @@ int hostapd_disable_iface(struct hostapd_iface *hapd_iface) driver = hapd_iface->bss[0]->driver; drv_priv = hapd_iface->bss[0]->drv_priv; - /* whatever hostapd_interface_deinit does */ + hapd_iface->driver_ap_teardown = + !!(hapd_iface->drv_flags & + WPA_DRIVER_FLAGS_AP_TEARDOWN_SUPPORT); + + /* same as hostapd_interface_deinit without deinitializing ctrl-iface */ for (j = 0; j < hapd_iface->num_bss; j++) { struct hostapd_data *hapd = hapd_iface->bss[j]; hostapd_free_stas(hapd); @@ -1943,6 +1960,10 @@ int hostapd_remove_iface(struct hapd_interfaces *interfaces, char *buf) return -1; if (!os_strcmp(hapd_iface->conf->bss[0]->iface, buf)) { wpa_printf(MSG_INFO, "Remove interface '%s'", buf); + hapd_iface->driver_ap_teardown = + !!(hapd_iface->drv_flags & + WPA_DRIVER_FLAGS_AP_TEARDOWN_SUPPORT); + hostapd_interface_deinit_free(hapd_iface); k = i; while (k < (interfaces->count - 1)) { @@ -1955,8 +1976,12 @@ int hostapd_remove_iface(struct hapd_interfaces *interfaces, char *buf) } for (j = 0; j < hapd_iface->conf->num_bss; j++) { - if (!os_strcmp(hapd_iface->conf->bss[j]->iface, buf)) + if (!os_strcmp(hapd_iface->conf->bss[j]->iface, buf)) { + hapd_iface->driver_ap_teardown = + !(hapd_iface->drv_flags & + WPA_DRIVER_FLAGS_AP_TEARDOWN_SUPPORT); return hostapd_remove_bss(hapd_iface, j); + } } } return -1; diff --git a/src/ap/hostapd.h b/src/ap/hostapd.h index 489ab1652..be7df51b7 100644 --- a/src/ap/hostapd.h +++ b/src/ap/hostapd.h @@ -273,6 +273,12 @@ struct hostapd_iface { unsigned int wait_channel_update:1; unsigned int cac_started:1; + /* + * When set, indicates that the driver will handle the AP + * teardown: delete global keys, station keys, and stations. + */ + unsigned int driver_ap_teardown:1; + int num_ap; /* number of entries in ap_list */ struct ap_info *ap_list; /* AP info list head */ struct ap_info *ap_hash[STA_HASH_SIZE]; diff --git a/src/ap/sta_info.c b/src/ap/sta_info.c index f7af0889f..0b90e3b23 100644 --- a/src/ap/sta_info.c +++ b/src/ap/sta_info.c @@ -156,7 +156,8 @@ void ap_free_sta(struct hostapd_data *hapd, struct sta_info *sta) if (sta->flags & WLAN_STA_WDS) hostapd_set_wds_sta(hapd, NULL, sta->addr, sta->aid, 0); - if (!(sta->flags & WLAN_STA_PREAUTH)) + if (!hapd->iface->driver_ap_teardown && + !(sta->flags & WLAN_STA_PREAUTH)) hostapd_drv_sta_remove(hapd, sta->addr); ap_sta_hash_del(hapd, sta); diff --git a/src/drivers/driver.h b/src/drivers/driver.h index b6434c294..6b6c0efe4 100644 --- a/src/drivers/driver.h +++ b/src/drivers/driver.h @@ -914,7 +914,8 @@ struct wpa_driver_capa { #define WPA_DRIVER_FLAGS_P2P_DEDICATED_INTERFACE 0x00000400 /* This interface is P2P capable (P2P GO or P2P Client) */ #define WPA_DRIVER_FLAGS_P2P_CAPABLE 0x00000800 -/* unused: 0x00001000 */ +/* Driver supports station and key removal when stopping an AP */ +#define WPA_DRIVER_FLAGS_AP_TEARDOWN_SUPPORT 0x00001000 /* * Driver uses the initial interface for P2P management interface and non-P2P * purposes (e.g., connect to infra AP), but this interface cannot be used for diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c index 6a2af9511..87c9661de 100644 --- a/src/drivers/driver_nl80211.c +++ b/src/drivers/driver_nl80211.c @@ -3818,6 +3818,15 @@ static int wpa_driver_nl80211_capa(struct wpa_driver_nl80211_data *drv) drv->capa.flags |= WPA_DRIVER_FLAGS_SET_KEYS_AFTER_ASSOC_DONE; drv->capa.flags |= WPA_DRIVER_FLAGS_EAPOL_TX_STATUS; + /* + * As all cfg80211 drivers must support cases where the AP interface is + * removed without the knowledge of wpa_supplicant/hostapd, e.g., in + * case that the user space daemon has crashed, they must be able to + * cleanup all stations and key entries in the AP tear down flow. Thus, + * this flag can/should always be set for cfg80211 drivers. + */ + drv->capa.flags |= WPA_DRIVER_FLAGS_AP_TEARDOWN_SUPPORT; + if (!info.device_ap_sme) { drv->capa.flags |= WPA_DRIVER_FLAGS_DEAUTH_TX_STATUS; diff --git a/wpa_supplicant/ap.c b/wpa_supplicant/ap.c index f150679e7..af2730370 100644 --- a/wpa_supplicant/ap.c +++ b/wpa_supplicant/ap.c @@ -669,6 +669,9 @@ void wpa_supplicant_ap_deinit(struct wpa_supplicant *wpa_s) wpa_s->ap_iface->bss[0]->p2p_group = NULL; wpas_p2p_group_deinit(wpa_s); #endif /* CONFIG_P2P */ + wpa_s->ap_iface->driver_ap_teardown = + !!(wpa_s->drv_flags & WPA_DRIVER_FLAGS_AP_TEARDOWN_SUPPORT); + hostapd_interface_deinit(wpa_s->ap_iface); hostapd_interface_free(wpa_s->ap_iface); wpa_s->ap_iface = NULL;