From f42a2434a0d4daf8e990285226aa5b0456def1a3 Mon Sep 17 00:00:00 2001
From: Shayne Chen <shayne.chen@mediatek.com>
Date: Wed, 7 Jun 2023 17:40:15 +0800
Subject: [PATCH 51/54] hostapd: mtk: implement per-sta profile in ml probe
 resp

Signed-off-by: Shayne Chen <shayne.chen@mediatek.com>
---
 src/ap/beacon.c         | 17 +++++---
 src/ap/hostapd.h        |  2 +
 src/ap/ieee802_11.c     | 58 ++++++++++++++++++++++++-
 src/ap/ieee802_11.h     |  5 ++-
 src/ap/ieee802_11_eht.c | 93 ++++++++++++++++++++++++++++++++++++++++-
 5 files changed, 165 insertions(+), 10 deletions(-)

diff --git a/src/ap/beacon.c b/src/ap/beacon.c
index d76deec6b..678b1e3cc 100644
--- a/src/ap/beacon.c
+++ b/src/ap/beacon.c
@@ -552,7 +552,8 @@ static u8 * hostapd_eid_mbssid_config(struct hostapd_data *hapd, u8 *eid,
 static u8 * hostapd_gen_probe_resp(struct hostapd_data *hapd,
 				   const struct ieee80211_mgmt *req,
 				   int is_p2p, size_t *resp_len,
-				   const u8 *known_bss, u8 known_bss_len)
+				   const u8 *known_bss, u8 known_bss_len,
+				   bool mlo_probe_resp)
 {
 	struct ieee80211_mgmt *resp;
 	u8 *pos, *epos, *csa_pos;
@@ -764,7 +765,7 @@ 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_basic_ml(hapd, pos, NULL, true, mlo_probe_resp);
 
 		pos = hostapd_eid_eht_capab(hapd, pos, IEEE80211_MODE_AP);
 		pos = hostapd_eid_eht_operation(hapd, pos);
@@ -1280,9 +1281,13 @@ void handle_probe_req(struct hostapd_data *hapd,
 	wpa_msg_ctrl(hapd->msg_ctx, MSG_INFO, RX_PROBE_REQUEST "sa=" MACSTR
 		     " signal=%d", MAC2STR(mgmt->sa), ssi_signal);
 
+	if (elems.probe_req_mle)
+		hostapd_ml_probe_resp(hapd);
+
 	resp = hostapd_gen_probe_resp(hapd, mgmt, elems.p2p != NULL,
 				      &resp_len, elems.mbssid_known_bss,
-				      elems.mbssid_known_bss_len);
+				      elems.mbssid_known_bss_len,
+				      elems.probe_req_mle);
 	if (resp == NULL)
 		return;
 
@@ -1352,7 +1357,7 @@ static u8 * hostapd_probe_resp_offloads(struct hostapd_data *hapd,
 			   "this");
 
 	/* Generate a Probe Response template for the non-P2P case */
-	return hostapd_gen_probe_resp(hapd, NULL, 0, resp_len, NULL, 0);
+	return hostapd_gen_probe_resp(hapd, NULL, 0, resp_len, NULL, 0, 0);
 }
 
 #endif /* NEED_AP_MLME */
@@ -1371,7 +1376,7 @@ static u8 * hostapd_unsol_bcast_probe_resp(struct hostapd_data *hapd,
 
 	return hostapd_gen_probe_resp(hapd, NULL, 0,
 				      &params->unsol_bcast_probe_resp_tmpl_len,
-				      NULL, 0);
+				      NULL, 0, 0);
 }
 #endif /* CONFIG_IEEE80211AX */
 
