From 8f89661df5b74bcc6fd94c6590ae178c48415af1 Mon Sep 17 00:00:00 2001 From: Andrei Otcheretianski Date: Tue, 6 Dec 2022 11:46:06 +0200 Subject: [PATCH] SME: Add support for handling authentication with MLD In case both the local driver and the AP support MLD, request an MLD authentication from the driver. When processing the authentication event from the driver verify that the MLD address in the authentication data matches that of the requested AP. Signed-off-by: Ilan Peer Signed-off-by: Andrei Otcheretianski --- wpa_supplicant/bss.c | 18 +++ wpa_supplicant/bss.h | 2 + wpa_supplicant/events.c | 2 +- wpa_supplicant/sme.c | 197 +++++++++++++++++++++++++++++- wpa_supplicant/wpa_supplicant_i.h | 1 + 5 files changed, 218 insertions(+), 2 deletions(-) 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);