@@ -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);
@@ -16,7 +16,7 @@
#include "wpa_auth.h"
#include "sta_info.h"
#include "ap_mlme.h"
-
+#include "hostapd.h"
#ifndef CONFIG_NO_HOSTAPD_LOGGER
static const char * mlme_auth_alg_str(int alg)
@@ -80,7 +80,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 == 0)
+ mlme_deletekeys_request(hapd, sta);
}
@@ -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 == 0) {
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 == 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;
+ }
}
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;
@@ -345,6 +345,12 @@ struct hostapd_iface {
unsigned int cs_c_off_proberesp;
int csa_in_progress;
+ /*
+ * when set, indicates that the driver will handle the AP
+ * teardown: delete global keys, station keys and stations
+ */
+ unsigned int driver_ap_teardown:1;
+
#ifdef CONFIG_ACS
unsigned int acs_num_completed_scans;
#endif /* CONFIG_ACS */
@@ -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 == 0 &&
+ !(sta->flags & WLAN_STA_PREAUTH))
hostapd_drv_sta_remove(hapd, sta->addr);
ap_sta_hash_del(hapd, sta);
@@ -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 stations and keys 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
@@ -3808,6 +3808,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;
@@ -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 =
+ !!(iface->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;