AP/GO interface teardown optimization
authorMoshe Benji <Moshe.Benji@intel.com>
Wed, 5 Mar 2014 12:55:29 +0000 (14:55 +0200)
committerJouni Malinen <j@w1.fi>
Wed, 5 Mar 2014 21:57:02 +0000 (23:57 +0200)
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 <moshe.benji@intel.com>
hostapd/main.c
src/ap/ap_mlme.c
src/ap/hostapd.c
src/ap/hostapd.h
src/ap/sta_info.c
src/drivers/driver.h
src/drivers/driver_nl80211.c
wpa_supplicant/ap.c

index 3026929..68bc9b5 100644 (file)
@@ -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);
index a959694..a7129f1 100644 (file)
@@ -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);
 }
 
 
index ad1c2d0..6ba6f98 100644 (file)
@@ -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;
index 489ab16..be7df51 100644 (file)
@@ -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];
index f7af088..0b90e3b 100644 (file)
@@ -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);
index b6434c2..6b6c0ef 100644 (file)
@@ -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
index 6a2af95..87c9661 100644 (file)
@@ -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;
 
index f150679..af27303 100644 (file)
@@ -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;