diff mbox series

[20/22] hostapd: fix RNR building for co-location and MLO

Message ID 20240328181652.2956122-21-quic_adisi@quicinc.com
State Accepted
Headers show
Series [01/22] hostapd: MLO: fix for_each_mld_link macro | expand

Commit Message

Aditya Kumar Singh March 28, 2024, 6:16 p.m. UTC
Currently with MLO changes, RNR formation for co-location or MLO
was not working as expected. Hence make it work as per the
expectation.

For example, during co-location, if the BSS is also its ML partner
then there is no need to include a separate TBTT for it.

Also, during co-location, if the BSS is not its partner but it is
ML capable, then the TBTT length should be 16 bytes and it should
include the MLD Parameters for it in the RNR.

During co-location, for a given Neighbor AP (operating on a given
channel and op-class) if it has BSSes which are ML capable as well
as BSSes which are not, then there should be two Neighbor AP Info
present. One indicating TBTT length as 13 bytes and one indicating
TBTT info length as 16 bytes.

Signed-off-by: Aditya Kumar Singh <quic_adisi@quicinc.com>
---
 src/ap/beacon.c     |  12 +-
 src/ap/ieee802_11.c | 387 ++++++++++++++++++++++++++++++++------------
 src/ap/ieee802_11.h |   5 +-
 3 files changed, 290 insertions(+), 114 deletions(-)
diff mbox series

Patch

diff --git a/src/ap/beacon.c b/src/ap/beacon.c
index b780d98e4bab..4354dfae35f1 100644
--- a/src/ap/beacon.c
+++ b/src/ap/beacon.c
@@ -677,7 +677,7 @@  static size_t hostapd_probe_resp_elems_len(struct hostapd_data *hapd,
 					 params->known_bss,
 					 params->known_bss_len, NULL);
 	if (!params->is_ml_sta_info)
-		buflen += hostapd_eid_rnr_len(hapd, WLAN_FC_STYPE_PROBE_RESP);
+		buflen += hostapd_eid_rnr_len(hapd, WLAN_FC_STYPE_PROBE_RESP, true);
 	buflen += hostapd_mbo_ie_len(hapd);
 	buflen += hostapd_eid_owe_trans_len(hapd);
 	buflen += hostapd_eid_dpp_cc_len(hapd);
@@ -797,7 +797,7 @@  static u8 * hostapd_probe_resp_fill_elems(struct hostapd_data *hapd,
 	pos = hostapd_eid_wb_chsw_wrapper(hapd, pos);
 
 	if (!params->is_ml_sta_info)
-		pos = hostapd_eid_rnr(hapd, pos, WLAN_FC_STYPE_PROBE_RESP);
+		pos = hostapd_eid_rnr(hapd, pos, WLAN_FC_STYPE_PROBE_RESP, true);
 	pos = hostapd_eid_fils_indic(hapd, pos, 0);
 	pos = hostapd_get_rsnxe(hapd, pos, epos - pos);
 
@@ -1946,7 +1946,7 @@  static u8 * hostapd_gen_fils_discovery(struct hostapd_data *hapd, size_t *len)
 		total_len += 3;
 	}
 
-	total_len += hostapd_eid_rnr_len(hapd, WLAN_FC_STYPE_ACTION);
+	total_len += hostapd_eid_rnr_len(hapd, WLAN_FC_STYPE_ACTION, true);
 
 	pos = hostapd_eid_fils_indic(hapd, buf, 0);
 	buf_len = pos - buf;
@@ -2020,7 +2020,7 @@  static u8 * hostapd_gen_fils_discovery(struct hostapd_data *hapd, size_t *len)
 	/* Fill in the Length field value */
 	*length_pos = pos - (length_pos + 1);
 
