From f540d078c9895d9f1640ea24029428091731648f Mon Sep 17 00:00:00 2001 From: Andrei Otcheretianski Date: Mon, 22 May 2023 22:33:41 +0300 Subject: [PATCH] AP: Support building Basic Multi-Link element Define a struct to hold MLD station info and implement publishing of the Basic Multi-Link element. Add it into Beacon and Probe Response frames. Signed-off-by: Andrei Otcheretianski --- src/ap/beacon.c | 21 ++++ src/ap/hostapd.c | 23 ++++ src/ap/hostapd.h | 2 + src/ap/ieee802_11.h | 2 + src/ap/ieee802_11_eht.c | 204 +++++++++++++++++++++++++++++++++++ src/ap/sta_info.h | 34 ++++++ src/common/ieee802_11_defs.h | 10 ++ 7 files changed, 296 insertions(+) diff --git a/src/ap/beacon.c b/src/ap/beacon.c index 7ff678319..32c143a2b 100644 --- a/src/ap/beacon.c +++ b/src/ap/beacon.c @@ -605,6 +605,14 @@ static u8 * hostapd_gen_probe_resp(struct hostapd_data *hapd, buflen += 3 + sizeof(struct ieee80211_eht_operation); if (hapd->iconf->punct_bitmap) buflen += EHT_OPER_DISABLED_SUBCHAN_BITMAP_SIZE; + + /* + * TODO: Multi-Link element has variable length and can be + * long based on the common info and number of per + * station profiles. For now use 256. + */ + if (hapd->conf->mld_ap) + buflen += 256; } #endif /* CONFIG_IEEE80211BE */ @@ -755,6 +763,8 @@ static u8 * hostapd_gen_probe_resp(struct hostapd_data *hapd, #ifdef CONFIG_IEEE80211BE if (hapd->iconf->ieee80211be && !hapd->conf->disable_11be) { + if (hapd->conf->mld_ap) + pos = hostapd_eid_eht_basic_ml(hapd, pos, NULL, true); pos = hostapd_eid_eht_capab(hapd, pos, IEEE80211_MODE_AP); pos = hostapd_eid_eht_operation(hapd, pos); } @@ -1711,6 +1721,14 @@ int ieee802_11_build_ap_params(struct hostapd_data *hapd, tail_len += 3 + sizeof(struct ieee80211_eht_operation); if (hapd->iconf->punct_bitmap) tail_len += EHT_OPER_DISABLED_SUBCHAN_BITMAP_SIZE; + + /* + * TODO: Multi-Link element has variable length and can be + * long based on the common info and number of per + * station profiles. For now use 256. + */ + if (hapd->conf->mld_ap) + tail_len += 256; } #endif /* CONFIG_IEEE80211BE */ @@ -1881,6 +1899,9 @@ int ieee802_11_build_ap_params(struct hostapd_data *hapd, #ifdef CONFIG_IEEE80211BE if (hapd->iconf->ieee80211be && !hapd->conf->disable_11be) { + if (hapd->conf->mld_ap) + tailpos = hostapd_eid_eht_basic_ml(hapd, tailpos, NULL, + true); tailpos = hostapd_eid_eht_capab(hapd, tailpos, IEEE80211_MODE_AP); tailpos = hostapd_eid_eht_operation(hapd, tailpos); diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c index f82b87c7c..0f6574f5d 100644 --- a/src/ap/hostapd.c +++ b/src/ap/hostapd.c @@ -4260,3 +4260,26 @@ void hostapd_ocv_check_csa_sa_query(void *eloop_ctx, void *timeout_ctx) } } #endif /* CONFIG_OCV */ + + +#ifdef CONFIG_IEEE80211BE +struct hostapd_data * hostapd_mld_get_link_bss(struct hostapd_data *hapd, + u8 link_id) +{ + unsigned int i; + + for (i = 0; i < hapd->iface->interfaces->count; i++) { + struct hostapd_iface *h = hapd->iface->interfaces->iface[i]; + struct hostapd_data *h_hapd = h->bss[0]; + struct hostapd_bss_config *hconf = h_hapd->conf; + + if (!hconf->mld_ap || hconf->mld_id != hapd->conf->mld_id) + continue; + + if (h_hapd->mld_link_id == link_id) + return h_hapd; + } + + return NULL; +} +#endif /* CONFIG_IEEE80211BE */ diff --git a/src/ap/hostapd.h b/src/ap/hostapd.h index d0de971bb..d721d15b5 100644 --- a/src/ap/hostapd.h +++ b/src/ap/hostapd.h @@ -764,5 +764,7 @@ void fst_hostapd_fill_iface_obj(struct hostapd_data *hapd, int hostapd_set_acl(struct hostapd_data *hapd); struct hostapd_data * hostapd_mbssid_get_tx_bss(struct hostapd_data *hapd); int hostapd_mbssid_get_bss_index(struct hostapd_data *hapd); +struct hostapd_data * hostapd_mld_get_link_bss(struct hostapd_data *hapd, + u8 link_id); #endif /* HOSTAPD_H */ diff --git a/src/ap/ieee802_11.h b/src/ap/ieee802_11.h index 1190a5ea8..b174fdd1f 100644 --- a/src/ap/ieee802_11.h +++ b/src/ap/ieee802_11.h @@ -84,6 +84,8 @@ void hostapd_get_eht_capab(struct hostapd_data *hapd, const struct ieee80211_eht_capabilities *src, struct ieee80211_eht_capabilities *dest, size_t len); +u8 * hostapd_eid_eht_basic_ml(struct hostapd_data *hapd, u8 *eid, + struct sta_info *info, bool include_mld_id); int hostapd_get_aid(struct hostapd_data *hapd, struct sta_info *sta); u16 copy_sta_ht_capab(struct hostapd_data *hapd, struct sta_info *sta, const u8 *ht_capab); diff --git a/src/ap/ieee802_11_eht.c b/src/ap/ieee802_11_eht.c index 6ebe0f91d..79062b003 100644 --- a/src/ap/ieee802_11_eht.c +++ b/src/ap/ieee802_11_eht.c @@ -417,3 +417,207 @@ void hostapd_get_eht_capab(struct hostapd_data *hapd, os_memset(dest, 0, sizeof(*dest)); os_memcpy(dest, src, len); } + + +u8 * hostapd_eid_eht_basic_ml(struct hostapd_data *hapd, u8 *eid, + struct sta_info *info, bool include_mld_id) +{ + struct wpabuf *buf; + u16 control; + u8 *pos = eid; + const u8 *ptr; + size_t len, slice_len; + u8 link_id; + u8 common_info_len; + + /* + * As the Multi-Link element can exceed the size of 255 bytes need to + * first build it and then handle fragmentation. + */ + buf = wpabuf_alloc(1024); + if (!buf) + return pos; + + /* Multi-Link Control field */ + 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_EML_CAPA | + BASIC_MULTI_LINK_CTRL_PRES_MLD_CAPA; + + /* + * Set the basic Multi-Link common information. Hard code the common + * info length to 13 based on the length of the present fields: + * Length (1) + MLD address (6) + Link ID (1) + + * BSS Parameters Change Count (1) + EML Capabilities (2) + + * MLD Capabilities and Operations (2) + */ + common_info_len = 13; + + if (include_mld_id) { + /* AP MLD ID */ + control |= BASIC_MULTI_LINK_CTRL_PRES_AP_MLD_ID; + common_info_len++; + } + + wpabuf_put_le16(buf, control); + + wpabuf_put_u8(buf, common_info_len); + + /* Own MLD MAC Address */ + wpabuf_put_data(buf, hapd->mld_addr, ETH_ALEN); + + /* Own Link ID */ + wpabuf_put_u8(buf, hapd->mld_link_id); + + /* Currently hard code the BSS Parameters Change Count to 0x1 */ + wpabuf_put_u8(buf, 0x1); + + wpa_printf(MSG_DEBUG, "MLD: EML Capabilities=0x%x", + hapd->iface->mld_eml_capa); + wpabuf_put_le16(buf, hapd->iface->mld_eml_capa); + + wpa_printf(MSG_DEBUG, "MLD: MLD Capabilities and Operations=0x%x", + hapd->iface->mld_mld_capa); + wpabuf_put_le16(buf, hapd->iface->mld_mld_capa); + + if (include_mld_id) { + wpa_printf(MSG_DEBUG, "MLD: AP MLD ID=0x%x", + hapd->conf->mld_id); + wpabuf_put_u8(buf, hapd->conf->mld_id); + } + + if (!info) + goto out; + + /* Add link info for the other links */ + for (link_id = 0; link_id < MAX_NUM_MLD_LINKS; link_id++) { + struct mld_link_info *link = &info->mld_info.links[link_id]; + struct hostapd_data *link_bss; + + /* + * control (2) + station info length (1) + MAC address (6) + + * beacon interval (2) + TSF offset (8) + DTIM info (2) + BSS + * parameters change counter (1) + station profile length. + */ + const size_t fixed_len = 22; + size_t total_len = fixed_len + link->resp_sta_profile_len; + + /* Skip the local one */ + if (link_id == hapd->mld_link_id || !link->valid) + continue; + + link_bss = hostapd_mld_get_link_bss(hapd, link_id); + if (!link_bss) { + wpa_printf(MSG_ERROR, + "MLD: Couldn't find link BSS - skip it"); + continue; + } + + /* Per-STA Profile subelement */ + wpabuf_put_u8(buf, EHT_ML_SUB_ELEM_PER_STA_PROFILE); + + if (total_len <= 255) + wpabuf_put_u8(buf, total_len); + else + wpabuf_put_u8(buf, 255); + + /* STA Control */ + control = (link_id & 0xf) | + EHT_PER_STA_CTRL_MAC_ADDR_PRESENT_MSK | + EHT_PER_STA_CTRL_COMPLETE_PROFILE_MSK | + EHT_PER_STA_CTRL_TSF_OFFSET_PRESENT_MSK | + EHT_PER_STA_CTRL_BEACON_INTERVAL_PRESENT_MSK | + EHT_PER_STA_CTRL_DTIM_INFO_PRESENT_MSK | + EHT_PER_STA_CTRL_BSS_PARAM_CNT_PRESENT_MSK; + wpabuf_put_le16(buf, control); + + /* STA Info */ + + /* STA Info Length */ + wpabuf_put_u8(buf, fixed_len - 2); + wpabuf_put_data(buf, link->local_addr, ETH_ALEN); + wpabuf_put_le16(buf, link_bss->iconf->beacon_int); + + /* TSF Offset */ + /* + * TODO: Currently setting TSF offset to zero. However, this + * information needs to come from the driver. + */ + wpabuf_put_le64(buf, 0); + + /* DTIM Info */ + wpabuf_put_le16(buf, link_bss->conf->dtim_period); + + /* BSS Parameters Change Count */ + /* TODO: Currently hard code the BSS Parameters Change Count to + * 0x1 */ + wpabuf_put_u8(buf, 0x1); + + /* Fragment the sub element if needed */ + if (total_len <= 255) { + wpabuf_put_data(buf, link->resp_sta_profile, + link->resp_sta_profile_len); + } else { + ptr = link->resp_sta_profile; + len = link->resp_sta_profile_len; + + slice_len = 255 - fixed_len; + + wpabuf_put_data(buf, ptr, slice_len); + len -= slice_len; + ptr += slice_len; + + while (len) { + if (len <= 255) + slice_len = len; + else + slice_len = 255; + + wpabuf_put_u8(buf, EHT_ML_SUB_ELEM_FRAGMENT); + wpabuf_put_u8(buf, slice_len); + wpabuf_put_data(buf, ptr, slice_len); + + len -= slice_len; + ptr += slice_len; + } + } + } + +out: + /* Fragment the Multi-Link element, if needed */ + len = wpabuf_len(buf); + ptr = wpabuf_head(buf); + + if (len <= 254) + slice_len = len; + else + slice_len = 254; + + *pos++ = WLAN_EID_EXTENSION; + *pos++ = slice_len + 1; + *pos++ = WLAN_EID_EXT_MULTI_LINK; + os_memcpy(pos, ptr, slice_len); + + ptr += slice_len; + pos += slice_len; + len -= slice_len; + + while (len) { + if (len <= 255) + slice_len = len; + else + slice_len = 255; + + *pos++ = WLAN_EID_FRAGMENT; + *pos++ = slice_len; + os_memcpy(pos, ptr, slice_len); + + ptr += slice_len; + pos += slice_len; + len -= slice_len; + } + + wpabuf_free(buf); + return pos; +} diff --git a/src/ap/sta_info.h b/src/ap/sta_info.h index 8433ff8d6..e2b9dde87 100644 --- a/src/ap/sta_info.h +++ b/src/ap/sta_info.h @@ -69,6 +69,35 @@ struct pending_eapol_rx { enum frame_encryption encrypted; }; +#define EHT_ML_MAX_STA_PROF_LEN 1024 +struct mld_info { + bool mld_sta; + + struct ml_common_info { + u8 mld_addr[ETH_ALEN]; + u16 medium_sync_delay; + u16 eml_capa; + u16 mld_capa; + } common_info; + + struct mld_link_info { + u8 valid; + u8 local_addr[ETH_ALEN]; + u8 peer_addr[ETH_ALEN]; + + size_t nstr_bitmap_len; + u8 nstr_bitmap[2]; + + u16 capability; + + u16 status; + size_t resp_sta_profile_len; + u8 resp_sta_profile[EHT_ML_MAX_STA_PROF_LEN]; + + const u8 *rsne, *rsnxe; + } links[MAX_NUM_MLD_LINKS]; +}; + struct sta_info { struct sta_info *next; /* next entry in sta list */ struct sta_info *hnext; /* next entry in hash table list */ @@ -299,6 +328,11 @@ struct sta_info { #ifdef CONFIG_PASN struct pasn_data *pasn; #endif /* CONFIG_PASN */ + +#ifdef CONFIG_IEEE80211BE + struct mld_info mld_info; + u8 mld_assoc_link_id; +#endif /* CONFIG_IEEE80211BE */ }; diff --git a/src/common/ieee802_11_defs.h b/src/common/ieee802_11_defs.h index b86906828..571ace2f5 100644 --- a/src/common/ieee802_11_defs.h +++ b/src/common/ieee802_11_defs.h @@ -2660,6 +2660,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;