From b21f8f47a13ece5af3361cc4b29f83771d530a90 Mon Sep 17 00:00:00 2001
From: Ilan Peer <ilan.peer@intel.com>
Date: Mon, 22 May 2023 22:33:37 +0300
Subject: [PATCH 09/54] AP: Include an RNR element in beacons for MLD AP

- Include RNR element in beacons of MLD APs.
- Whenever a new interface is added to an MLD AP, reconfigure
  the beacon for all other interfaces, to allow updating their RNR
  element.

Signed-off-by: Ilan Peer <ilan.peer@intel.com>
---
 src/ap/beacon.c              | 24 ++++++----
 src/ap/hostapd.c             |  3 ++
 src/ap/ieee802_11.c          | 90 +++++++++++++++++++++++++++++-------
 src/common/ieee802_11_defs.h |  1 +
 4 files changed, 93 insertions(+), 25 deletions(-)

diff --git a/src/ap/beacon.c b/src/ap/beacon.c
index 5e9ced1e2..6253b188d 100644
--- a/src/ap/beacon.c
+++ b/src/ap/beacon.c
@@ -2194,21 +2194,29 @@ int ieee802_11_set_beacon(struct hostapd_data *hapd)
 	if (!iface->interfaces || iface->interfaces->count <= 1)
 		return 0;
 