-	pos = hostapd_eid_rnr(hapd, pos, WLAN_FC_STYPE_ACTION);
+	pos = hostapd_eid_rnr(hapd, pos, WLAN_FC_STYPE_ACTION, true);
 
 	/* FILS Indication element */
 	if (buf_len) {
@@ -2126,7 +2126,7 @@  int ieee802_11_build_ap_params(struct hostapd_data *hapd,
 	if (hapd->iconf->mbssid == ENHANCED_MBSSID_ENABLED &&
 	    hapd == hostapd_mbssid_get_tx_bss(hapd))
 		tail_len += 5; /* Multiple BSSID Configuration element */
-	tail_len += hostapd_eid_rnr_len(hapd, WLAN_FC_STYPE_BEACON);
+	tail_len += hostapd_eid_rnr_len(hapd, WLAN_FC_STYPE_BEACON, true);
 	tail_len += hostapd_mbo_ie_len(hapd);
 	tail_len += hostapd_eid_owe_trans_len(hapd);
 	tail_len += hostapd_eid_dpp_cc_len(hapd);
@@ -2262,7 +2262,7 @@  int ieee802_11_build_ap_params(struct hostapd_data *hapd,
 
 	tailpos = hostapd_eid_wb_chsw_wrapper(hapd, tailpos);
 
-	tailpos = hostapd_eid_rnr(hapd, tailpos, WLAN_FC_STYPE_BEACON);
+	tailpos = hostapd_eid_rnr(hapd, tailpos, WLAN_FC_STYPE_BEACON, true);
 	tailpos = hostapd_eid_fils_indic(hapd, tailpos, 0);
 	tailpos = hostapd_get_rsnxe(hapd, tailpos, tailend - tailpos);
 	tailpos = hostapd_eid_mbssid_config(hapd, tailpos,
diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c
index 7ee18f4ae73d..9a23c72405b9 100644
--- a/src/ap/ieee802_11.c
+++ b/src/ap/ieee802_11.c
@@ -7273,20 +7273,21 @@  static size_t
 hostapd_eid_rnr_iface_len(struct hostapd_data *hapd,
 			  struct hostapd_data *reporting_hapd,
 			  size_t *current_len,
-			  struct mbssid_ie_profiles *skip_profiles)
+			  struct mbssid_ie_profiles *skip_profiles,
+			  bool mld_update)
 {
 	size_t total_len = 0, len = *current_len;
-	int tbtt_count = 0;
-	size_t i, start = 0;
-	bool ap_mld = false;
+	int tbtt_count, total_tbtt_count = 0;
+	size_t i, start;
+	u8 tbtt_info_len = mld_update ? RNR_TBTT_INFO_MLD_LEN : RNR_TBTT_INFO_LEN;
 
-#ifdef CONFIG_IEEE80211BE
-	ap_mld = !!hapd->conf->mld_ap;
-#endif /* CONFIG_IEEE80211BE */
+repeat_rnr_len:
+	start = 0;
+	tbtt_count = 0;
 
 	while (start < hapd->iface->num_bss) {
 		if (!len ||
-		    len + RNR_TBTT_HEADER_LEN + RNR_TBTT_INFO_LEN > 255 ||
+		    len + RNR_TBTT_HEADER_LEN + tbtt_info_len > 255 ||
 		    tbtt_count >= RNR_TBTT_INFO_COUNT_MAX) {
 			len = RNR_HEADER_LEN;
 			total_len += RNR_HEADER_LEN;
@@ -7298,10 +7299,15 @@  hostapd_eid_rnr_iface_len(struct hostapd_data *hapd,
 
 		for (i = start; i < hapd->iface->num_bss; i++) {
 			struct hostapd_data *bss = hapd->iface->bss[i];
+			bool ap_mld = false;
 
 			if (!bss || !bss->conf || !bss->started)
 				continue;
 
+#ifdef CONFIG_IEEE80211BE
+			ap_mld = !!bss->conf->mld_ap;
+#endif /* CONFIG_IEEE80211BE */
+
 			if (bss == reporting_hapd ||
 			    bss->conf->ignore_broadcast_ssid)
 				continue;
@@ -7310,23 +7316,71 @@  hostapd_eid_rnr_iface_len(struct hostapd_data *hapd,
 			    i >= skip_profiles->start && i < skip_profiles->end)
 				continue;
 
-			if (len + RNR_TBTT_INFO_LEN > 255 ||
+			/* No need to report if length is for normal TBTT and the BSS
+			 * is a MLD. MLD TBTT will include this.
+			 */
+			if (tbtt_info_len == RNR_TBTT_INFO_LEN && ap_mld)
+				continue;
+
+			/* No need to report if length is for MLD TBTT and the BSS
+			 * is not MLD. Normal TBTT will include this.
+			 */
+			if (tbtt_info_len == RNR_TBTT_INFO_MLD_LEN && !ap_mld)
+				continue;
+
+#ifdef CONFIG_IEEE80211BE
+			/* If building for co-location and they are ML partners,
+			 * no need to include since the ML RNR will carry this.
+			 */
+			if (!mld_update && hostapd_is_ml_partner(reporting_hapd, bss))
+				continue;
+
+			/* If building for ML RNR and they are not ML parnters,
+			 * don't include.
+			 */
+			if (mld_update && !hostapd_is_ml_partner(reporting_hapd, bss))
+				continue;
+#endif /* CONFIG_IEEE80211BE */
+
+			if (len + tbtt_info_len > 255 ||
 			    tbtt_count >= RNR_TBTT_INFO_COUNT_MAX)
 				break;
 
-			if (!ap_mld) {
-				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;
-			}
+			len += tbtt_info_len;
+			total_len += tbtt_info_len;
 			tbtt_count++;
 		}
 		start = i;
 	}
 
-	if (!tbtt_count)
+	total_tbtt_count += tbtt_count;
+
+	/* If building for co-location, re-build again but this time include
+	 * ML TBTTs.
+	 */
+	if (!mld_update && tbtt_info_len == RNR_TBTT_INFO_LEN) {
+		tbtt_info_len = RNR_TBTT_INFO_MLD_LEN;
+
+		/* If no TBTT was found, then adjust the len and total_len since
+		 * it would have incremented before we checked all bss.
+		 */
+		if (!tbtt_count) {
+			len -= RNR_TBTT_HEADER_LEN;
+			total_len -= RNR_TBTT_HEADER_LEN;
+		}
+
+		goto repeat_rnr_len;
+	}
+
+	/* this is possible when it re-built and in that no suitable TBTT was
+	 * found. Adjust the length accordingly.
+	 */
+	if (!tbtt_count && total_tbtt_count) {
+		len -= RNR_TBTT_HEADER_LEN;
+		total_len -= RNR_TBTT_HEADER_LEN;
+	}
+
+	if (!total_tbtt_count)
 		total_len = 0;
 	else
 		*current_len = len;
@@ -7375,8 +7429,8 @@  static enum colocation_mode get_colocation_mode(struct hostapd_data *hapd)
 }
 
 
-static size_t hostapd_eid_rnr_multi_iface_len(struct hostapd_data *hapd,
-					      size_t *current_len)
+static size_t hostapd_eid_rnr_colocation_len(struct hostapd_data *hapd,
+					     size_t *current_len)
 {
 	struct hostapd_iface *iface;
 	size_t len = 0;
@@ -7387,34 +7441,57 @@  static size_t hostapd_eid_rnr_multi_iface_len(struct hostapd_data *hapd,
 
 	for (i = 0; i < hapd->iface->interfaces->count; i++) {
 		iface = hapd->iface->interfaces->iface[i];
-		bool ap_mld = false;
-
-#ifdef CONFIG_IEEE80211BE
-		if (hostapd_is_ml_partner(hapd, iface->bss[0]))
-			ap_mld = true;
-#endif /* CONFIG_IEEE80211BE */
 
-		if (iface == hapd->iface ||
-		    !(is_6ghz_op_class(iface->conf->op_class) || ap_mld))
+		if (!iface || iface == hapd->iface ||
+		    !is_6ghz_op_class(iface->conf->op_class))
 			continue;
 
 		len += hostapd_eid_rnr_iface_len(iface->bss[0], hapd,
-						 current_len, NULL);
+						 current_len, NULL, false);
 	}
 
 	return len;
 }
 
-
-size_t hostapd_eid_rnr_len(struct hostapd_data *hapd, u32 type)
+static size_t hostapd_eid_rnr_mlo_len(struct hostapd_data *hapd, u32 type,
+				      size_t *current_len)
 {
-	size_t total_len = 0, current_len = 0;
-	enum colocation_mode mode = get_colocation_mode(hapd);
-	bool ap_mld = false;
+	size_t len = 0;
 
 #ifdef CONFIG_IEEE80211BE
-	ap_mld = !!hapd->conf->mld_ap;
+	struct hostapd_iface *iface;
+	size_t i;
+
+	if (!hapd->iface || !hapd->iface->interfaces)
+		return 0;
+
+	if (!hapd->conf->mld_ap)
+		return 0;
+
+	/* TODO allow for FILS/Action as well */
+	if (type != WLAN_FC_STYPE_BEACON && type != WLAN_FC_STYPE_PROBE_RESP)
+		return 0;
+
+	for (i = 0; i < hapd->iface->interfaces->count; i++) {
+		iface = hapd->iface->interfaces->iface[i];
+
+		if (!iface || iface == hapd->iface)
+			continue;
+
+		if (hapd->iface->freq == iface->freq)
+			continue;
+
+		len += hostapd_eid_rnr_iface_len(iface->bss[0], hapd,
+						 current_len, NULL, true);
+	}
 #endif /* CONFIG_IEEE80211BE */
+	return len;
+}
+
+size_t hostapd_eid_rnr_len(struct hostapd_data *hapd, u32 type, bool include_mld_params)
+{
+	size_t total_len = 0, current_len = 0;
+	enum colocation_mode mode = get_colocation_mode(hapd);
 
 	switch (type) {
 	case WLAN_FC_STYPE_BEACON:
@@ -7423,29 +7500,35 @@  size_t hostapd_eid_rnr_len(struct hostapd_data *hapd, u32 type)
 		/* fallthrough */
 
 	case WLAN_FC_STYPE_PROBE_RESP:
-		if (mode == COLOCATED_LOWER_BAND || ap_mld)
+		if (mode == COLOCATED_LOWER_BAND)
 			total_len +=
-				hostapd_eid_rnr_multi_iface_len(hapd,
-								&current_len);
+				hostapd_eid_rnr_colocation_len(hapd,
+							       &current_len);
 
 		if (hapd->conf->rnr && hapd->iface->num_bss > 1 &&
 		    !hapd->iconf->mbssid)
 			total_len += hostapd_eid_rnr_iface_len(hapd, hapd,
 							       &current_len,
-							       NULL);
+							       NULL, false);
 		break;
 
 	case WLAN_FC_STYPE_ACTION:
 		if (hapd->iface->num_bss > 1 && mode == STANDALONE_6GHZ)
 			total_len += hostapd_eid_rnr_iface_len(hapd, hapd,
 							       &current_len,
-							       NULL);
+							       NULL, false);
 		break;
 
 	default:
 		break;
 	}
 
+	/* For EMA Beacons, MLD neighbor repoting is added as part of mbssid rnr */
+	if (include_mld_params &&
+	    (type != WLAN_FC_STYPE_BEACON ||
+	     hapd->iconf->mbssid != ENHANCED_MBSSID_ENABLED))
+		total_len += hostapd_eid_rnr_mlo_len(hapd, type, &current_len);
+
 	return total_len;
 }
 
@@ -7509,13 +7592,14 @@  static bool hostapd_eid_rnr_bss(struct hostapd_data *hapd,
 				struct hostapd_data *reporting_hapd,
 				struct mbssid_ie_profiles *skip_profiles,
 				size_t i, u8 *tbtt_count, size_t *len,
-				u8 **pos)
+				u8 **pos, u8 **tbtt_count_pos, u8 tbtt_info_len,
+				u8 op_class, bool mld_update)
 {
 	struct hostapd_iface *iface = hapd->iface;
 	struct hostapd_data *bss = iface->bss[i];
 	u8 bss_param = 0;
-	bool ap_mld = false;
 	u8 *eid = *pos;
+	bool ap_mld = false;
 
 #ifdef CONFIG_IEEE80211BE
 	ap_mld = !!hapd->conf->mld_ap;
@@ -7529,10 +7613,47 @@  static bool hostapd_eid_rnr_bss(struct hostapd_data *hapd,
 	    && i >= skip_profiles->start && i < skip_profiles->end)
 		return false;
 
+	/* No need to report if length is for normal TBTT and the BSS
+	 * is a MLD. MLD TBTT will include this.
+	 */
+	if (tbtt_info_len == RNR_TBTT_INFO_LEN && ap_mld)
+		return false;
+
+	/* No need to report if length is for MLD TBTT and the BSS
+	 * is not MLD. Normal TBTT will include this.
+	 */
+	if (tbtt_info_len == RNR_TBTT_INFO_MLD_LEN && !ap_mld)
+		return false;
+
+#ifdef CONFIG_IEEE80211BE
+	/* If building for co-location and they are ML partners,
+	 * no need to include since the ML RNR will carry this.
+	 */
+	if (!mld_update && hostapd_is_ml_partner(reporting_hapd, bss))
+		return false;
+
+	/* If building for ML RNR and they are not ML parnters,
+	 * don't include.
+	 */
+	if (mld_update && !hostapd_is_ml_partner(reporting_hapd, bss))
+		return false;
+#endif /* CONFIG_IEEE80211BE */
+
 	if (*len + RNR_TBTT_INFO_LEN > 255 ||
 	    *tbtt_count >= RNR_TBTT_INFO_COUNT_MAX)
 		return true;
 
+	if (!(*tbtt_count)) {
+		/* Add Neighbor report header info only if there is at least
+		 * one tbtt info available
+		 */
+		*tbtt_count_pos = eid++;
+		*eid++ = tbtt_info_len;
+		*eid++ = op_class;
+		*eid++ = bss->iconf->channel;
+		*len += RNR_TBTT_HEADER_LEN;
+	}
+
 	*eid++ = RNR_NEIGHBOR_AP_OFFSET_UNKNOWN;
 	os_memcpy(eid, bss->own_addr, ETH_ALEN);
 	eid += ETH_ALEN;
@@ -7556,29 +7677,36 @@  static bool hostapd_eid_rnr_bss(struct hostapd_data *hapd,
 	*eid++ = bss_param;
 	*eid++ = RNR_20_MHZ_PSD_MAX_TXPOWER;
 
-	if (!ap_mld) {
-		*len += RNR_TBTT_INFO_LEN;
-	} else {
 #ifdef CONFIG_IEEE80211BE
-		u8 param_ch = hapd->eht_mld_bss_param_change;
-
-		if (hostapd_is_ml_partner(bss, reporting_hapd))
-			*eid++ = 0;
-		else
-			*eid++ = hostapd_get_mld_id(hapd);
-
-		*eid++ = hapd->mld_link_id | ((param_ch & 0xF) << 4);
-		*eid = (param_ch >> 4) & 0xF;
+	if (ap_mld) {
+		u8 param_ch = bss->eht_mld_bss_param_change;
+		bool is_partner;
+
+		/* If bss is not partner of the reporting_hapd then
+		 *  a) MLD ID advertised shall be 255.
+		 *  b) Link ID advertised shall be 15.
+		 *  c) BPCC advertised shall be 255
+		 */
+		is_partner = hostapd_is_ml_partner(bss, reporting_hapd);
+		/* MLD ID */
+		*eid++ = is_partner ? hostapd_get_mld_id(bss) : 0xFF;
+		/* Link ID (Bit 3 to Bit 0)
+		 * BPCC (Bit 4 to Bit 7)
+		 */
+		*eid++ = is_partner ?
+			 bss->mld_link_id | ((param_ch & 0xF) << 4) :
+			 (MAX_NUM_MLD_LINKS | 0xF0);
+		/* BPCC (Bit 3 to Bit 0) */
+		*eid = is_partner ? ((param_ch & 0xF0) >> 4) : 0x0F;
 #ifdef CONFIG_TESTING_OPTIONS
-		if (hapd->conf->mld_indicate_disabled)
+		if (bss->conf->mld_indicate_disabled)
 			*eid |= RNR_TBTT_INFO_MLD_PARAM2_LINK_DISABLED;
 #endif /* CONFIG_TESTING_OPTIONS */
 		eid++;
-
-		*len += RNR_TBTT_INFO_MLD_LEN;
-#endif /* CONFIG_IEEE80211BE */
 	}
+#endif /* CONFIG_IEEE80211BE */
 
+	*len += tbtt_info_len;
 	(*tbtt_count)++;
 	*pos = eid;
 
@@ -7589,18 +7717,16 @@  static bool hostapd_eid_rnr_bss(struct hostapd_data *hapd,
 static u8 * hostapd_eid_rnr_iface(struct hostapd_data *hapd,
 				  struct hostapd_data *reporting_hapd,
 				  u8 *eid, size_t *current_len,
-				  struct mbssid_ie_profiles *skip_profiles)
+				  struct mbssid_ie_profiles *skip_profiles,
+				  bool mld_update)
 {
 	struct hostapd_iface *iface = hapd->iface;
-	size_t i, start = 0;
+	size_t i, start;
 	size_t len = *current_len;
-	u8 *tbtt_count_pos, *eid_start = eid, *size_offset = (eid - len) + 1;
-	u8 tbtt_count = 0, op_class, channel;
-	bool ap_mld = false;
-
-#ifdef CONFIG_IEEE80211BE
-	ap_mld = !!hapd->conf->mld_ap;
-#endif /* CONFIG_IEEE80211BE */
+	u8 *eid_start = eid, *size_offset = (eid - len) + 1;
+	u8 *tbtt_count_pos = size_offset + 1;
+	u8 tbtt_count, total_tbtt_count = 0, op_class, channel;
+	u8 tbtt_info_len = mld_update ? RNR_TBTT_INFO_MLD_LEN : RNR_TBTT_INFO_LEN;
 
 	if (!(iface->drv_flags & WPA_DRIVER_FLAGS_AP_CSA) || !iface->freq)
 		return eid;
@@ -7612,9 +7738,12 @@  static u8 * hostapd_eid_rnr_iface(struct hostapd_data *hapd,
 	    NUM_HOSTAPD_MODES)
 		return eid;
 
+repeat_rnr:
+	start = 0;
+	tbtt_count = 0;
 	while (start < iface->num_bss) {
 		if (!len ||
-		    len + RNR_TBTT_HEADER_LEN + RNR_TBTT_INFO_LEN > 255 ||
+		    len + RNR_TBTT_HEADER_LEN + tbtt_info_len > 255 ||
 		    tbtt_count >= RNR_TBTT_INFO_COUNT_MAX) {
 			eid_start = eid;
 			*eid++ = WLAN_EID_REDUCED_NEIGHBOR_REPORT;
@@ -7623,34 +7752,42 @@  static u8 * hostapd_eid_rnr_iface(struct hostapd_data *hapd,
 			tbtt_count = 0;
 		}
 
-		tbtt_count_pos = eid++;
-		*eid++ = ap_mld ? RNR_TBTT_INFO_MLD_LEN : RNR_TBTT_INFO_LEN;
-		*eid++ = op_class;
-		*eid++ = hapd->iconf->channel;
-		len += RNR_TBTT_HEADER_LEN;
-
 		for (i = start; i < iface->num_bss; i++) {
 			if (hostapd_eid_rnr_bss(hapd, reporting_hapd,
 						skip_profiles, i,
-						&tbtt_count, &len, &eid))
+						&tbtt_count, &len, &eid,
+						&tbtt_count_pos, tbtt_info_len,
+						op_class, mld_update))
 				break;
 		}
 
 		start = i;
-		*tbtt_count_pos = RNR_TBTT_INFO_COUNT(tbtt_count - 1);
-		*size_offset = (eid - size_offset) - 1;
+
+		if (tbtt_count) {
+			*tbtt_count_pos = RNR_TBTT_INFO_COUNT(tbtt_count - 1);
+			*size_offset = (eid - size_offset) - 1;
+		}
+	}
+
+	total_tbtt_count += tbtt_count;
+
+	/* If building for co-location, re-build again but this time include
+	 * ML TBTTs.
+	 */
+	if (!mld_update && tbtt_info_len == RNR_TBTT_INFO_LEN) {
+		tbtt_info_len = RNR_TBTT_INFO_MLD_LEN;
+		goto repeat_rnr;
 	}
 
-	if (tbtt_count == 0)
+	if (!total_tbtt_count)
 		return eid_start;
 
 	*current_len = len;
 	return eid;
 }
 
-
-static u8 * hostapd_eid_rnr_multi_iface(struct hostapd_data *hapd, u8 *eid,
-					size_t *current_len)
+u8 *hostapd_eid_rnr_colocation(struct hostapd_data *hapd, u8 *eid,
+			       size_t *current_len)
 {
 	struct hostapd_iface *iface;
 	size_t i;
@@ -7660,35 +7797,56 @@  static u8 * hostapd_eid_rnr_multi_iface(struct hostapd_data *hapd, u8 *eid,
 
 	for (i = 0; i < hapd->iface->interfaces->count; i++) {
 		iface = hapd->iface->interfaces->iface[i];
-		bool ap_mld = false;
 
-#ifdef CONFIG_IEEE80211BE
-		if (hostapd_is_ml_partner(hapd, iface->bss[0]))
-			ap_mld = true;
-#endif /* CONFIG_IEEE80211BE */
-
-		if (iface == hapd->iface ||
-		    !(is_6ghz_op_class(iface->conf->op_class) || ap_mld))
+		if (!iface || iface == hapd->iface ||
+		    !is_6ghz_op_class(iface->conf->op_class))
 			continue;
 
 		eid = hostapd_eid_rnr_iface(iface->bss[0], hapd, eid,
-					    current_len, NULL);
+					    current_len, NULL, false);
 	}
 
 	return eid;
 }
 
+u8 *hostapd_eid_rnr_mlo(struct hostapd_data *hapd, u32 type,
+			u8 *eid, size_t *current_len)
+{
+#ifdef CONFIG_IEEE80211BE
+	struct hostapd_iface *iface;
+	size_t i;
+
+	if (!hapd->iface || !hapd->iface->interfaces)
+		return eid;
+
+	if (!hapd->conf->mld_ap)
+		return eid;
+
+	/* TODO allow for FILS/Action as well */
+	if (type != WLAN_FC_STYPE_BEACON && type != WLAN_FC_STYPE_PROBE_RESP)
+		return eid;
+
+	for (i = 0; i < hapd->iface->interfaces->count; i++) {
+		iface = hapd->iface->interfaces->iface[i];
+
+		if (!iface || iface == hapd->iface)
+			continue;
+
+		if (hapd->iface->freq == iface->freq)
+			continue;
 
-u8 * hostapd_eid_rnr(struct hostapd_data *hapd, u8 *eid, u32 type)
+		eid = hostapd_eid_rnr_iface(iface->bss[0], hapd, eid,
+					    current_len, NULL, true);
+	}
+#endif /* CONFIG_IEEE80211BE */
+	return eid;
+}
+
+u8 *hostapd_eid_rnr(struct hostapd_data *hapd, u8 *eid, u32 type, bool include_mld_params)
 {
 	u8 *eid_start = eid;
 	size_t current_len = 0;
 	enum colocation_mode mode = get_colocation_mode(hapd);
-	bool ap_mld = false;
-
-#ifdef CONFIG_IEEE80211BE
-	ap_mld = !!hapd->conf->mld_ap;
-#endif /* CONFIG_IEEE80211BE */
 
 	switch (type) {
 	case WLAN_FC_STYPE_BEACON:
@@ -7697,26 +7855,34 @@  u8 * hostapd_eid_rnr(struct hostapd_data *hapd, u8 *eid, u32 type)
 		/* fallthrough */
 
 	case WLAN_FC_STYPE_PROBE_RESP:
-		if (mode == COLOCATED_LOWER_BAND || ap_mld)
-			eid = hostapd_eid_rnr_multi_iface(hapd, eid,
-							  &current_len);
+		if (mode == COLOCATED_LOWER_BAND)
+			eid = hostapd_eid_rnr_colocation(hapd, eid,
+							 &current_len);
 
 		if (hapd->conf->rnr && hapd->iface->num_bss > 1 &&
 		    !hapd->iconf->mbssid)
 			eid = hostapd_eid_rnr_iface(hapd, hapd, eid,
-						    &current_len, NULL);
+						    &current_len, NULL,
+						    false);
 		break;
 
 	case WLAN_FC_STYPE_ACTION:
 		if (hapd->iface->num_bss > 1 && mode == STANDALONE_6GHZ)
 			eid = hostapd_eid_rnr_iface(hapd, hapd, eid,
-						    &current_len, NULL);
+						    &current_len, NULL,
+						    false);
 		break;
 
 	default:
 		return eid_start;
 	}
 
+	/* For EMA Beacons, MLD neighbor repoting is added as part of mbssid rnr */
+	if (include_mld_params &&
+	    (type != WLAN_FC_STYPE_BEACON ||
+	     hapd->iconf->mbssid != ENHANCED_MBSSID_ENABLED))
+		eid = hostapd_eid_rnr_mlo(hapd, type, eid, &current_len);
+
 	if (eid == eid_start + 2)
 		return eid_start;
 
@@ -7815,6 +7981,11 @@  size_t hostapd_eid_mbssid_len(struct hostapd_data *hapd, u32 frame_type,
 			      size_t known_bss_len, size_t *rnr_len)
 {
 	size_t len = 0, bss_index = 1;
+	bool ap_mld = false;
+
+#ifdef CONFIG_IEEE80211BE
+	ap_mld = !!hapd->conf->mld_ap;
+#endif /* CONFIG_IEEE80211BE */
 
 	if (!hapd->iconf->mbssid || hapd->iface->num_bss <= 1 ||
 	    (frame_type != WLAN_FC_STYPE_BEACON &&
@@ -7847,12 +8018,12 @@  size_t hostapd_eid_mbssid_len(struct hostapd_data *hapd, u32 frame_type,
 
 			*rnr_len += hostapd_eid_rnr_iface_len(
 				hapd, hostapd_mbssid_get_tx_bss(hapd),
-				&rnr_cur_len, &skip_profiles);
+				&rnr_cur_len, &skip_profiles, ap_mld);
 		}
 	}
 
 	if (hapd->iconf->mbssid == ENHANCED_MBSSID_ENABLED && rnr_len)
-		*rnr_len += hostapd_eid_rnr_len(hapd, frame_type);
+		*rnr_len += hostapd_eid_rnr_len(hapd, frame_type, false);
 
 	return len;
 }
@@ -7978,7 +8149,11 @@  u8 * hostapd_eid_mbssid(struct hostapd_data *hapd, u8 *eid, u8 *end,
 {
 	size_t bss_index = 1, cur_len = 0;
 	u8 elem_index = 0, *rnr_start_eid = rnr_eid;
-	bool add_rnr;
+	bool add_rnr, ap_mld = false;
+
+#ifdef CONFIG_IEEE80211BE
+	ap_mld = !!hapd->conf->mld_ap;
+#endif /* CONFIG_IEEE80211BE */
 
 	if (!hapd->iconf->mbssid || hapd->iface->num_bss <= 1 ||
 	    (frame_stype != WLAN_FC_STYPE_BEACON &&
@@ -8023,7 +8198,7 @@  u8 * hostapd_eid_mbssid(struct hostapd_data *hapd, u8 *eid, u8 *end,
 			cur_len = 0;
 			rnr_eid = hostapd_eid_rnr_iface(
 				hapd, hostapd_mbssid_get_tx_bss(hapd),
-				rnr_eid, &cur_len, &skip_profiles);
+				rnr_eid, &cur_len, &skip_profiles, ap_mld);
 		}
 	}
 
@@ -8035,8 +8210,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_multi_iface(hapd, rnr_eid,
-							      &cur_len);
+			rnr_eid = hostapd_eid_rnr_colocation(hapd, rnr_eid,
+							     &cur_len);
 	}
 
 	return eid;
diff --git a/src/ap/ieee802_11.h b/src/ap/ieee802_11.h
index 262e0ce14ab4..078f4baf94b0 100644
--- a/src/ap/ieee802_11.h
+++ b/src/ap/ieee802_11.h
@@ -225,8 +225,9 @@  void auth_sae_process_commit(void *eloop_ctx, void *user_ctx);
 u8 * hostapd_eid_rsnxe(struct hostapd_data *hapd, u8 *eid, size_t len);
 u16 check_ext_capab(struct hostapd_data *hapd, struct sta_info *sta,
 		    const u8 *ext_capab_ie, size_t ext_capab_ie_len);
-size_t hostapd_eid_rnr_len(struct hostapd_data *hapd, u32 type);
-u8 * hostapd_eid_rnr(struct hostapd_data *hapd, u8 *eid, u32 type);
+size_t hostapd_eid_rnr_len(struct hostapd_data *hapd, u32 type, bool include_mld_params);
+u8 *hostapd_eid_rnr(struct hostapd_data *hapd, u8 *eid, u32 type,
+		    bool include_mld_params);
 int ieee802_11_set_radius_info(struct hostapd_data *hapd, struct sta_info *sta,
 			       int res, struct radius_sta *info);
 size_t hostapd_eid_eht_capab_len(struct hostapd_data *hapd,