]> git.ipfire.org Git - thirdparty/hostap.git/commitdiff
mesh: Fix DFS deinit/init
authorMarkus Theil <markus.theil@tu-ilmenau.de>
Tue, 30 Jun 2020 12:19:03 +0000 (14:19 +0200)
committerJouni Malinen <j@w1.fi>
Mon, 30 Nov 2020 22:31:56 +0000 (00:31 +0200)
The hostapd DFS code deinitializes and initializes the AP interface, if
a clean channel switch is not possible. In this case the AP code paths
would deinit the driver, for example nl80211, without wpa_supplicant
code paths getting notice of this.

Therefore add callbacks for wpa_supplicant mesh methods, which are
called on init/deinit of the AP BSS. These callbacks are then used to
handle the reset in the mesh code.

Signed-off-by: Markus Theil <markus.theil@tu-ilmenau.de>
src/ap/dfs.c
src/ap/hostapd.c
src/ap/hostapd.h
wpa_supplicant/mesh.c

index f04a00af0ed4a92ef1f21b672d30cfba035b8715..15ef50bce860bb20c37bb2b04619aa14f1e8acad 100644 (file)
@@ -1032,6 +1032,7 @@ static int hostapd_dfs_start_channel_switch(struct hostapd_iface *iface)
        int err = 1;
        struct hostapd_hw_modes *cmode = iface->current_mode;
        u8 current_vht_oper_chwidth = hostapd_get_oper_chwidth(iface->conf);
