diff --git a/wpa_supplicant/bss.c b/wpa_supplicant/bss.c index 401c5342e..cd2c164c3 100644 --- a/wpa_supplicant/bss.c +++ b/wpa_supplicant/bss.c @@ -1445,3 +1445,21 @@ int wpa_bss_ext_capab(const struct wpa_bss *bss, unsigned int capab) return ieee802_11_ext_capab(wpa_bss_get_ie(bss, WLAN_EID_EXT_CAPAB), capab); } + + +/** + * wpa_bss_defrag_mle - Get a buffer holding a de-fragmented ML element + * @bss: BSS table entry + * @type: ML control type + */ +struct wpabuf * wpa_bss_defrag_mle(const struct wpa_bss *bss, u8 type) +{ + struct ieee802_11_elems elems; + const u8 *pos = wpa_bss_ie_ptr(bss); + size_t len = bss->ie_len; + + if (ieee802_11_parse_elems(pos, len, &elems, 1) == ParseFailed) + return NULL; + + return ieee802_11_defrag_mle(&elems, type); +} diff --git a/wpa_supplicant/bss.h b/wpa_supplicant/bss.h index b68fc879d..611da8844 100644 --- a/wpa_supplicant/bss.h +++ b/wpa_supplicant/bss.h @@ -201,4 +201,6 @@ void calculate_update_time(const struct os_reltime *fetch_time, unsigned int age_ms, struct os_reltime *update_time); +struct wpabuf * wpa_bss_defrag_mle(const struct wpa_bss *bss, u8 type); + #endif /* BSS_H */ diff --git a/wpa_supplicant/events.c b/wpa_supplicant/events.c index 1d228f4b7..a0b8bcfb3 100644 --- a/wpa_supplicant/events.c +++ b/wpa_supplicant/events.c @@ -308,7 +308,7 @@ void wpa_supplicant_stop_countermeasures(void *eloop_ctx, void *sock_ctx) } -static void wpas_reset_mlo_info(struct wpa_supplicant *wpa_s) +void wpas_reset_mlo_info(struct wpa_supplicant *wpa_s) { if (!wpa_s->valid_links) return; diff --git a/wpa_supplicant/sme.c b/wpa_supplicant/sme.c index dce8a5563..7d44fe0fa 100644 --- a/wpa_supplicant/sme.c +++ b/wpa_supplicant/sme.c @@ -370,6 +370,188 @@ static void sme_auth_handle_rrm(struct wpa_supplicant *wpa_s, } +static bool wpas_ml_element(struct wpa_supplicant *wpa_s, struct wpa_bss *bss) +{ + struct wpabuf *mlbuf; + const u8 *rnr_ie, *pos; + u8 ml_ie_len, rnr_ie_len; + const struct ieee80211_eht_ml *eht_ml; + const struct eht_ml_basic_common_info *ml_basic_common_info; + u8 i; + const u16 control = + host_to_le16(MULTI_LINK_CONTROL_TYPE_BASIC | + BASIC_MULTI_LINK_CTRL_PRES_LINK_ID | + BASIC_MULTI_LINK_CTRL_PRES_BSS_PARAM_CH_COUNT | + BASIC_MULTI_LINK_CTRL_PRES_MLD_CAPA); + bool ret = false; + + if (!(wpa_s->drv_flags2 & WPA_DRIVER_FLAGS2_MLO)) + return false; + + mlbuf = wpa_bss_defrag_mle(bss, MULTI_LINK_CONTROL_TYPE_BASIC); + if (!mlbuf) { + wpa_dbg(wpa_s, MSG_DEBUG, "MLD: No ML element"); + return false; + } + + ml_ie_len = wpabuf_len(mlbuf); + + /* control + common info len + MLD address + MLD link information */ + if (ml_ie_len < 2 + 1 + ETH_ALEN + 1) + goto out; + + eht_ml = wpabuf_head(mlbuf); + if ((eht_ml->ml_control & control) != control) { + wpa_printf(MSG_DEBUG, "MLD: Unexpected ML element control=0x%x", + eht_ml->ml_control); + goto out; + } + + ml_basic_common_info = + (const struct eht_ml_basic_common_info *) eht_ml->variable; + + /* common info length should be valid (self, mld_addr, link_id) */ + if (ml_basic_common_info->len < 1 + ETH_ALEN + 1) + goto out; + + /* get the MLD address and MLD link ID */ + os_memcpy(wpa_s->ap_mld_addr, ml_basic_common_info->mld_addr, + ETH_ALEN); + wpa_s->mlo_assoc_link_id = ml_basic_common_info->variable[0] & + EHT_ML_LINK_ID_MSK; + + os_memcpy(wpa_s->links[wpa_s->mlo_assoc_link_id].bssid, bss->bssid, + ETH_ALEN); + wpa_s->links[wpa_s->mlo_assoc_link_id].freq = bss->freq; + + wpa_printf(MSG_DEBUG, "MLD: address=" MACSTR ", link ID=%u", + MAC2STR(wpa_s->ap_mld_addr), wpa_s->mlo_assoc_link_id); + + wpa_s->valid_links = BIT(wpa_s->mlo_assoc_link_id); + + rnr_ie = wpa_bss_get_ie(bss, WLAN_EID_REDUCED_NEIGHBOR_REPORT); + if (!rnr_ie) { + wpa_dbg(wpa_s, MSG_DEBUG, "MLD: No RNR element"); + ret = true; + goto out; + } + + rnr_ie_len = rnr_ie[1]; + pos = rnr_ie + 2; + + while (rnr_ie_len > sizeof(struct ieee80211_neighbor_ap_info)) { + const struct ieee80211_neighbor_ap_info *ap_info = + (const struct ieee80211_neighbor_ap_info *) pos; + const u8 *data = ap_info->data; + size_t len = sizeof(struct ieee80211_neighbor_ap_info) + + ap_info->tbtt_info_len; + + wpa_printf(MSG_DEBUG, "MLD: op_class=%u, channel=%u", + ap_info->op_class, ap_info->channel); + + if (len > rnr_ie_len) + break; + + if (ap_info->tbtt_info_len < 16) { + rnr_ie_len -= len; + pos += len; + continue; + } + + data += 13; + + wpa_printf(MSG_DEBUG, "MLD: mld ID=%u, link ID=%u", + *data, *(data + 1) & 0xF); + + if (*data) { + wpa_printf(MSG_DEBUG, + "MLD: Reported link not part of MLD"); + } else { + struct wpa_bss *neigh_bss = + wpa_bss_get_bssid(wpa_s, ap_info->data + 1); + u8 link_id = *(data + 1) & 0xF; + + if (neigh_bss) { + if (wpa_scan_res_match(wpa_s, 0, neigh_bss, + wpa_s->current_ssid, + 1, 0)) { + wpa_s->valid_links |= BIT(link_id); + os_memcpy(wpa_s->links[link_id].bssid, + ap_info->data + 1, ETH_ALEN); + wpa_s->links[link_id].freq = + neigh_bss->freq; + } else { + wpa_printf(MSG_DEBUG, + "MLD: Neighbor doesn't match current SSID - skip link"); + } + } else { + wpa_printf(MSG_DEBUG, + "MLD: Neighbor not found in scan"); + } + } + + rnr_ie_len -= len; + pos += len; + } + + wpa_printf(MSG_DEBUG, "MLD: valid_links=0x%x", wpa_s->valid_links); + + for (i = 0; i < MAX_NUM_MLD_LINKS; i++) { + if (!(wpa_s->valid_links & BIT(i))) + continue; + + wpa_printf(MSG_DEBUG, "MLD: link=%u, bssid=" MACSTR, + i, MAC2STR(wpa_s->links[i].bssid)); + } + + ret = true; +out: + wpabuf_free(mlbuf); + return ret; +} + + +static void wpas_sme_ml_auth(struct wpa_supplicant *wpa_s, + union wpa_event_data *data, + int ie_offset) +{ + struct ieee802_11_elems elems; + const u8 *mld_addr; + + if (!wpa_s->valid_links) + return; + + if (ieee802_11_parse_elems(data->auth.ies + ie_offset, + data->auth.ies_len - ie_offset, + &elems, 0) != ParseOK) { + wpa_printf(MSG_DEBUG, "MLD: Failed parsing elements"); + goto out; + } + + if (!elems.basic_mle || !elems.basic_mle_len) { + wpa_printf(MSG_DEBUG, "MLD: No ML element in authentication"); + goto out; + } + + mld_addr = get_basic_mle_mld_addr(elems.basic_mle, elems.basic_mle_len); + if (!mld_addr) + goto out; + + wpa_printf(MSG_DEBUG, "MLD: mld_address=" MACSTR, MAC2STR(mld_addr)); + + if (os_memcmp(wpa_s->ap_mld_addr, mld_addr, ETH_ALEN) != 0) { + wpa_printf(MSG_DEBUG, "MLD: Unexpected MLD address (expected " + MACSTR ")", MAC2STR(wpa_s->ap_mld_addr)); + goto out; + } + + return; +out: + wpa_printf(MSG_DEBUG, "MLD: Authentication - clearing MLD state"); + wpas_reset_mlo_info(wpa_s); +} + + static void sme_send_authentication(struct wpa_supplicant *wpa_s, struct wpa_bss *bss, struct wpa_ssid *ssid, int start) @@ -414,6 +596,13 @@ static void sme_send_authentication(struct wpa_supplicant *wpa_s, params.ssid_len = bss->ssid_len; params.p2p = ssid->p2p_group; + if (wpas_ml_element(wpa_s, bss)) { + wpa_printf(MSG_DEBUG, "MLD: In authentication"); + params.mld = true; + params.mld_link_id = wpa_s->mlo_assoc_link_id; + params.ap_mld_addr = wpa_s->ap_mld_addr; + } + if (wpa_s->sme.ssid_len != params.ssid_len || os_memcmp(wpa_s->sme.ssid, params.ssid, params.ssid_len) != 0) wpa_s->sme.prev_bssid_set = 0; @@ -1646,6 +1835,7 @@ void sme_external_auth_mgmt_rx(struct wpa_supplicant *wpa_s, void sme_event_auth(struct wpa_supplicant *wpa_s, union wpa_event_data *data) { struct wpa_ssid *ssid = wpa_s->current_ssid; + int ie_offset = 0; if (ssid == NULL) { wpa_dbg(wpa_s, MSG_DEBUG, "SME: Ignore authentication event " @@ -1681,7 +1871,7 @@ void sme_event_auth(struct wpa_supplicant *wpa_s, union wpa_event_data *data) res = sme_sae_auth(wpa_s, data->auth.auth_transaction, data->auth.status_code, data->auth.ies, data->auth.ies_len, 0, data->auth.peer, - NULL); + &ie_offset); if (res < 0) { wpas_connection_failed(wpa_s, wpa_s->pending_bssid); wpa_supplicant_set_state(wpa_s, WPA_DISCONNECTED); @@ -1818,6 +2008,11 @@ void sme_event_auth(struct wpa_supplicant *wpa_s, union wpa_event_data *data) } #endif /* CONFIG_FILS */ + /* TODO: Support additional auth_type values as well */ + if (data->auth.auth_type == WLAN_AUTH_OPEN || + data->auth.auth_type == WLAN_AUTH_SAE) + wpas_sme_ml_auth(wpa_s, data, ie_offset); + sme_associate(wpa_s, ssid->mode, data->auth.peer, data->auth.auth_type); } diff --git a/wpa_supplicant/wpa_supplicant_i.h b/wpa_supplicant/wpa_supplicant_i.h index d92686139..0c044129c 100644 --- a/wpa_supplicant/wpa_supplicant_i.h +++ b/wpa_supplicant/wpa_supplicant_i.h @@ -1768,6 +1768,7 @@ void wpa_supplicant_update_channel_list(struct wpa_supplicant *wpa_s, int wpa_supplicant_need_to_roam_within_ess(struct wpa_supplicant *wpa_s, struct wpa_bss *current_bss, struct wpa_bss *seleceted); +void wpas_reset_mlo_info(struct wpa_supplicant *wpa_s); /* eap_register.c */ int eap_register_methods(void);