@@ -1914,7 +1919,7 @@ int ieee802_11_build_ap_params(struct hostapd_data *hapd,
 	if (hapd->iconf->ieee80211be && !hapd->conf->disable_11be) {
 		if (hapd->conf->mld_ap)
 			tailpos = hostapd_eid_eht_basic_ml(hapd, tailpos, NULL,
-							   true);
+							   true, false);
 
 		tailpos = hostapd_eid_eht_capab(hapd, tailpos,
 						IEEE80211_MODE_AP);
diff --git a/src/ap/hostapd.h b/src/ap/hostapd.h
index a04f195ab..317ac16cb 100644
--- a/src/ap/hostapd.h
+++ b/src/ap/hostapd.h
@@ -199,6 +199,8 @@ struct hostapd_data {
 	u8 mld_next_link_id;
 
 	struct hostapd_data *mld_first_bss;
+	size_t resp_sta_profile_len;
+	u8 resp_sta_profile[1024];
 
 	/* OpenWrt specific statistics */
 	struct hostapd_openwrt_stats openwrt_stats;
diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c
index 7e54bb4c1..7b7e0c803 100644
--- a/src/ap/ieee802_11.c
+++ b/src/ap/ieee802_11.c
@@ -4902,7 +4902,7 @@ rsnxe_done:
 #ifdef CONFIG_IEEE80211BE
 	if (hapd->iconf->ieee80211be && !hapd->conf->disable_11be) {
 		if (hapd->conf->mld_ap)
-			p = hostapd_eid_eht_basic_ml(hapd, p, sta, false);
+			p = hostapd_eid_eht_basic_ml(hapd, p, sta, false, false);
 		p = hostapd_eid_eht_capab(hapd, p, IEEE80211_MODE_AP);
 		p = hostapd_eid_eht_operation(hapd, p);
 	}
@@ -7980,4 +7980,60 @@ void punct_update_legacy_bw(u16 bitmap, u8 pri, enum oper_chan_width *width,
 	/* TODO: 320 MHz */
 }
 
+
+static size_t ieee80211_ml_build_probe_resp(struct hostapd_data *hapd,
+					    u8 *buf, size_t buflen)
+{
+	u8 *p = buf;
+
+	/* capability info */
+	WPA_PUT_LE16(p, hostapd_own_capab_info(hapd));
+	p += 2;
+
+	p = hostapd_eid_supp_rates(hapd, p);
+	p = hostapd_eid_ext_supp_rates(hapd, p);
+	p = hostapd_eid_rm_enabled_capab(hapd, p, buf + buflen - p);
+	p = hostapd_eid_ht_capabilities(hapd, p);
+	p = hostapd_eid_ht_operation(hapd, p);
+
+	if (hapd->iconf->ieee80211ac && !hapd->conf->disable_11ac) {
+		p = hostapd_eid_vht_capabilities(hapd, p, 0);
+		p = hostapd_eid_vht_operation(hapd, p);
+	}
+
+	if (hapd->iconf->ieee80211ax && !hapd->conf->disable_11ax) {
+		p = hostapd_eid_he_capab(hapd, p, IEEE80211_MODE_AP);
+		p = hostapd_eid_he_operation(hapd, p);
+		p = hostapd_eid_spatial_reuse(hapd, p);
+		p = hostapd_eid_he_mu_edca_parameter_set(hapd, p);
+		p = hostapd_eid_he_6ghz_band_cap(hapd, p);
+		if (hapd->iconf->ieee80211be && !hapd->conf->disable_11be) {
+			p = hostapd_eid_eht_capab(hapd, p, IEEE80211_MODE_AP);
+			p = hostapd_eid_eht_operation(hapd, p);
+		}
+	}
+
+	p = hostapd_eid_ext_capab(hapd, p, false);
+
+out:
+	return p - buf;
+}
+
+
+void hostapd_ml_probe_resp(struct hostapd_data *hapd)
+{
+	int link_id;
+
+	for (link_id = 0; link_id < MAX_NUM_MLD_LINKS; link_id++) {
+		struct hostapd_data *h = hostapd_mld_get_link_bss(hapd, link_id);
+
+		if (!h)
+			continue;
+
+		h->resp_sta_profile_len =
+			ieee80211_ml_build_probe_resp(h, h->resp_sta_profile,
+						      sizeof(h->resp_sta_profile));
+	}
+}
+
 #endif /* CONFIG_NATIVE_WINDOWS */
diff --git a/src/ap/ieee802_11.h b/src/ap/ieee802_11.h
index cc05c98a1..f0d63bcee 100644
--- a/src/ap/ieee802_11.h
+++ b/src/ap/ieee802_11.h
@@ -86,7 +86,8 @@ void hostapd_get_eht_capab(struct hostapd_data *hapd,
 			   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);
+			     struct sta_info *info, bool include_mld_id,
+			     bool mlo_probe_resp);
 struct wpabuf *hostapd_ml_auth_resp(struct hostapd_data *hapd);
 const u8 *hostapd_process_ml_auth(struct hostapd_data *hapd,
 				  const struct ieee80211_mgmt *mgmt,
@@ -238,4 +239,6 @@ void punct_update_legacy_bw(u16 bitmap, u8 pri_chan,
 			    enum oper_chan_width *width, u8 *seg0, u8 *seg1);
 
 bool hostapd_is_mld_ap(struct hostapd_data *hapd);
+
+void hostapd_ml_probe_resp(struct hostapd_data *hapd);
 #endif /* IEEE802_11_H */
diff --git a/src/ap/ieee802_11_eht.c b/src/ap/ieee802_11_eht.c
index 12a053fa9..f67eeb635 100644
--- a/src/ap/ieee802_11_eht.c
+++ b/src/ap/ieee802_11_eht.c
@@ -420,7 +420,8 @@ void hostapd_get_eht_capab(struct hostapd_data *hapd,
 }
 
 u8 *hostapd_eid_eht_basic_ml(struct hostapd_data *hapd, u8 *eid,
-			     struct sta_info *info, bool include_mld_id)
+			     struct sta_info *info, bool include_mld_id,
+			     bool mlo_probe_resp)
 {
 	struct wpabuf *buf;
 	u16 control;
@@ -487,9 +488,97 @@ u8 *hostapd_eid_eht_basic_ml(struct hostapd_data *hapd, u8 *eid,
 		wpabuf_put_u8(buf, hapd->conf->mld_id);
 	}
 
-	if (!info)
+	if (!info && !mlo_probe_resp)
 		goto out;
 
+	if (mlo_probe_resp) {
+		for (link_id = 0; link_id < MAX_NUM_MLD_LINKS; link_id++) {
+			struct hostapd_data *h = hostapd_mld_get_link_bss(hapd, link_id);
+			const size_t fixed_len = 22;
+			size_t total_len;
+
+			/* skip the local one */
+			if (link_id == hapd->mld_link_id)
+				continue;
+
+			if (!h || link_id != h->mld_link_id)
+				continue;
+
+			total_len = fixed_len + h->resp_sta_profile_len;
+
+			wpa_printf(MSG_DEBUG, "MLD: probe resp per-sta profile link=%d",
+					      h->mld_link_id);
+			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);
+
+			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 length */
+			wpabuf_put_u8(buf, fixed_len - 2);
+
+			wpabuf_put_data(buf, h->own_addr, ETH_ALEN);
+
+			/* TODO: currently assume same beacon interval */
+			wpabuf_put_le16(buf, hapd->iconf->beacon_int);
+
+			/*
+			* TODO: currently setting TSF offset to zero. However this
+			* information needs to come from the driver
+			*/
+			wpabuf_put_le32(buf, 0);
+			wpabuf_put_le32(buf, 0);
+
+			/* TODO: currently assume same DTIM information */
+			wpabuf_put_le16(buf, hapd->conf->dtim_period);
+
+			/* TODO: currently hard code the BSS change parameters to 0x1 */
+			wpabuf_put_u8(buf, 0x1);
+
+			/* Fragment the sub element if needed */
+			if (total_len <= 255) {
+				wpabuf_put_data(buf, h->resp_sta_profile,
+						h->resp_sta_profile_len);
+			} else {
+				ptr = h->resp_sta_profile;
+				len = h->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;
+				}
+			}
+		}
+
+		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];
-- 
2.39.2

