Add support for building and sending ML probe requests and add a control interface to trigger ML probes for testing purposes. During connect, try to send an ML probe request if we are going to connect to an MLD AP and the BSS information for some of the links is missing. Signed-off-by: Benjamin Berg <benjamin.berg@xxxxxxxxx> Signed-off-by: Ilan Peer <ilan.peer@xxxxxxxxx> Signed-off-by: Andrei Otcheretianski <andrei.otcheretianski@xxxxxxxxx> --- src/common/ieee802_11_defs.h | 15 ++ src/utils/common.c | 15 ++ src/utils/common.h | 1 + wpa_supplicant/bss.c | 229 ++++++++++++++++++++++++++++++ wpa_supplicant/bss.h | 14 +- wpa_supplicant/ctrl_iface.c | 77 ++++++++++ wpa_supplicant/events.c | 79 +++++++++++ wpa_supplicant/notify.c | 4 + wpa_supplicant/scan.c | 82 +++++++++++ wpa_supplicant/wpa_cli.c | 9 ++ wpa_supplicant/wpa_supplicant.c | 3 + wpa_supplicant/wpa_supplicant_i.h | 7 + 12 files changed, 534 insertions(+), 1 deletion(-) diff --git a/src/common/ieee802_11_defs.h b/src/common/ieee802_11_defs.h index 12789227c4..e36f6db139 100644 --- a/src/common/ieee802_11_defs.h +++ b/src/common/ieee802_11_defs.h @@ -2451,9 +2451,14 @@ struct ieee80211_he_mu_edca_parameter_set { */ #define RNR_HEADER_LEN 2 #define RNR_TBTT_HEADER_LEN 4 +#define RNR_TBTT_INFO_HDR_TYPE_MSK 0x03 +#define RNR_TBTT_INFO_HDR_FILTERED_AP 0x04 +#define RNR_TBTT_INFO_HDR_CNT_MSK 0xf0 #define RNR_TBTT_INFO_COUNT(x) (((x) & 0xf) << 4) #define RNR_TBTT_INFO_COUNT_MAX 16 +#define RNR_TBTT_INFO_COUNT_VAL(x) (((x) & 0xf0) >> 4) #define RNR_TBTT_INFO_LEN 13 +#define RNR_TBTT_INFO_MLD_LEN 16 #define RNR_NEIGHBOR_AP_OFFSET_UNKNOWN 255 /* Figure 9-632a - BSS Parameters subfield format */ #define RNR_BSS_PARAM_OCT_RECOMMENDED BIT(0) @@ -2659,6 +2664,16 @@ struct eht_ml_basic_common_info { #define EHT_ML_MLD_CAPA_FREQ_SEP_FOR_STR_MASK 0x0f80 #define EHT_ML_MLD_CAPA_AAR_SUPP 0x1000 +#define EHT_PER_STA_CTRL_LINK_ID_MSK 0x000f +#define EHT_PER_STA_CTRL_COMPLETE_PROFILE_MSK 0x0010 +#define EHT_PER_STA_CTRL_MAC_ADDR_PRESENT_MSK 0x0020 +#define EHT_PER_STA_CTRL_BEACON_INTERVAL_PRESENT_MSK 0x0040 +#define EHT_PER_STA_CTRL_TSF_OFFSET_PRESENT_MSK 0x0080 +#define EHT_PER_STA_CTRL_DTIM_INFO_PRESENT_MSK 0x0100 +#define EHT_PER_STA_CTRL_NSTR_LINK_PAIR_PRESENT_MSK 0x0200 +#define EHT_PER_STA_CTRL_NSTR_BM_SIZE_MSK 0x0400 +#define EHT_PER_STA_CTRL_BSS_PARAM_CNT_PRESENT_MSK 0x0800 + /* IEEE P802.11be/D2.0, 9.4.2.312.2.4 - Per-STA Profile subelement format */ struct ieee80211_eht_per_sta_profile { le16 sta_control; diff --git a/src/utils/common.c b/src/utils/common.c index 6acfcbd898..4a90ba8cf5 100644 --- a/src/utils/common.c +++ b/src/utils/common.c @@ -1259,6 +1259,21 @@ u8 rssi_to_rcpi(int rssi) return (rssi + 110) * 2; } +/** + * popcount - count the number of bits set in a byte + * + * This function returns the number of bits set in a byte. + */ +u8 popcount(u8 x) +{ + const u8 m1 = 0x55; + const u8 m2 = 0x33; + const u8 m4 = 0x0f; + + x = (x & m1) + ((x >> 1) & m1); + x = (x & m2) + ((x >> 2) & m2); + return (x & m4) + ((x >> 4) & m4); +} char * get_param(const char *cmd, const char *param) { diff --git a/src/utils/common.h b/src/utils/common.h index 435a9a84ca..4ca573a7d7 100644 --- a/src/utils/common.h +++ b/src/utils/common.h @@ -576,6 +576,7 @@ u8 rssi_to_rcpi(int rssi); char * get_param(const char *cmd, const char *param); void forced_memzero(void *ptr, size_t len); +u8 popcount(u8 x); /* * gcc 4.4 ends up generating strict-aliasing warnings about some very common diff --git a/wpa_supplicant/bss.c b/wpa_supplicant/bss.c index 320441426b..1aa490bcbc 100644 --- a/wpa_supplicant/bss.c +++ b/wpa_supplicant/bss.c @@ -385,6 +385,9 @@ static int wpa_bss_in_use(struct wpa_supplicant *wpa_s, struct wpa_bss *bss) if (bss == wpa_s->current_bss) return 1; + if (bss == wpa_s->ml_connect_probe_bss) + return 1; + if (wpa_s->current_bss && (bss->ssid_len != wpa_s->current_bss->ssid_len || os_memcmp(bss->ssid, wpa_s->current_bss->ssid, @@ -736,8 +739,13 @@ wpa_bss_update(struct wpa_supplicant *wpa_s, struct wpa_bss *bss, break; } } + if (wpa_s->current_bss == bss) wpa_s->current_bss = nbss; + + if (wpa_s->ml_connect_probe_bss == bss) + wpa_s->ml_connect_probe_bss = nbss; + wpa_bss_update_pending_connect(wpa_s, bss, nbss); bss = nbss; os_memcpy(bss->ies, res + 1, @@ -1468,3 +1476,224 @@ struct wpabuf * wpa_bss_defrag_mle(const struct wpa_bss *bss, u8 type) return ieee802_11_defrag_mle(&elems, type); } + + +static void +wpa_bss_parse_ml_rnr_ap_info(struct wpa_supplicant *wpa_s, + struct wpa_bss *bss, u8 mbssid_idx, + struct ieee80211_neighbor_ap_info *ap_info, + u16 *seen, u16 *missing) +{ + const u8 *pos; + const u8 *mld_params; + u8 count, mld_params_offset; + u8 i, type, link_id; + + count = RNR_TBTT_INFO_COUNT_VAL(ap_info->tbtt_info_hdr) + 1; + type = ap_info->tbtt_info_hdr & RNR_TBTT_INFO_HDR_TYPE_MSK; + + /* MLD information is at offset 13 or at start */ + if (type == 0 && + ap_info->tbtt_info_len >= RNR_TBTT_INFO_MLD_LEN) { + /* MLD info is appended */ + mld_params_offset = RNR_TBTT_INFO_LEN; + } else { + /* TODO: Support NSTR AP */ + return; + } + + pos = (const u8 *)ap_info + sizeof(*ap_info); + + for (i = 0; i < count; i++) { + if (bss->n_mld_links >= MAX_NUM_MLD_LINKS) + return; + + mld_params = pos + mld_params_offset; + + link_id = *(mld_params + 1) & EHT_ML_LINK_ID_MSK; + + if (*mld_params != mbssid_idx) { + wpa_printf(MSG_DEBUG, + "MLD: reported link not part of MLD"); + } else if (!(BIT(link_id) & *seen)) { + struct wpa_bss *neigh_bss = + wpa_bss_get_bssid(wpa_s, ap_info->data + 1); + + *seen |= BIT(link_id); + wpa_printf(MSG_DEBUG, "MLD: mld ID=%u, link ID=%u", + *mld_params, link_id); + + if (neigh_bss) { + bss->mld_links[bss->n_mld_links].link_id = link_id; + os_memcpy(bss->mld_links[bss->n_mld_links].bssid, + ap_info->data + 1, ETH_ALEN); + bss->mld_links[bss->n_mld_links].freq = neigh_bss->freq; + bss->n_mld_links++; + } else if (neigh_bss) { + bss->n_mld_links++; + } else { + *missing |= BIT(link_id); + } + } + + pos += ap_info->tbtt_info_len; + } +} + + +/** + * wpa_bss_parse_basic_ml_element - Parse the basic ML element + * @wpa_s: Pointer to wpa_supplicant data + * @bss: BSS table entry + * @mld_addr: AP MLD address (or %NULL) + * @link_info: Array to store link information (or %NULL), + * should be initialized and #MAX_NUM_MLD_LINKS elements long + * @missing_links: Result bitmask of links that were not discovered (or %NULL) + * Returns: The number of configured links (or 0 for non-MLD) + * + * Parses the basic ML element of the BSS into @link_info using the scan + * information stored in the wpa_supplicant data to fill in information for + * links where possible. The @missing_links out parameter will contain any links + * for which no corresponding BSS was found. + */ +int wpa_bss_parse_basic_ml_element(struct wpa_supplicant *wpa_s, + struct wpa_bss *bss, + u8 *ap_mld_addr, + u16 *missing_links) +{ + struct ieee802_11_elems elems; + struct wpabuf *mlbuf; + const struct element *elem; + u8 mbssid_idx = 0; + u8 ml_ie_len; + struct ieee80211_eht_ml *eht_ml; + struct eht_ml_basic_common_info *ml_basic_common_info; + u8 i; + const u16 control_mask = + MULTI_LINK_CONTROL_TYPE_MASK | + BASIC_MULTI_LINK_CTRL_PRES_LINK_ID | + BASIC_MULTI_LINK_CTRL_PRES_BSS_PARAM_CH_COUNT | + BASIC_MULTI_LINK_CTRL_PRES_MLD_CAPA; + const u16 control = + 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; + u16 missing = 0; + u16 seen; + const u8 *ies_pos = wpa_bss_ie_ptr(bss); + size_t ies_len = bss->ie_len ? bss->ie_len : bss->beacon_ie_len; + int ret = -1; + + + if (ieee802_11_parse_elems(ies_pos, ies_len, &elems, 1) == ParseFailed) { + wpa_dbg(wpa_s, MSG_DEBUG, "MLD: failed to parse elements"); + return ret; + } + + mlbuf = ieee802_11_defrag_mle(&elems, MULTI_LINK_CONTROL_TYPE_BASIC); + if (!mlbuf) { + wpa_dbg(wpa_s, MSG_DEBUG, "MLD: no ML element"); + return ret; + } + + ml_ie_len = wpabuf_len(mlbuf); + + /* + * for ext ID + 2 control + common info len + MLD address + + * link info + */ + if (ml_ie_len < 2UL + 1UL + ETH_ALEN + 1UL) + goto out; + + eht_ml = (struct ieee80211_eht_ml *)wpabuf_head(mlbuf); + if ((le_to_host16(eht_ml->ml_control) & control_mask) != control) { + wpa_printf(MSG_DEBUG, "MLD: unexpected ML element control=0x%x", + le_to_host16(eht_ml->ml_control)); + goto out; + } + + ml_basic_common_info = + (struct eht_ml_basic_common_info *)eht_ml->variable; + + /* common info length should be valid */ + if (ml_basic_common_info->len < ETH_ALEN + 1UL) + goto out; + + /* get the MLD address and MLD link ID */ + if (ap_mld_addr) + os_memcpy(ap_mld_addr, ml_basic_common_info->mld_addr, + ETH_ALEN); + + bss->n_mld_links = 0; + bss->mld_links[bss->n_mld_links].link_id = + ml_basic_common_info->variable[0] & EHT_ML_LINK_ID_MSK; + os_memcpy(bss->mld_links[bss->n_mld_links].bssid, bss->bssid, ETH_ALEN); + bss->mld_links[bss->n_mld_links].freq = bss->freq; + + seen = BIT(ml_basic_common_info->variable[0] & EHT_ML_LINK_ID_MSK); + bss->n_mld_links++; + + /* + * The AP MLD ID in the RNR corresponds to the MBSSID index see + * 802.11beD3.0 section 9.4.2.170.2. + * + * For the transmitting BSSID it is clear that both the MBSSID index + * and the AP MLD ID in the RNR are zero. + * + * For nontransmitted BSSIDs we will have a BSS generated from the + * MBSSID element(s) using inheritance rules. Included in the elements + * is the MBSSID Index Element. The RNR is copied from the beacon/probe + * response that was send by the transmitting BSSID. As such, the + * reported AP MLD ID in the RNR will match the value in the MBSSID + * Index Element. + */ + elem = (void *)wpa_bss_get_ie(bss, WLAN_EID_MULTIPLE_BSSID_INDEX); + if (elem && elem->datalen >= 1) + mbssid_idx = elem->data[0]; + + for_each_element_id(elem, WLAN_EID_REDUCED_NEIGHBOR_REPORT, + wpa_bss_ie_ptr(bss), + bss->ie_len ? bss->ie_len : bss->beacon_ie_len) { + struct ieee80211_neighbor_ap_info *ap_info; + const u8 *pos = elem->data; + size_t len = elem->datalen; + + /* RNR IE may contain more than one NEIGHBOR_AP_INFO */ + while (sizeof(*ap_info) <= len) { + size_t ap_info_len = sizeof(*ap_info); + u8 count; + + ap_info = (void *)pos; + count = RNR_TBTT_INFO_COUNT_VAL(ap_info->tbtt_info_hdr) + 1; + ap_info_len += count * ap_info->tbtt_info_len; + + if (ap_info_len > len) + goto out; + + wpa_bss_parse_ml_rnr_ap_info(wpa_s, bss, mbssid_idx, + ap_info, &seen, + &missing); + + pos += ap_info_len; + len -= ap_info_len; + } + } + + wpa_printf(MSG_DEBUG, "MLD: n_mld_links=%u (unresolved: 0x%04hx)", + bss->n_mld_links, missing); + + for (i = 0; i < bss->n_mld_links; i++) { + wpa_printf(MSG_DEBUG, "MLD: link=%u, bssid=" MACSTR, + bss->mld_links[i].link_id, + MAC2STR(bss->mld_links[i].bssid)); + } + + if (missing_links) + *missing_links = missing; + + ret = 0; +out: + wpabuf_free(mlbuf); + return ret; +} diff --git a/wpa_supplicant/bss.h b/wpa_supplicant/bss.h index 611da88444..291f02b912 100644 --- a/wpa_supplicant/bss.h +++ b/wpa_supplicant/bss.h @@ -124,6 +124,15 @@ struct wpa_bss { size_t beacon_ie_len; /** MLD address of the AP */ u8 mld_addr[ETH_ALEN]; + + /** An array of MLD links */ + u8 n_mld_links; + struct + { + u8 link_id; + u8 bssid[ETH_ALEN]; + int freq; + } mld_links [MAX_NUM_MLD_LINKS]; /* followed by ie_len octets of IEs */ /* followed by beacon_ie_len octets of IEs */ u8 ies[]; @@ -202,5 +211,8 @@ void calculate_update_time(const struct os_reltime *fetch_time, struct os_reltime *update_time); struct wpabuf * wpa_bss_defrag_mle(const struct wpa_bss *bss, u8 type); - +int wpa_bss_parse_basic_ml_element(struct wpa_supplicant *wpa_s, + struct wpa_bss *bss, + u8 *ap_mld_addr, + u16 *missing_links); #endif /* BSS_H */ diff --git a/wpa_supplicant/ctrl_iface.c b/wpa_supplicant/ctrl_iface.c index 9abfeb2168..e9d04ce70e 100644 --- a/wpa_supplicant/ctrl_iface.c +++ b/wpa_supplicant/ctrl_iface.c @@ -11913,6 +11913,80 @@ static int wpas_ctrl_iface_mlo_status(struct wpa_supplicant *wpa_s, } +#ifdef CONFIG_TESTING_OPTIONS +static int wpas_ctrl_ml_probe(struct wpa_supplicant *wpa_s, char *cmd) +{ + char *token, *context = NULL; + u8 bssid[ETH_ALEN]; + int mld_id = -1, link_id = -1; + struct wpa_bss *bss; + + os_memset(bssid, 0, sizeof(bssid)); + + while ((token = str_token(cmd, " ", &context))) { + if (os_strncmp(token, "bssid=", 6) == 0) { + if (hwaddr_aton(token + 6, bssid)) + return -1; + } else if (os_strncmp(token, "mld_id=", 7) == 0) { + mld_id = atoi(token + 7); + } else if (os_strncmp(token, "link_id=", 8) == 0) { + link_id = atoi(token + 8); + } + } + + if (mld_id < 0 || is_zero_ether_addr(bssid)) { + wpa_printf(MSG_DEBUG, + "MLD: failed parsing ML probe request args"); + return -1; + } + + bss = wpa_bss_get_bssid(wpa_s, bssid); + if (!bss) { + wpa_printf(MSG_DEBUG, + "MLD: unknown BSS for " MACSTR, MAC2STR(bssid)); + return -1; + } + + if (!wpa_s->sched_scanning && !wpa_s->scanning && + ((wpa_s->wpa_state <= WPA_SCANNING) || + (wpa_s->wpa_state == WPA_COMPLETED))) { + int *freqs = os_malloc(sizeof(int) * 2); + + if (!freqs) + return -1; + + freqs[0] = bss->freq; + freqs[1] = 0; + + wpa_s->manual_scan_passive = 0; + wpa_s->manual_scan_use_id = 0; + wpa_s->manual_scan_only_new = 0; + wpa_s->scan_id_count = 0; + wpa_s->scan_res_handler = scan_only_handler; + os_free(wpa_s->manual_scan_freqs); + wpa_s->manual_scan_freqs = freqs; + + os_memcpy(wpa_s->ml_probe_bssid, bssid, ETH_ALEN); + wpa_s->ml_probe_mld_id = mld_id; + if (link_id >= 0) + wpa_s->ml_probe_links = BIT(link_id); + + wpa_s->normal_scans = 0; + wpa_s->scan_req = MANUAL_SCAN_REQ; + wpa_s->after_wps = 0; + wpa_s->known_wps_freq = 0; + wpa_supplicant_req_scan(wpa_s, 0, 0); + } else { + wpa_printf(MSG_DEBUG, + "MLO: Ongoing scan: reject ML probe request"); + return -1; + } + + return 0; +} +#endif /* CONFIG_TESTING_OPTIONS */ + + char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s, char *buf, size_t *resp_len) { @@ -12718,6 +12792,9 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s, } else if (os_strcmp(buf, "TWT_TEARDOWN") == 0) { if (wpas_ctrl_iface_send_twt_teardown(wpa_s, "")) reply_len = -1; + } else if (os_strncmp(buf, "ML_PROBE_REQ ", 8) == 0) { + if (wpas_ctrl_ml_probe(wpa_s, buf + 13)) + reply_len = -1; #endif /* CONFIG_TESTING_OPTIONS */ } else if (os_strncmp(buf, "VENDOR_ELEM_ADD ", 16) == 0) { if (wpas_ctrl_vendor_elem_add(wpa_s, buf + 16) < 0) diff --git a/wpa_supplicant/events.c b/wpa_supplicant/events.c index e7aaa1a128..b666d7b202 100644 --- a/wpa_supplicant/events.c +++ b/wpa_supplicant/events.c @@ -1831,6 +1831,78 @@ static void wpa_supplicant_req_new_scan(struct wpa_supplicant *wpa_s, } +static u32 ml_link_probe_scan(struct wpa_supplicant *wpa_s) +{ + if (!wpa_s->ml_connect_probe_ssid || !wpa_s->ml_connect_probe_bss) + return 0; + + wpa_msg(wpa_s, MSG_DEBUG, + "Request association with " MACSTR " after ML probe", + MAC2STR(wpa_s->ml_connect_probe_bss->bssid)); + + wpa_supplicant_associate(wpa_s, + wpa_s->ml_connect_probe_bss, + wpa_s->ml_connect_probe_ssid); + + wpa_s->ml_connect_probe_ssid = NULL; + wpa_s->ml_connect_probe_bss = NULL; + + return 1; +} + + +static int wpa_supplicant_connect_ml_missing(struct wpa_supplicant *wpa_s, + struct wpa_bss *selected, + struct wpa_ssid *ssid) +{ + int *freqs; + u16 missing_links = 0; + + if (!((wpa_s->drv_flags2 & WPA_DRIVER_FLAGS2_MLO) && + (wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME))) + return 0; + + /* Try to resolve any missing link information */ + if (wpa_bss_parse_basic_ml_element(wpa_s, selected, NULL, + &missing_links) || !missing_links) + return 0; + + wpa_dbg(wpa_s, MSG_DEBUG, + "MLD: Doing an ML probe for missing links 0x%04x", + missing_links); + + freqs = os_malloc(sizeof(int) * 2); + if (!freqs) + return 0; + + wpa_s->ml_connect_probe_ssid = ssid; + wpa_s->ml_connect_probe_bss = selected; + + freqs[0] = selected->freq; + freqs[1] = 0; + + wpa_s->manual_scan_passive = 0; + wpa_s->manual_scan_use_id = 0; + wpa_s->manual_scan_only_new = 0; + wpa_s->scan_id_count = 0; + os_free(wpa_s->manual_scan_freqs); + wpa_s->manual_scan_freqs = freqs; + + os_memcpy(wpa_s->ml_probe_bssid, selected->bssid, + ETH_ALEN); + wpa_s->ml_probe_mld_id = -1; + wpa_s->ml_probe_links = missing_links; + + wpa_s->normal_scans = 0; + wpa_s->scan_req = MANUAL_SCAN_REQ; + wpa_s->after_wps = 0; + wpa_s->known_wps_freq = 0; + wpa_supplicant_req_scan(wpa_s, 0, 0); + + return 1; +} + + int wpa_supplicant_connect(struct wpa_supplicant *wpa_s, struct wpa_bss *selected, struct wpa_ssid *ssid) @@ -1887,6 +1959,10 @@ int wpa_supplicant_connect(struct wpa_supplicant *wpa_s, wpa_supplicant_req_new_scan(wpa_s, 10, 0); return 0; } + + if (wpa_supplicant_connect_ml_missing(wpa_s, selected, ssid)) + return 0; + wpa_msg(wpa_s, MSG_DEBUG, "Request association with " MACSTR, MAC2STR(selected->bssid)); wpa_supplicant_associate(wpa_s, selected, ssid); @@ -2308,6 +2384,9 @@ static int _wpa_supplicant_event_scan_results(struct wpa_supplicant *wpa_s, wpas_beacon_rep_scan_process(wpa_s, scan_res, &data->scan_info) > 0) goto scan_work_done; + if (ml_link_probe_scan(wpa_s) > 0) + goto scan_work_done; + if ((wpa_s->conf->ap_scan == 2 && !wpas_wps_searching(wpa_s))) goto scan_work_done; diff --git a/wpa_supplicant/notify.c b/wpa_supplicant/notify.c index 4b4a34b52d..ae00ee9d28 100644 --- a/wpa_supplicant/notify.c +++ b/wpa_supplicant/notify.c @@ -396,6 +396,10 @@ void wpas_notify_network_removed(struct wpa_supplicant *wpa_s, wpa_s->last_ssid = NULL; if (wpa_s->current_ssid == ssid) wpa_s->current_ssid = NULL; + if (wpa_s->ml_connect_probe_ssid == ssid) { + wpa_s->ml_connect_probe_ssid = NULL; + wpa_s->ml_connect_probe_bss = NULL; + } #if defined(CONFIG_SME) && defined(CONFIG_SAE) if (wpa_s->sme.ext_auth_wpa_ssid == ssid) wpa_s->sme.ext_auth_wpa_ssid = NULL; diff --git a/wpa_supplicant/scan.c b/wpa_supplicant/scan.c index 166751a1f2..a38d1ae32c 100644 --- a/wpa_supplicant/scan.c +++ b/wpa_supplicant/scan.c @@ -649,6 +649,69 @@ void wpa_supplicant_set_default_scan_ies(struct wpa_supplicant *wpa_s) } +static struct wpabuf * wpa_supplicant_ml_probe_ie(int mld_id, u16 links) +{ + struct wpabuf *extra_ie; + u16 control = MULTI_LINK_CONTROL_TYPE_PROBE_REQ; + u8 len = 4; + u8 link_id; + + if (mld_id >= 0) { + control |= EHT_ML_PRES_BM_PROBE_REQ_AP_MLD_ID; + len += 1; + } + + len += 4 * (popcount(links & 0xff) + popcount((links >> 8) & 0xff)); + + extra_ie = wpabuf_alloc(len + 2); + if (!extra_ie) + return NULL; + + wpabuf_put_u8(extra_ie, WLAN_EID_EXTENSION); + wpabuf_put_u8(extra_ie, len); + wpabuf_put_u8(extra_ie, WLAN_EID_EXT_MULTI_LINK); + + wpabuf_put_le16(extra_ie, control); + + /* common info length and MLD ID (if requested) */ + if (mld_id >= 0) { + wpabuf_put_u8(extra_ie, 2); + wpabuf_put_u8(extra_ie, mld_id); + + wpa_printf(MSG_DEBUG, + "MLD: ML probe targeted at MLD ID %d", mld_id); + } else { + wpabuf_put_u8(extra_ie, 1); + + wpa_printf(MSG_DEBUG, + "MLD: ML probe targeted at receiving AP"); + } + + if (!links) + wpa_printf(MSG_DEBUG, + "MLD: Probing all links"); + else + wpa_printf(MSG_DEBUG, + "MLD: Probing links 0x%04x", links); + + for (link_id = 0; link_id < MAX_NUM_MLD_LINKS; link_id++) { + if (!(links & BIT(link_id))) + continue; + + wpabuf_put_u8(extra_ie, EHT_ML_SUB_ELEM_PER_STA_PROFILE); + + /* sub-element length includes only the control */ + wpabuf_put_u8(extra_ie, 2); + + control = link_id | EHT_PER_STA_CTRL_COMPLETE_PROFILE_MSK; + + wpabuf_put_le16(extra_ie, control); + } + + return extra_ie; +} + + static struct wpabuf * wpa_supplicant_extra_ies(struct wpa_supplicant *wpa_s) { struct wpabuf *extra_ie = NULL; @@ -659,6 +722,15 @@ static struct wpabuf * wpa_supplicant_extra_ies(struct wpa_supplicant *wpa_s) enum wps_request_type req_type = WPS_REQ_ENROLLEE_INFO; #endif /* CONFIG_WPS */ + if (!is_zero_ether_addr(wpa_s->ml_probe_bssid)) { + extra_ie = wpa_supplicant_ml_probe_ie(wpa_s->ml_probe_mld_id, + wpa_s->ml_probe_links); + + /* No other elements should be included in the probe request */ + wpa_printf(MSG_DEBUG, "MLD: scan: including only ML element"); + return extra_ie; + } + #ifdef CONFIG_P2P if (wpa_s->p2p_group_interface == P2P_GROUP_INTERFACE_CLIENT) wpa_drv_get_ext_capa(wpa_s, WPA_IF_P2P_CLIENT); @@ -1397,8 +1469,13 @@ ssid_list_set: "Scan a previously specified BSSID " MACSTR, MAC2STR(params.bssid)); } + } else if (!is_zero_ether_addr(wpa_s->ml_probe_bssid)) { + wpa_printf(MSG_DEBUG, "Scanning for ML probe request"); + params.bssid = wpa_s->ml_probe_bssid; + params.min_probe_req_content = true; } + if (wpa_s->last_scan_req == MANUAL_SCAN_REQ && wpa_s->manual_non_coloc_6ghz) { wpa_dbg(wpa_s, MSG_DEBUG, "Collocated 6 GHz logic is disabled"); @@ -1480,6 +1557,11 @@ scan: if (params.bssid) os_memset(wpa_s->next_scan_bssid, 0, ETH_ALEN); } + + wpa_s->ml_probe_mld_id = -1; + wpa_s->ml_probe_links = 0; + os_memset(wpa_s->ml_probe_bssid, 0, + sizeof(wpa_s->ml_probe_bssid)); } diff --git a/wpa_supplicant/wpa_cli.c b/wpa_supplicant/wpa_cli.c index effc7b3bc2..43ee050f41 100644 --- a/wpa_supplicant/wpa_cli.c +++ b/wpa_supplicant/wpa_cli.c @@ -2095,6 +2095,13 @@ static int wpa_cli_cmd_drop_sa(struct wpa_ctrl *ctrl, int argc, char *argv[]) { return wpa_ctrl_command(ctrl, "DROP_SA"); } + + +static int wpa_cli_cmd_ml_probe_req(struct wpa_ctrl *ctrl, + int argc, char *argv[]) +{ + return wpa_cli_cmd(ctrl, "ML_PROBE_REQ", 2, argc, argv); +} #endif /* CONFIG_TESTING_OPTIONS */ @@ -3673,6 +3680,8 @@ static const struct wpa_cli_cmd wpa_cli_commands[] = { #ifdef CONFIG_TESTING_OPTIONS { "drop_sa", wpa_cli_cmd_drop_sa, NULL, cli_cmd_flag_none, "= drop SA without deauth/disassoc (test command)" }, + { "ml_probe_req", wpa_cli_cmd_ml_probe_req, NULL, cli_cmd_flag_none, + "= send Multi-Link Probe request <bssid=addr> <mld_id=id> [link_id=id] (test command)" }, #endif /* CONFIG_TESTING_OPTIONS */ { "roam", wpa_cli_cmd_roam, wpa_cli_complete_bss, cli_cmd_flag_none, diff --git a/wpa_supplicant/wpa_supplicant.c b/wpa_supplicant/wpa_supplicant.c index e0f3240e87..0092c024b1 100644 --- a/wpa_supplicant/wpa_supplicant.c +++ b/wpa_supplicant/wpa_supplicant.c @@ -4429,6 +4429,8 @@ static void wpa_supplicant_clear_connection(struct wpa_supplicant *wpa_s, { struct wpa_ssid *old_ssid; + wpa_s->ml_connect_probe_ssid = NULL; + wpa_s->ml_connect_probe_bss = NULL; wpas_connect_work_done(wpa_s); wpa_clear_keys(wpa_s, addr); old_ssid = wpa_s->current_ssid; @@ -5741,6 +5743,7 @@ wpa_supplicant_alloc(struct wpa_supplicant *parent) dl_list_init(&wpa_s->drv_signal_override); #endif /* CONFIG_TESTING_OPTIONS */ dl_list_init(&wpa_s->active_scs_ids); + wpa_s->ml_probe_mld_id = -1; return wpa_s; } diff --git a/wpa_supplicant/wpa_supplicant_i.h b/wpa_supplicant/wpa_supplicant_i.h index d5b3dab67f..89bc2882d0 100644 --- a/wpa_supplicant/wpa_supplicant_i.h +++ b/wpa_supplicant/wpa_supplicant_i.h @@ -869,6 +869,10 @@ struct wpa_supplicant { unsigned int suitable_network; unsigned int no_suitable_network; + u8 ml_probe_bssid[ETH_ALEN]; + int ml_probe_mld_id; + u16 ml_probe_links; + u64 drv_flags; u64 drv_flags2; unsigned int drv_enc; @@ -1529,6 +1533,9 @@ struct wpa_supplicant { unsigned int wait_for_dscp_req:1; struct wpa_signal_info last_signal_info; + + struct wpa_ssid *ml_connect_probe_ssid; + struct wpa_bss *ml_connect_probe_bss; }; -- 2.38.1 _______________________________________________ Hostap mailing list Hostap@xxxxxxxxxxxxxxxxxxx http://lists.infradead.org/mailman/listinfo/hostap