diff --git a/src/common/wpa_ctrl.h b/src/common/wpa_ctrl.h index 57d69ae2e..06149ecc8 100644 --- a/src/common/wpa_ctrl.h +++ b/src/common/wpa_ctrl.h @@ -112,6 +112,8 @@ extern "C" { #define WPA_EVENT_SKIP_ROAM "CTRL-EVENT-SKIP-ROAM " /** TID-to-link mapping response event */ #define WPA_EVENT_T2LM_UPDATE "CTRL-EVENT-T2LM-UPDATE " +/** MLO link reconfiguration event */ +#define WPA_EVENT_LINK_RECONFIG "CTRL-EVENT-LINK-RECONFIG " /** IP subnet status change notification * diff --git a/src/drivers/driver.h b/src/drivers/driver.h index c68cc415e..12371a7d8 100644 --- a/src/drivers/driver.h +++ b/src/drivers/driver.h @@ -5664,6 +5664,11 @@ enum wpa_event_type { * Described in wpa_event_data.t2l_map_info. */ EVENT_TID_LINK_MAP, + + /** + * EVENT_LINK_RECONFIG - Notification that AP links removed + */ + EVENT_LINK_RECONFIG, }; diff --git a/src/drivers/driver_common.c b/src/drivers/driver_common.c index 4dda51e73..f3625e8c8 100644 --- a/src/drivers/driver_common.c +++ b/src/drivers/driver_common.c @@ -99,6 +99,7 @@ const char * event_to_string(enum wpa_event_type event) E2S(LINK_CH_SWITCH); E2S(LINK_CH_SWITCH_STARTED); E2S(TID_LINK_MAP); + E2S(LINK_RECONFIG); } return "UNKNOWN"; diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c index d4353541c..beba67417 100644 --- a/src/drivers/driver_nl80211.c +++ b/src/drivers/driver_nl80211.c @@ -281,6 +281,8 @@ void nl80211_mark_disconnected(struct wpa_driver_nl80211_data *drv) drv->pending_roam_data = NULL; os_free(drv->pending_t2lm_data); drv->pending_t2lm_data = NULL; + os_free(drv->pending_link_reconfig_data); + drv->pending_link_reconfig_data = NULL; #endif /* CONFIG_DRIVER_NL80211_QCA */ drv->auth_mld = false; diff --git a/src/drivers/driver_nl80211.h b/src/drivers/driver_nl80211.h index 208c8b043..10a799e0d 100644 --- a/src/drivers/driver_nl80211.h +++ b/src/drivers/driver_nl80211.h @@ -257,6 +257,8 @@ struct wpa_driver_nl80211_data { size_t pending_roam_data_len; u8 *pending_t2lm_data; size_t pending_t2lm_data_len; + u8 *pending_link_reconfig_data; + size_t pending_link_reconfig_data_len; #endif /* CONFIG_DRIVER_NL80211_QCA */ }; diff --git a/src/drivers/driver_nl80211_event.c b/src/drivers/driver_nl80211_event.c index bb3622970..bc1dd4371 100644 --- a/src/drivers/driver_nl80211_event.c +++ b/src/drivers/driver_nl80211_event.c @@ -422,6 +422,78 @@ convert_connect_fail_reason_codes(enum qca_sta_connect_fail_reason_codes } +static void qca_nl80211_link_reconfig_event(struct wpa_driver_nl80211_data *drv, + u8 *data, size_t len) +{ + struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_LINK_RECONFIG_MAX + 1]; + u16 removed_links; + u8 *ap_mld; + int i; + + if (!data) + return; + + if (nla_parse(tb, QCA_WLAN_VENDOR_ATTR_LINK_RECONFIG_MAX, + (struct nlattr *) data, len, NULL) || + !tb[QCA_WLAN_VENDOR_ATTR_LINK_RECONFIG_AP_MLD_ADDR]) + return; + + ap_mld = nla_data(tb[QCA_WLAN_VENDOR_ATTR_LINK_RECONFIG_AP_MLD_ADDR]); + wpa_printf(MSG_DEBUG, "nl80211: AP MLD address " MACSTR + " received in link reconfig event", MAC2STR(ap_mld)); + if (!drv->sta_mlo_info.valid_links || + os_memcmp(drv->sta_mlo_info.ap_mld_addr, ap_mld, ETH_ALEN) != 0) { + if (drv->pending_link_reconfig_data == data) { + wpa_printf(MSG_DEBUG, + "nl80211: Drop pending link reconfig event since AP MLD not matched even after new connect/roam event"); + os_free(drv->pending_link_reconfig_data); + drv->pending_link_reconfig_data = NULL; + return; + } + + wpa_printf(MSG_DEBUG, + "nl80211: Cache new link reconfig event till next connect/roam event"); + if (drv->pending_link_reconfig_data) { + wpa_printf(MSG_DEBUG, "nl80211: Override old link reconfig event data"); + os_free(drv->pending_link_reconfig_data); + } + drv->pending_link_reconfig_data = os_memdup(data, len); + if (!drv->pending_link_reconfig_data) + return; + drv->pending_link_reconfig_data_len = len; + return; + } + + if (!tb[QCA_WLAN_VENDOR_ATTR_LINK_RECONFIG_REMOVED_LINKS]) + return; + removed_links = nla_get_u16( + tb[QCA_WLAN_VENDOR_ATTR_LINK_RECONFIG_REMOVED_LINKS]); + + drv->sta_mlo_info.valid_links &= ~removed_links; + + /* + * Set default BSSID to the BSSID of the lowest link ID of remaining + * links when the link used for (re)association is removed. + */ + if (removed_links & BIT(drv->sta_mlo_info.assoc_link_id)) { + for (i = 0; i < MAX_NUM_MLD_LINKS; i++) { + if (!(drv->sta_mlo_info.valid_links & BIT(i))) + continue; + + os_memcpy(drv->bssid, drv->sta_mlo_info.links[i].bssid, + ETH_ALEN); + drv->sta_mlo_info.assoc_link_id = i; + break; + } + } + + wpa_printf(MSG_DEBUG, "nl80211: Removed MLO links bitmap: 0x%x", + removed_links); + + wpa_supplicant_event(drv->ctx, EVENT_LINK_RECONFIG, NULL); +} + + static void nl80211_parse_qca_vendor_mlo_link_info(struct driver_sta_mlo_info *mlo, struct nlattr *mlo_links) @@ -1040,6 +1112,11 @@ static void mlme_event_connect(struct wpa_driver_nl80211_data *drv, drv->pending_t2lm_data_len); else drv->sta_mlo_info.default_map = true; + + if (drv->pending_link_reconfig_data) + qca_nl80211_link_reconfig_event( + drv, drv->pending_link_reconfig_data, + drv->pending_link_reconfig_data_len); #endif /* CONFIG_DRIVER_NL80211_QCA */ } @@ -2994,6 +3071,9 @@ static void nl80211_vendor_event_qca(struct wpa_driver_nl80211_data *drv, case QCA_NL80211_VENDOR_SUBCMD_TID_TO_LINK_MAP: qca_nl80211_tid_to_link_map_event(drv, data, len); break; + case QCA_NL80211_VENDOR_SUBCMD_LINK_RECONFIG: + qca_nl80211_link_reconfig_event(drv, data, len); + break; #endif /* CONFIG_DRIVER_NL80211_QCA */ default: wpa_printf(MSG_DEBUG, diff --git a/wpa_supplicant/events.c b/wpa_supplicant/events.c index 4af02c1ec..e7aaa1a12 100644 --- a/wpa_supplicant/events.c +++ b/wpa_supplicant/events.c @@ -5307,6 +5307,44 @@ static void wpas_tid_link_map(struct wpa_supplicant *wpa_s, } +static void wpas_link_reconfig(struct wpa_supplicant *wpa_s) +{ + u8 bssid[ETH_ALEN]; + + if (wpa_drv_get_bssid(wpa_s, bssid) < 0) { + wpa_printf(MSG_ERROR, "LINK_RECONFIG: Failed to get BSSID"); + wpa_supplicant_deauthenticate(wpa_s, + WLAN_REASON_DEAUTH_LEAVING); + return; + } + + if (os_memcmp(bssid, wpa_s->bssid, ETH_ALEN) != 0) { + os_memcpy(wpa_s->bssid, bssid, ETH_ALEN); + wpa_supplicant_update_current_bss(wpa_s, wpa_s->bssid); + wpas_notify_bssid_changed(wpa_s); + } + + if (wpa_drv_get_mlo_info(wpa_s) < 0) { + wpa_printf(MSG_ERROR, + "LINK_RECONFIG: Failed to get MLO connection info"); + wpa_supplicant_deauthenticate(wpa_s, + WLAN_REASON_DEAUTH_LEAVING); + return; + } + + if (wpa_sm_set_ml_info(wpa_s)) { + wpa_printf(MSG_ERROR, + "LINK_RECONFIG: Failed to set MLO connection info to wpa_sm"); + wpa_supplicant_deauthenticate(wpa_s, + WLAN_REASON_DEAUTH_LEAVING); + return; + } + + wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_LINK_RECONFIG "valid_links=0x%x", + wpa_s->valid_links); +} + + void wpa_supplicant_event(void *ctx, enum wpa_event_type event, union wpa_event_data *data) { @@ -5406,6 +5444,9 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event, wpas_event_deauth(wpa_s, data ? &data->deauth_info : NULL); break; + case EVENT_LINK_RECONFIG: + wpas_link_reconfig(wpa_s); + break; case EVENT_MICHAEL_MIC_FAILURE: wpa_supplicant_event_michael_mic_failure(wpa_s, data); break;