-	/* Update Beacon frames in case of 6 GHz colocation */
+	/* Update Beacon frames in case of 6 GHz colocation or MLD AP */
 	is_6g = is_6ghz_op_class(iface->conf->op_class);
 	for (j = 0; j < iface->interfaces->count; j++) {
-		struct hostapd_iface *colocated;
+		struct hostapd_iface *other;
+		bool mld_ap = false;
 
-		colocated = iface->interfaces->iface[j];
-		if (colocated == iface || !colocated || !colocated->conf)
+		other = iface->interfaces->iface[j];
+		if (other == iface || !other || !other->conf)
 			continue;
 
-		if (is_6g == is_6ghz_op_class(colocated->conf->op_class))
+#ifdef CONFIG_IEEE80211BE
+		if (hapd->conf->mld_ap && other->bss[0]->conf->mld_ap &&
+		    hapd->conf->mld_id == other->bss[0]->conf->mld_id)
+			mld_ap = true;
+#endif /* CONFIG_IEEE80211BE */
+
+		if (is_6g == is_6ghz_op_class(other->conf->op_class) &&
+		    !mld_ap)
 			continue;
 
-		for (i = 0; i < colocated->num_bss; i++) {
-			if (colocated->bss[i] && colocated->bss[i]->started)
-				__ieee802_11_set_beacon(colocated->bss[i]);
+		for (i = 0; i < other->num_bss; i++) {
+			if (other->bss[i] && other->bss[i]->started)
+				__ieee802_11_set_beacon(other->bss[i]);
 		}
 	}
 
diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c
index 106469ed1..c7ffec343 100644
--- a/src/ap/hostapd.c
+++ b/src/ap/hostapd.c
@@ -2471,6 +2471,9 @@ dfs_offload:
 	for (j = 0; j < iface->num_bss; j++)
 		hostapd_neighbor_set_own_report(iface->bss[j]);
 
+	if (iface->interfaces && iface->interfaces->count > 1)
+		ieee802_11_set_beacons(iface);
+
 	return 0;
 
 fail:
diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c
index 93a6b4f56..331c3dfaa 100644
--- a/src/ap/ieee802_11.c
+++ b/src/ap/ieee802_11.c
@@ -6443,6 +6443,11 @@ hostapd_eid_rnr_iface_len(struct hostapd_data *hapd,
 	size_t total_len = 0, len = *current_len;
 	int tbtt_count = 0;
 	size_t i, start = 0;
+	bool mld_ap = false;
+
+#ifdef CONFIG_IEEE80211BE
+	mld_ap = !!hapd->conf->mld_ap;
+#endif /* CONFIG_IEEE80211BE */
 
 	while (start < hapd->iface->num_bss) {
 		if (!len ||
@@ -6472,8 +6477,13 @@ hostapd_eid_rnr_iface_len(struct hostapd_data *hapd,
 			    tbtt_count >= RNR_TBTT_INFO_COUNT_MAX)
 				break;
 
-			len += RNR_TBTT_INFO_LEN;
-			total_len += RNR_TBTT_INFO_LEN;
+			if (!mld_ap) {
+				len += RNR_TBTT_INFO_LEN;
+				total_len += RNR_TBTT_INFO_LEN;
+			} else {
+				len += RNR_TBTT_INFO_MLD_LEN;
+				total_len += RNR_TBTT_INFO_MLD_LEN;
+			}
 			tbtt_count++;
 		}
 		start = i;
@@ -6528,8 +6538,8 @@ static enum colocation_mode get_colocation_mode(struct hostapd_data *hapd)
 }
 
 
-static size_t hostapd_eid_rnr_colocation_len(struct hostapd_data *hapd,
-					     size_t *current_len)
+static size_t hostapd_eid_rnr_multi_iface_len(struct hostapd_data *hapd,
+					      size_t *current_len)
 {
 	struct hostapd_iface *iface;
 	size_t len = 0;
@@ -6540,9 +6550,16 @@ static size_t hostapd_eid_rnr_colocation_len(struct hostapd_data *hapd,
 
 	for (i = 0; i < hapd->iface->interfaces->count; i++) {
 		iface = hapd->iface->interfaces->iface[i];
+		bool mld_ap = false;
+
+#ifdef CONFIG_IEEE80211BE
+		if (hapd->conf->mld_ap && iface->bss[0]->conf->mld_ap &&
+		    hapd->conf->mld_id == iface->bss[0]->conf->mld_id)
+			mld_ap = true;
+#endif /* CONFIG_IEEE80211BE */
 
 		if (iface == hapd->iface ||
-		    !is_6ghz_op_class(iface->conf->op_class))
+		    !(is_6ghz_op_class(iface->conf->op_class) || mld_ap))
 			continue;
 
 		len += hostapd_eid_rnr_iface_len(iface->bss[0], hapd,
@@ -6557,6 +6574,11 @@ size_t hostapd_eid_rnr_len(struct hostapd_data *hapd, u32 type)
 {
 	size_t total_len = 0, current_len = 0;
 	enum colocation_mode mode = get_colocation_mode(hapd);
+	bool mld_ap = false;
+
+#ifdef CONFIG_IEEE80211BE
+	mld_ap = !!hapd->conf->mld_ap;
+#endif /* CONFIG_IEEE80211BE */
 
 	switch (type) {
 	case WLAN_FC_STYPE_BEACON:
@@ -6565,9 +6587,10 @@ size_t hostapd_eid_rnr_len(struct hostapd_data *hapd, u32 type)
 		/* fallthrough */
 
 	case WLAN_FC_STYPE_PROBE_RESP:
-		if (mode == COLOCATED_LOWER_BAND)
-			total_len += hostapd_eid_rnr_colocation_len(
-				hapd, &current_len);
+		if (mode == COLOCATED_LOWER_BAND || mld_ap)
+			total_len +=
+				hostapd_eid_rnr_multi_iface_len(hapd,
+								&current_len);
 
 		if (hapd->conf->rnr && hapd->iface->num_bss > 1 &&
 		    !hapd->iconf->mbssid)
@@ -6657,6 +6680,11 @@ static u8 * hostapd_eid_rnr_iface(struct hostapd_data *hapd,
 	size_t len = *current_len;
 	u8 *tbtt_count_pos, *eid_start = eid, *size_offset = (eid - len) + 1;
 	u8 tbtt_count = 0, op_class, channel, bss_param;
+	bool mld_ap = false;
+
+#ifdef CONFIG_IEEE80211BE
+	mld_ap = !!hapd->conf->mld_ap;
+#endif /* CONFIG_IEEE80211BE */
 
 	if (!(iface->drv_flags & WPA_DRIVER_FLAGS_AP_CSA) || !iface->freq)
 		return eid;
@@ -6679,7 +6707,12 @@ static u8 * hostapd_eid_rnr_iface(struct hostapd_data *hapd,
 		}
 
 		tbtt_count_pos = eid++;
-		*eid++ = RNR_TBTT_INFO_LEN;
+
+		if (!mld_ap)
+			*eid++ = RNR_TBTT_INFO_LEN;
+		else
+			*eid++ = RNR_TBTT_INFO_MLD_LEN;
+
 		*eid++ = op_class;
 		*eid++ = hapd->iconf->channel;
 		len += RNR_TBTT_HEADER_LEN;
@@ -6728,7 +6761,18 @@ static u8 * hostapd_eid_rnr_iface(struct hostapd_data *hapd,
 
 			*eid++ = bss_param;
 			*eid++ = RNR_20_MHZ_PSD_MAX_TXPOWER - 1;
-			len += RNR_TBTT_INFO_LEN;
+
+			if (!mld_ap) {
+				len += RNR_TBTT_INFO_LEN;
+			} else {
+#ifdef CONFIG_IEEE80211BE
+				*eid++ = hapd->conf->mld_id;
+				*eid++ = hapd->mld_link_id | (1 << 4);
+				*eid++ = 0;
+				len += RNR_TBTT_INFO_MLD_LEN;
+#endif /* CONFIG_IEEE80211BE */
+			}
+
 			tbtt_count += 1;
 		}
 
@@ -6745,7 +6789,7 @@ static u8 * hostapd_eid_rnr_iface(struct hostapd_data *hapd,
 }
 
 
-static u8 * hostapd_eid_rnr_colocation(struct hostapd_data *hapd, u8 *eid,
+static u8 *hostapd_eid_rnr_multi_iface(struct hostapd_data *hapd, u8 *eid,
 				       size_t *current_len)
 {
 	struct hostapd_iface *iface;
@@ -6756,9 +6800,16 @@ static u8 * hostapd_eid_rnr_colocation(struct hostapd_data *hapd, u8 *eid,
 
 	for (i = 0; i < hapd->iface->interfaces->count; i++) {
 		iface = hapd->iface->interfaces->iface[i];
+		bool mld_ap = false;
+
+#ifdef CONFIG_IEEE80211BE
+		if (hapd->conf->mld_ap && iface->bss[0]->conf->mld_ap &&
+		    hapd->conf->mld_id == iface->bss[0]->conf->mld_id)
+			mld_ap = true;
+#endif /* CONFIG_IEEE80211BE */
 
 		if (iface == hapd->iface ||
-		    !is_6ghz_op_class(iface->conf->op_class))
+		    !(is_6ghz_op_class(iface->conf->op_class) || mld_ap))
 			continue;
 
 		eid = hostapd_eid_rnr_iface(iface->bss[0], hapd, eid,
@@ -6774,6 +6825,11 @@ u8 * hostapd_eid_rnr(struct hostapd_data *hapd, u8 *eid, u32 type)
 	u8 *eid_start = eid;
 	size_t current_len = 0;
 	enum colocation_mode mode = get_colocation_mode(hapd);
+	bool mld_ap = false;
+
+#ifdef CONFIG_IEEE80211BE
+	mld_ap = !!hapd->conf->mld_ap;
+#endif /* CONFIG_IEEE80211BE */
 
 	switch (type) {
 	case WLAN_FC_STYPE_BEACON:
@@ -6782,9 +6838,9 @@ u8 * hostapd_eid_rnr(struct hostapd_data *hapd, u8 *eid, u32 type)
 		/* fallthrough */
 
 	case WLAN_FC_STYPE_PROBE_RESP:
-		if (mode == COLOCATED_LOWER_BAND)
-			eid = hostapd_eid_rnr_colocation(hapd, eid,
-							 &current_len);
+		if (mode == COLOCATED_LOWER_BAND || mld_ap)
+			eid = hostapd_eid_rnr_multi_iface(hapd, eid,
+							  &current_len);
 
 		if (hapd->conf->rnr && hapd->iface->num_bss > 1 &&
 		    !hapd->iconf->mbssid)
@@ -7099,8 +7155,8 @@ u8 * hostapd_eid_mbssid(struct hostapd_data *hapd, u8 *eid, u8 *end,
 		if (hapd->conf->rnr)
 			rnr_eid = hostapd_eid_nr_db(hapd, rnr_eid, &cur_len);
 		if (get_colocation_mode(hapd) == COLOCATED_LOWER_BAND)
-			rnr_eid = hostapd_eid_rnr_colocation(hapd, rnr_eid,
-							     &cur_len);
+			rnr_eid = hostapd_eid_rnr_multi_iface(hapd, rnr_eid,
+							      &cur_len);
 	}
 
 	return eid;
diff --git a/src/common/ieee802_11_defs.h b/src/common/ieee802_11_defs.h
index 12789227c..b86906828 100644
--- a/src/common/ieee802_11_defs.h
+++ b/src/common/ieee802_11_defs.h
@@ -2454,6 +2454,7 @@ struct ieee80211_he_mu_edca_parameter_set {
 #define RNR_TBTT_INFO_COUNT(x)                      (((x) & 0xf) << 4)
 #define RNR_TBTT_INFO_COUNT_MAX                     16
 #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)
-- 
2.39.2

