Update below MLO Link info of the current connection to wpa_sm: - AP MLD address and link ID of the (re)association link. - For each requested link - own link address - AP's link bssid, RSNE, RSNXE Get the requested MLO links info from driver if available. Otherwise, parse Multi-Link element in Association request and response IEs and determine the required MLO connection information. Signed-off-by: Veerendranath Jakkam <quic_vjakkam@xxxxxxxxxxx> --- src/drivers/driver.h | 3 +- src/drivers/driver_nl80211_event.c | 1 + src/rsn_supp/wpa.c | 74 ++++++++++ src/rsn_supp/wpa.h | 8 ++ src/rsn_supp/wpa_i.h | 16 +++ wpa_supplicant/events.c | 209 +++++++++++++++++++++++++++++ wpa_supplicant/wpa_supplicant.c | 4 + 7 files changed, 314 insertions(+), 1 deletion(-) diff --git a/src/drivers/driver.h b/src/drivers/driver.h index 9132409c1..82fec2174 100644 --- a/src/drivers/driver.h +++ b/src/drivers/driver.h @@ -2741,7 +2741,8 @@ struct weighted_pcl { }; struct driver_sta_mlo_info { - u16 valid_links; /* bitmap of valid link IDs */ + u16 req_links; /* bitmap of requested link IDs */ + u16 valid_links; /* bitmap of accepted link IDs */ u8 assoc_link_id; u8 ap_mld_addr[ETH_ALEN]; struct { diff --git a/src/drivers/driver_nl80211_event.c b/src/drivers/driver_nl80211_event.c index 929bb1888..289a9c734 100644 --- a/src/drivers/driver_nl80211_event.c +++ b/src/drivers/driver_nl80211_event.c @@ -483,6 +483,7 @@ static void nl80211_parse_mlo_link_info(struct driver_sta_mlo_info *mlo, continue; if (tb[NL80211_ATTR_STATUS_CODE]) { + mlo->req_links |= BIT(link_id); if (nla_get_u16(tb[NL80211_ATTR_STATUS_CODE]) == WLAN_STATUS_SUCCESS) mlo->valid_links |= BIT(link_id); diff --git a/src/rsn_supp/wpa.c b/src/rsn_supp/wpa.c index 5f305b897..26e426b98 100644 --- a/src/rsn_supp/wpa.c +++ b/src/rsn_supp/wpa.c @@ -3006,6 +3006,8 @@ struct wpa_sm * wpa_sm_init(struct wpa_sm_ctx *ctx) */ void wpa_sm_deinit(struct wpa_sm *sm) { + int i; + if (sm == NULL) return; pmksa_cache_deinit(sm->pmksa); @@ -3016,6 +3018,10 @@ void wpa_sm_deinit(struct wpa_sm *sm) os_free(sm->ap_wpa_ie); os_free(sm->ap_rsn_ie); os_free(sm->ap_rsnxe); + for (i = 0; i < MAX_NUM_MLD_LINKS; i++) { + os_free(sm->mlo.links[i].ap_rsne); + os_free(sm->mlo.links[i].ap_rsnxe); + } wpa_sm_drop_sa(sm); os_free(sm->ctx); #ifdef CONFIG_IEEE80211R @@ -3303,6 +3309,74 @@ void wpa_sm_set_config(struct wpa_sm *sm, struct rsn_supp_config *config) } } +int wpa_sm_set_mlo_params(struct wpa_sm *sm, const struct wpa_sm_mlo *mlo) +{ + int i; + + if (!sm) + return -1; + + os_memcpy(sm->mlo.ap_mld_addr, mlo->ap_mld_addr, ETH_ALEN); + sm->mlo.assoc_link_id = mlo->assoc_link_id; + sm->mlo.valid_links = mlo->valid_links; + sm->mlo.req_links = mlo->req_links; + for (i = 0; i < MAX_NUM_MLD_LINKS; i++) { + const u8 *ie; + size_t len; + + if (sm->mlo.req_links & BIT(i)) { + if (mlo->links[i].ap_rsne == NULL || + mlo->links[i].ap_rsne_len == 0) { + wpa_dbg(sm->ctx->msg_ctx, MSG_ERROR, + "RSN: No RSN IE for AP MLO link %d with bssid " MACSTR, + i, MAC2STR(mlo->links[i].bssid)); + return -1; + + } + os_memcpy(sm->mlo.links[i].addr, mlo->links[i].addr, + ETH_ALEN); + os_memcpy(sm->mlo.links[i].bssid, mlo->links[i].bssid, + ETH_ALEN); + } + + ie = mlo->links[i].ap_rsne; + len = mlo->links[i].ap_rsne_len; + os_free(sm->mlo.links[i].ap_rsne); + if (ie == NULL || len == 0) { + wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, + "RSN: clearing MLO link[%u] AP RSNE", i); + sm->mlo.links[i].ap_rsne = NULL; + sm->mlo.links[i].ap_rsne_len = 0; + } else { + wpa_hexdump_link(MSG_DEBUG, i, "RSN: set AP RSNE", ie, + len); + sm->mlo.links[i].ap_rsne = os_memdup(ie, len); + if (!sm->mlo.links[i].ap_rsne) + return -1; + sm->mlo.links[i].ap_rsne_len = len; + } + + ie = mlo->links[i].ap_rsnxe; + len = mlo->links[i].ap_rsnxe_len; + os_free(sm->mlo.links[i].ap_rsnxe); + if (ie == NULL || len == 0) { + wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, + "RSN: clearing MLO link[%u] AP RSNXE", i); + sm->mlo.links[i].ap_rsnxe = NULL; + sm->mlo.links[i].ap_rsnxe_len = 0; + } else { + wpa_hexdump_link(MSG_DEBUG, i, "RSN: set AP RSNXE", ie, + len); + sm->mlo.links[i].ap_rsnxe = os_memdup(ie, len); + if (!sm->mlo.links[i].ap_rsnxe) + return -1; + sm->mlo.links[i].ap_rsnxe_len = len; + } + } + + return 0; +} + /** * wpa_sm_set_own_addr - Set own MAC address diff --git a/src/rsn_supp/wpa.h b/src/rsn_supp/wpa.h index 8449db761..216573131 100644 --- a/src/rsn_supp/wpa.h +++ b/src/rsn_supp/wpa.h @@ -20,6 +20,7 @@ struct wpa_config_blob; struct hostapd_freq_params; struct wpa_channel_info; enum frame_encryption; +struct wpa_sm_mlo; struct wpa_sm_ctx { void *ctx; /* pointer to arbitrary upper level context */ @@ -224,6 +225,7 @@ void wpa_sm_set_ptk_kck_kek(struct wpa_sm *sm, const u8 *ptk_kek, size_t ptk_kek_len); int wpa_fils_is_completed(struct wpa_sm *sm); void wpa_sm_pmksa_cache_reconfig(struct wpa_sm *sm); +int wpa_sm_set_mlo_params(struct wpa_sm *sm, const struct wpa_sm_mlo *mlo); #else /* CONFIG_NO_WPA */ @@ -438,6 +440,12 @@ static inline void wpa_sm_pmksa_cache_reconfig(struct wpa_sm *sm) { } +static inline int wpa_sm_set_mlo_params(struct wpa_sm *sm, + const struct wpa_sm_mlo *mlo) +{ + return 0; +} + #endif /* CONFIG_NO_WPA */ #ifdef CONFIG_IEEE80211R diff --git a/src/rsn_supp/wpa_i.h b/src/rsn_supp/wpa_i.h index 3811c3bc4..3c78cc1b0 100644 --- a/src/rsn_supp/wpa_i.h +++ b/src/rsn_supp/wpa_i.h @@ -19,6 +19,21 @@ struct pasn_ft_r1kh { u8 r1kh_id[FT_R1KH_ID_LEN]; }; +struct wpa_sm_link { + u8 addr[ETH_ALEN]; + u8 bssid[ETH_ALEN]; + u8 *ap_rsne, *ap_rsnxe; + size_t ap_rsne_len, ap_rsnxe_len; +} links[MAX_NUM_MLD_LINKS]; + +struct wpa_sm_mlo { + u8 ap_mld_addr[ETH_ALEN]; + u8 assoc_link_id; + u16 valid_links; /* bitmap of accepted links */ + u16 req_links; /* bitmap of requested links */ + struct wpa_sm_link links[MAX_NUM_MLD_LINKS]; +}; + /** * struct wpa_sm - Internal WPA state machine data */ @@ -218,6 +233,7 @@ struct wpa_sm { struct wpabuf *dpp_z; int dpp_pfs; #endif /* CONFIG_DPP2 */ + struct wpa_sm_mlo mlo; }; diff --git a/wpa_supplicant/events.c b/wpa_supplicant/events.c index f3cbe9755..9374133d4 100644 --- a/wpa_supplicant/events.c +++ b/wpa_supplicant/events.c @@ -50,6 +50,7 @@ #include "mesh_mpm.h" #include "wmm_ac.h" #include "dpp_supplicant.h" +#include "rsn_supp/wpa_i.h" #define MAX_OWE_TRANSITION_BSS_SELECT_COUNT 5 @@ -3410,6 +3411,205 @@ static int wpa_drv_get_mlo_info(struct wpa_supplicant *wpa_s) } +struct links_info { + /* bitmap of link IDs in Per-STA profile subelements*/ + u16 non_assoc_links; + u8 addr[MAX_NUM_MLD_LINKS][ETH_ALEN]; +}; + + +static void wpas_get_basic_mle_links_info(const u8 *mle, size_t mle_len, + struct links_info *info) +{ + size_t rem_len; + const u8 *pos; + + if (mle_len < (MULTI_LINK_CONTROL_LEN + 1) || + (mle_len - MULTI_LINK_CONTROL_LEN) < mle[MULTI_LINK_CONTROL_LEN]) + return; + + // Skip Common Info + pos = mle + MULTI_LINK_CONTROL_LEN + mle[MULTI_LINK_CONTROL_LEN]; + rem_len = mle_len - + (MULTI_LINK_CONTROL_LEN + mle[MULTI_LINK_CONTROL_LEN]); + + // Parse Subelements + while (rem_len > 2) { + int ie_len = 2 + pos[1]; + + if (rem_len < ie_len) + return; + + if (pos[0] == MULTI_LINK_SUB_ELEM_ID_PER_STA_PROFILE) { + u8 link_id; + const u8 *sta_profile; + + if (pos[1] < + (BASIC_MLE_STA_PROF_STA_MAC_IDX + ETH_ALEN)) + goto next_subelem; + + sta_profile = &pos[2]; + link_id = sta_profile[0] & + BASIC_MLE_STA_CTRL0_LINK_ID_MASK; + if (link_id >= MAX_NUM_MLD_LINKS) + goto next_subelem; + + if (!(sta_profile[0] & + BASIC_MLE_STA_CTRL0_PRES_STA_MAC)) + goto next_subelem; + + info->non_assoc_links |= BIT(link_id); + os_memcpy(info->addr[link_id], + &sta_profile[BASIC_MLE_STA_PROF_STA_MAC_IDX], + ETH_ALEN); + } +next_subelem: + pos += ie_len; + rem_len -= ie_len; + } +} + + +static int wpas_get_ml_req_links_info(struct wpa_supplicant *wpa_s, + union wpa_event_data *data, + struct driver_sta_mlo_info *drv_mlo) +{ + int i; + struct wpabuf *mle; + struct ieee802_11_elems req_elems, resp_elems; + struct links_info req_info, resp_info; + + if (!data || !data->assoc_info.req_ies || !data->assoc_info.resp_ies) { + wpa_dbg(wpa_s, MSG_ERROR, + "MLO: Association request and/or response IEs not present"); + return -1; + } + + if (ieee802_11_parse_elems(data->assoc_info.resp_ies, + data->assoc_info.resp_ies_len, &resp_elems, + 0) == ParseFailed || + ieee802_11_parse_elems(data->assoc_info.req_ies, + data->assoc_info.req_ies_len, &req_elems, + 0) == ParseFailed) { + wpa_dbg(wpa_s, MSG_ERROR, + "MLO: Failed to parse Association request/response IEs"); + return -1; + } + + mle = ieee802_11_defrag_mle(&req_elems, MULTI_LINK_CONTROL_TYPE_BASIC); + if (!mle) { + wpa_dbg(wpa_s, MSG_ERROR, + "MLO: Basic Multi-Link element not found in Association request"); + return -1; + } + os_memset(&req_info, 0, sizeof(req_info)); + wpas_get_basic_mle_links_info((const u8 *) wpabuf_head(mle), + wpabuf_len(mle), &req_info); + wpabuf_free(mle); + + mle = ieee802_11_defrag_mle(&resp_elems, MULTI_LINK_CONTROL_TYPE_BASIC); + if (!mle) { + wpa_dbg(wpa_s, MSG_ERROR, + "MLO: Basic Multi-Link element not found in Association response"); + return -1; + } + os_memset(&resp_info, 0, sizeof(resp_info)); + wpas_get_basic_mle_links_info((const u8 *) wpabuf_head(mle), + wpabuf_len(mle), &resp_info); + wpabuf_free(mle); + + if (req_info.non_assoc_links != resp_info.non_assoc_links) { + wpa_dbg(wpa_s, MSG_ERROR, + "MLO: Association request and response links bitmap not equal"); + return -1; + } + + drv_mlo->req_links = BIT(drv_mlo->assoc_link_id) | + req_info.non_assoc_links; + if ((drv_mlo->req_links & drv_mlo->valid_links) != + drv_mlo->valid_links) { + wpa_dbg(wpa_s, MSG_ERROR, + "MLO: accepted links are not subset of requested links"); + return -1; + } + + /* Get MLO links info for rejected links */ + for (i = 0; i < MAX_NUM_MLD_LINKS; i++) { + if (!((drv_mlo->req_links & ~drv_mlo->valid_links) & BIT(i))) + continue; + + os_memcpy(drv_mlo->links[i].bssid, resp_info.addr[i], ETH_ALEN); + os_memcpy(drv_mlo->links[i].addr, req_info.addr[i], ETH_ALEN); + } + + return 0; +} + +static int wpa_sm_set_ml_info(struct wpa_supplicant *wpa_s, + union wpa_event_data *data) +{ + struct driver_sta_mlo_info drv_mlo; + struct wpa_sm_mlo wpa_mlo; + const u8 *bss_rsn = NULL, *bss_rsnx = NULL; + int i; + + drv_mlo.valid_links = 0; + drv_mlo.req_links = 0; + if (wpas_drv_get_sta_mlo_info(wpa_s, &drv_mlo)) { + wpa_dbg(wpa_s, MSG_ERROR, "Failed to get MLO link info"); + return -1; + } + + os_memset(&wpa_mlo, 0, sizeof(wpa_mlo)); + if (!drv_mlo.valid_links) + goto out; + + if (!drv_mlo.req_links && + wpas_get_ml_req_links_info(wpa_s, data, &drv_mlo)) + return -1; + + os_memcpy(wpa_mlo.ap_mld_addr, drv_mlo.ap_mld_addr, ETH_ALEN); + wpa_mlo.assoc_link_id = drv_mlo.assoc_link_id; + wpa_mlo.valid_links = drv_mlo.valid_links; + wpa_mlo.req_links = drv_mlo.req_links; + for (i = 0; i < MAX_NUM_MLD_LINKS; i++) { + struct wpa_bss *bss; + + if (!(drv_mlo.req_links & BIT(i))) + continue; + + bss = wpa_supplicant_get_new_bss(wpa_s, drv_mlo.links[i].bssid); + if (!bss) { + wpa_supplicant_update_scan_results(wpa_s); + bss = wpa_supplicant_get_new_bss( + wpa_s, drv_mlo.links[i].bssid); + } + + if (!bss) { + wpa_dbg(wpa_s, MSG_ERROR, + "Failed to get MLO link %d bss", i); + return -1; + } + + bss_rsn = wpa_bss_get_ie(bss, WLAN_EID_RSN); + bss_rsnx = wpa_bss_get_ie(bss, WLAN_EID_RSNX); + + wpa_mlo.links[i].ap_rsne = bss_rsn ? (u8 *) bss_rsn : NULL; + wpa_mlo.links[i].ap_rsne_len = bss_rsn ? 2 + bss_rsn[1] : 0; + wpa_mlo.links[i].ap_rsnxe = bss_rsnx ? (u8 *) bss_rsnx : NULL; + wpa_mlo.links[i].ap_rsnxe_len = bss_rsnx ? 2 + bss_rsnx[1] : 0; + + os_memcpy(wpa_mlo.links[i].bssid, drv_mlo.links[i].bssid, + ETH_ALEN); + os_memcpy(wpa_mlo.links[i].addr, drv_mlo.links[i].addr, + ETH_ALEN); + } + +out: + return wpa_sm_set_mlo_params(wpa_s->wpa, &wpa_mlo); +} + + static void wpa_supplicant_event_assoc(struct wpa_supplicant *wpa_s, union wpa_event_data *data) { @@ -3534,6 +3734,15 @@ static void wpa_supplicant_event_assoc(struct wpa_supplicant *wpa_s, wpa_supplicant_scard_init(wpa_s, wpa_s->current_ssid); } wpa_sm_notify_assoc(wpa_s->wpa, bssid); + + if (wpa_sm_set_ml_info(wpa_s, data)) { + wpa_dbg(wpa_s, MSG_ERROR, + "Failed to set MLO connection info to wpa_sm"); + wpa_supplicant_deauthenticate(wpa_s, + WLAN_REASON_DEAUTH_LEAVING); + return; + } + if (wpa_s->l2) l2_packet_notify_auth_start(wpa_s->l2); diff --git a/wpa_supplicant/wpa_supplicant.c b/wpa_supplicant/wpa_supplicant.c index 3d7626aeb..bfdc23203 100644 --- a/wpa_supplicant/wpa_supplicant.c +++ b/wpa_supplicant/wpa_supplicant.c @@ -69,6 +69,7 @@ #include "ap/ap_config.h" #include "ap/hostapd.h" #endif /* CONFIG_MESH */ +#include "rsn_supp/wpa_i.h" const char *const wpa_supplicant_version = "wpa_supplicant v" VERSION_STR "\n" @@ -403,6 +404,7 @@ void wpa_supplicant_set_non_wpa_policy(struct wpa_supplicant *wpa_s, #ifdef CONFIG_WEP int i; #endif /* CONFIG_WEP */ + struct wpa_sm_mlo mlo; if (ssid->key_mgmt & WPA_KEY_MGMT_WPS) wpa_s->key_mgmt = WPA_KEY_MGMT_WPS; @@ -443,6 +445,8 @@ void wpa_supplicant_set_non_wpa_policy(struct wpa_supplicant *wpa_s, wpa_s->mgmt_group_cipher); pmksa_cache_clear_current(wpa_s->wpa); + os_memset(&mlo, 0, sizeof(mlo)); + wpa_sm_set_mlo_params(wpa_s->wpa, &mlo); } -- 2.25.1 _______________________________________________ Hostap mailing list Hostap@xxxxxxxxxxxxxxxxxxx http://lists.infradead.org/mailman/listinfo/hostap