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;