+       int ieee80211_mode = IEEE80211_MODE_AP;
 
        wpa_printf(MSG_DEBUG, "%s called (CAC active: %s, CSA active: %s)",
                   __func__, iface->cac_started ? "yes" : "no",
@@ -1099,6 +1100,10 @@ static int hostapd_dfs_start_channel_switch(struct hostapd_iface *iface)
        os_memset(&csa_settings, 0, sizeof(csa_settings));
        csa_settings.cs_count = 5;
        csa_settings.block_tx = 1;
+#ifdef CONFIG_MESH
+       if (iface->mconf)
+               ieee80211_mode = IEEE80211_MODE_MESH;
+#endif /* CONFIG_MESH */
        err = hostapd_set_freq_params(&csa_settings.freq_params,
                                      iface->conf->hw_mode,
                                      channel->freq,
@@ -1113,7 +1118,7 @@ static int hostapd_dfs_start_channel_switch(struct hostapd_iface *iface)
                                      oper_centr_freq_seg0_idx,
                                      oper_centr_freq_seg1_idx,
                                      cmode->vht_capab,
-                                     &cmode->he_capab[IEEE80211_MODE_AP]);
+                                     &cmode->he_capab[ieee80211_mode]);
 
        if (err) {
                wpa_printf(MSG_ERROR, "DFS failed to calculate CSA freq params");
index 38212305e6bdfd899bf870e1cb61c81d69163b2e..1de0329301b0ef310769f7c32f77d7b0f5880cc9 100644 (file)
@@ -354,7 +354,7 @@ static int hostapd_broadcast_wep_set(struct hostapd_data *hapd)
 #endif /* CONFIG_WEP */
 
 
-static void hostapd_free_hapd_data(struct hostapd_data *hapd)
+void hostapd_free_hapd_data(struct hostapd_data *hapd)
 {
        os_free(hapd->probereq_cb);
        hapd->probereq_cb = NULL;
@@ -498,7 +498,7 @@ static void sta_track_deinit(struct hostapd_iface *iface)
 }
 
 
-static void hostapd_cleanup_iface_partial(struct hostapd_iface *iface)
+void hostapd_cleanup_iface_partial(struct hostapd_iface *iface)
 {
        wpa_printf(MSG_DEBUG, "%s(%p)", __func__, iface);
 #ifdef NEED_AP_MLME
@@ -626,7 +626,7 @@ static int hostapd_flush_old_stations(struct hostapd_data *hapd, u16 reason)
 }
 
 
-static void hostapd_bss_deinit_no_free(struct hostapd_data *hapd)
+void hostapd_bss_deinit_no_free(struct hostapd_data *hapd)
 {
        hostapd_free_stas(hapd);
        hostapd_flush_old_stations(hapd, WLAN_REASON_DEAUTH_LEAVING);
@@ -2690,6 +2690,12 @@ int hostapd_enable_iface(struct hostapd_iface *hapd_iface)
 {
        size_t j;
 
+       if (!hapd_iface)
+               return -1;
+
+       if (hapd_iface->enable_iface_cb)
+               return hapd_iface->enable_iface_cb(hapd_iface);
+
        if (hapd_iface->bss[0]->drv_priv != NULL) {
                wpa_printf(MSG_ERROR, "Interface %s already enabled",
                           hapd_iface->conf->bss[0]->iface);
@@ -2751,6 +2757,9 @@ int hostapd_disable_iface(struct hostapd_iface *hapd_iface)
        if (hapd_iface == NULL)
                return -1;
 
+       if (hapd_iface->disable_iface_cb)
+               return hapd_iface->disable_iface_cb(hapd_iface);
+
        if (hapd_iface->bss[0]->drv_priv == NULL) {
                wpa_printf(MSG_INFO, "Interface %s already disabled",
                           hapd_iface->conf->bss[0]->iface);
index b70d13fbab47c38d0c791007d62bb7f224c8bc10..4ce31416deb7a9bcb8e79e3c1c3873a0903f4a1e 100644 (file)
@@ -589,6 +589,9 @@ struct hostapd_iface {
 
        /* Previous WMM element information */
        struct hostapd_wmm_ac_params prev_wmm[WMM_AC_NUM];
+
+       int (*enable_iface_cb)(struct hostapd_iface *iface);
+       int (*disable_iface_cb)(struct hostapd_iface *iface);
 };
 
 /* hostapd.c */
@@ -617,6 +620,9 @@ void hostapd_interface_deinit_free(struct hostapd_iface *iface);
 int hostapd_enable_iface(struct hostapd_iface *hapd_iface);
 int hostapd_reload_iface(struct hostapd_iface *hapd_iface);
 int hostapd_disable_iface(struct hostapd_iface *hapd_iface);
+void hostapd_bss_deinit_no_free(struct hostapd_data *hapd);
+void hostapd_free_hapd_data(struct hostapd_data *hapd);
+void hostapd_cleanup_iface_partial(struct hostapd_iface *iface);
 int hostapd_add_iface(struct hapd_interfaces *ifaces, char *buf);
 int hostapd_remove_iface(struct hapd_interfaces *ifaces, char *buf);
 void hostapd_channel_list_updated(struct hostapd_iface *iface, int initiator);
index 1acc8f965134ed7a2d59654cab0e444667f9d803..336ae69d0275d312416ffbcb6059af17e5b734b8 100644 (file)
 #include "mesh.h"
 
 
-static void wpa_supplicant_mesh_deinit(struct wpa_supplicant *wpa_s)
+static void wpa_supplicant_mesh_deinit(struct wpa_supplicant *wpa_s,
+                                      bool also_clear_hostapd)
 {
-       wpa_supplicant_mesh_iface_deinit(wpa_s, wpa_s->ifmsh, true);
-       wpa_s->ifmsh = NULL;
-       wpa_s->current_ssid = NULL;
+       wpa_supplicant_mesh_iface_deinit(wpa_s, wpa_s->ifmsh,
+                                        also_clear_hostapd);
+
+       if (also_clear_hostapd) {
+               wpa_s->ifmsh = NULL;
+               wpa_s->current_ssid = NULL;
+               os_free(wpa_s->mesh_params);
+               wpa_s->mesh_params = NULL;
+       }
+
        os_free(wpa_s->mesh_rsn);
        wpa_s->mesh_rsn = NULL;
-       os_free(wpa_s->mesh_params);
-       wpa_s->mesh_params = NULL;
+
        wpa_supplicant_leave_mesh(wpa_s, false);
 }
 
@@ -242,7 +249,7 @@ static int wpas_mesh_complete(struct wpa_supplicant *wpa_s)
                            he_capab)) {
                        wpa_printf(MSG_ERROR,
                                   "Error updating mesh frequency params");
-                       wpa_supplicant_mesh_deinit(wpa_s);
+                       wpa_supplicant_mesh_deinit(wpa_s, true);
                        return -1;
                }
        }
@@ -251,7 +258,7 @@ static int wpas_mesh_complete(struct wpa_supplicant *wpa_s)
            wpas_mesh_init_rsn(wpa_s)) {
                wpa_printf(MSG_ERROR,
                           "mesh: RSN initialization failed - deinit mesh");
-               wpa_supplicant_mesh_iface_deinit(wpa_s, wpa_s->ifmsh, false);
+               wpa_supplicant_mesh_deinit(wpa_s, false);
                return -1;
        }
 
@@ -297,6 +304,69 @@ static void wpas_mesh_complete_cb(void *arg)
 }
 
 
