From 7c2cad969a67453c35422cc6ad1d2803dc27adc1 Mon Sep 17 00:00:00 2001 From: Markus Theil Date: Tue, 30 Jun 2020 14:19:03 +0200 Subject: [PATCH] mesh: Fix DFS deinit/init 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 --- src/ap/dfs.c | 7 +++- src/ap/hostapd.c | 15 +++++-- src/ap/hostapd.h | 6 +++ wpa_supplicant/mesh.c | 94 ++++++++++++++++++++++++++++++++++++++----- 4 files changed, 107 insertions(+), 15 deletions(-) diff --git a/src/ap/dfs.c b/src/ap/dfs.c index f04a00af0..15ef50bce 100644 --- a/src/ap/dfs.c +++ b/src/ap/dfs.c @@ -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"); diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c index 38212305e..1de032930 100644 --- a/src/ap/hostapd.c +++ b/src/ap/hostapd.c @@ -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); diff --git a/src/ap/hostapd.h b/src/ap/hostapd.h index b70d13fba..4ce31416d 100644 --- a/src/ap/hostapd.h +++ b/src/ap/hostapd.h @@ -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); diff --git a/wpa_supplicant/mesh.c b/wpa_supplicant/mesh.c index 1acc8f965..336ae69d0 100644 --- a/wpa_supplicant/mesh.c +++ b/wpa_supplicant/mesh.c @@ -28,15 +28,22 @@ #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)