+static int wpa_supplicant_mesh_enable_iface_cb(struct hostapd_iface *ifmsh)
+{
+       struct wpa_supplicant *wpa_s = ifmsh->owner;
+       struct hostapd_data *bss;
+
+       ifmsh->mconf = mesh_config_create(wpa_s, wpa_s->current_ssid);
+
+       bss = ifmsh->bss[0];
+       bss->msg_ctx = wpa_s;
+       os_memcpy(bss->own_addr, wpa_s->own_addr, ETH_ALEN);
+       bss->driver = wpa_s->driver;
+       bss->drv_priv = wpa_s->drv_priv;
+       bss->iface = ifmsh;
+       bss->mesh_sta_free_cb = mesh_mpm_free_sta;
+       bss->setup_complete_cb = wpas_mesh_complete_cb;
+       bss->setup_complete_cb_ctx = wpa_s;
+
+       bss->conf->start_disabled = 1;
+       bss->conf->mesh = MESH_ENABLED;
+       bss->conf->ap_max_inactivity = wpa_s->conf->mesh_max_inactivity;
+
+       if (wpa_drv_init_mesh(wpa_s)) {
+               wpa_msg(wpa_s, MSG_ERROR, "Failed to init mesh in driver");
+               return -1;
+       }
+
+       if (hostapd_setup_interface(ifmsh)) {
+               wpa_printf(MSG_ERROR,
+                          "Failed to initialize hostapd interface for mesh");
+               return -1;
+       }
+
+       return 0;
+}
+
+
+static int wpa_supplicant_mesh_disable_iface_cb(struct hostapd_iface *ifmsh)
+{
+       struct wpa_supplicant *wpa_s = ifmsh->owner;
+       size_t j;
+
+       wpa_supplicant_mesh_deinit(wpa_s, false);
+
+#ifdef NEED_AP_MLME
+       for (j = 0; j < ifmsh->num_bss; j++)
+               hostapd_cleanup_cs_params(ifmsh->bss[j]);
+#endif /* NEED_AP_MLME */
+
+       /* Same as hostapd_interface_deinit() without deinitializing control
+        * interface */
+       for (j = 0; j < ifmsh->num_bss; j++) {
+               struct hostapd_data *hapd = ifmsh->bss[j];
+
+               hostapd_bss_deinit_no_free(hapd);
+               hostapd_free_hapd_data(hapd);
+       }
+
+       hostapd_cleanup_iface_partial(ifmsh);
+
+       return 0;
+}
+
+
 static int wpa_supplicant_mesh_init(struct wpa_supplicant *wpa_s,
                                    struct wpa_ssid *ssid,
                                    struct hostapd_freq_params *freq)
@@ -324,6 +394,8 @@ static int wpa_supplicant_mesh_init(struct wpa_supplicant *wpa_s,
        ifmsh->drv_flags = wpa_s->drv_flags;
        ifmsh->drv_flags2 = wpa_s->drv_flags2;
        ifmsh->num_bss = 1;
+       ifmsh->enable_iface_cb = wpa_supplicant_mesh_enable_iface_cb;
+       ifmsh->disable_iface_cb = wpa_supplicant_mesh_disable_iface_cb;
        ifmsh->bss = os_calloc(wpa_s->ifmsh->num_bss,
                               sizeof(struct hostapd_data *));
        if (!ifmsh->bss)
@@ -459,7 +531,7 @@ static int wpa_supplicant_mesh_init(struct wpa_supplicant *wpa_s,
 
        return 0;
 out_free:
-       wpa_supplicant_mesh_deinit(wpa_s);
+       wpa_supplicant_mesh_deinit(wpa_s, true);
        return -ENOMEM;
 }
 
@@ -507,7 +579,7 @@ int wpa_supplicant_join_mesh(struct wpa_supplicant *wpa_s,
                goto out;
        }
 
-       wpa_supplicant_mesh_deinit(wpa_s);
+       wpa_supplicant_mesh_deinit(wpa_s, true);
 
        wpa_s->pairwise_cipher = WPA_CIPHER_NONE;
        wpa_s->group_cipher = WPA_CIPHER_NONE;
@@ -596,7 +668,7 @@ int wpa_supplicant_leave_mesh(struct wpa_supplicant *wpa_s, bool need_deinit)
 
        /* Need to send peering close messages first */
        if (need_deinit)
-               wpa_supplicant_mesh_deinit(wpa_s);
+               wpa_supplicant_mesh_deinit(wpa_s, true);
 
        ret = wpa_drv_leave_mesh(wpa_s);
        if (ret)