[PATCH] AP: Member of an ESS with co-located 2.4/5 GHz APs in 6 GHz
Andrei-Alexandru Bleortu
me at andrei-z.com
Sun Oct 4 07:32:51 PDT 2026
A STA that knows every 6 GHz AP of an ESS has a co-located 2.4/5 GHz AP
can skip scanning the 6 GHz band for it: IEEE Std 802.11-2024 carries
this in the Member Of ESS With 2.4/5 GHz Colocated AP subfield, of the
BSS Parameters of a reported 6 GHz AP in the Reduced Neighbor Report
element and of the BSSID Information in a Neighbor Report element.
hostapd defines both bits but never set them: the condition concerns
the whole ESS, which hostapd cannot see, and is known only to whoever
deploys it (dot11MemberOfColocated6GHzESSOptionActivated).
Add member_of_colocated_6ghz_ess for a 6 GHz BSS to declare it. Its own
Neighbor Report and the Reduced Neighbor Report elements of co-located
2.4/5 GHz BSSs reporting it then set the subfield.
Assisted-by: LLM
Signed-off-by: Andrei-Alexandru Bleortu <me at andrei-z.com>
---
The test is appended to test_he.py where "AP: Set the RNR 20 MHz PSD
subfield for co-located 6 GHz APs" (in the series sent earlier today)
also appends one, so whichever goes in second needs that trivial
merge. The ESS-wide condition is the deployer's to state, hence the
option rather than deriving it from co-located interfaces: hostapd
cannot see other APs of the ESS.
hostapd/config_file.c | 2 ++
hostapd/hostapd.conf | 13 +++++++++
src/ap/ap_config.h | 3 ++
src/ap/ieee802_11.c | 4 +++
src/ap/neighbor_db.c | 3 ++
tests/hwsim/test_he.py | 62 ++++++++++++++++++++++++++++++++++++++++++
6 files changed, 87 insertions(+)
diff --git a/hostapd/config_file.c b/hostapd/config_file.c
index 59827966e..98aa7a9c0 100644
--- a/hostapd/config_file.c
+++ b/hostapd/config_file.c
@@ -3896,6 +3896,8 @@ static int hostapd_config_fill(struct hostapd_config *conf,
return 1;
}
bss->unsol_bcast_probe_resp_interval = val;
+ } else if (os_strcmp(buf, "member_of_colocated_6ghz_ess") == 0) {
+ bss->member_of_colocated_6ghz_ess = atoi(pos);
#ifdef CONFIG_AFC
} else if (os_strcmp(buf, "afcd_sock") == 0) {
os_free(conf->afc.socket);
diff --git a/hostapd/hostapd.conf b/hostapd/hostapd.conf
index 0e9001221..e19bd3334 100644
--- a/hostapd/hostapd.conf
+++ b/hostapd/hostapd.conf
@@ -1038,6 +1038,19 @@ wmm_ac_vo_acm=0
# Valid range: 0..20 TUs; default is 0 (disabled)
#unsol_bcast_probe_resp_interval=0
+# Member of an ESS whose 6 GHz APs all have a co-located 2.4/5 GHz AP
+# (dot11MemberOfColocated6GHzESSOptionActivated, IEEE Std 802.11-2024)
+# This is for the 6 GHz band only and states a property of the whole ESS that
+# hostapd cannot see: set it only if no AP of the ESS operates in the 6 GHz
+# band without one in the 2.4 or 5 GHz band in the same colocated AP set. The
+# BSS then sets the Member Of ESS With 2.4/5 GHz Colocated AP subfield in its
+# own Neighbor Report and is reported with it in the Reduced Neighbor Report
+# element of the co-located 2.4/5 GHz BSSs, so that STAs that received one can
+# skip scanning the 6 GHz band for this ESS.
+# 0 = disabled (default)
+# 1 = enabled
+#member_of_colocated_6ghz_ess=0
+
#channel_usage: Whether Channel Usage procedures is supported by AP.
# 0 = Channel Usage support is disabled (default)
# 1 = Channel Usage support is enabled
diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h
index c96481df7..8bf72d1d6 100644
--- a/src/ap/ap_config.h
+++ b/src/ap/ap_config.h
@@ -995,6 +995,9 @@ struct hostapd_bss_config {
unsigned int unsol_bcast_probe_resp_interval;
+ /* dot11MemberOfColocated6GHzESSOptionActivated (6 GHz only) */
+ bool member_of_colocated_6ghz_ess;
+
u8 ext_capa_mask[EXT_CAPA_MAX_LEN];
u8 ext_capa[EXT_CAPA_MAX_LEN];
diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c
index 219a94b0f..f374b0cfa 100644
--- a/src/ap/ieee802_11.c
+++ b/src/ap/ieee802_11.c
@@ -10860,6 +10860,10 @@ static bool hostapd_eid_rnr_bss(struct hostapd_data *hapd,
bss->conf->unsol_bcast_probe_resp_interval)
bss_param |= RNR_BSS_PARAM_UNSOLIC_PROBE_RESP_ACTIVE;
+ if (is_6ghz_op_class(op_class) &&
+ bss->conf->member_of_colocated_6ghz_ess)
+ bss_param |= RNR_BSS_PARAM_MEMBER_CO_LOCATED_ESS;
+
bss_param |= RNR_BSS_PARAM_CO_LOCATED;
*eid++ = bss_param;
diff --git a/src/ap/neighbor_db.c b/src/ap/neighbor_db.c
index fd4f881df..d20347257 100644
--- a/src/ap/neighbor_db.c
+++ b/src/ap/neighbor_db.c
@@ -290,6 +290,9 @@ void hostapd_neighbor_set_own_report(struct hostapd_data *hapd)
&op_class, &channel) ==
NUM_HOSTAPD_MODES)
return;
+ if (is_6ghz_op_class(op_class) &&
+ hapd->conf->member_of_colocated_6ghz_ess)
+ bssid_info |= NEI_REP_BSSID_INFO_MEMBER_OF_ESS_WITH_COLOC_AP;
width = hostapd_get_nr_chan_width(hapd, ht, vht, he, oper_chwidth,
secondary_channel);
diff --git a/tests/hwsim/test_he.py b/tests/hwsim/test_he.py
index 1b1ed664d..dcbd1e284 100644
--- a/tests/hwsim/test_he.py
+++ b/tests/hwsim/test_he.py
@@ -2138,3 +2138,65 @@ def test_he_6ghz_incumbt_sig_intf_sec40(dev, apdev):
expectation)
he_6ghz_incumbt_sig_intf_bw_reduction(dev, apdev, params, '0xC',
expectation)
+
+def run_he_6ghz_colocated_ess(dev, apdev, member):
+ check_sae_capab(dev[0])
+ hapd = None
+ try:
+ params = {"ssid": "coloc", "country_code": "DE", "hw_mode": "g",
+ "channel": "1", "ieee80211ax": "1", "wpa": "2",
+ "rsn_pairwise": "CCMP", "wpa_key_mgmt": "SAE",
+ "sae_pwe": "1", "sae_password": "password",
+ "ieee80211w": "2", "rnr": "1",
+ "rrm_neighbor_report": "1"}
+ hapd = hostapd.add_ap(apdev[0], params)
+ params6 = dict(params)
+ del params6["hw_mode"]
+ params6.update({"op_class": "131", "channel": "5",
+ "member_of_colocated_6ghz_ess": member})
+ hapd6 = hostapd.add_ap(apdev[1], params6, set_channel=False)
+ bssid6 = hapd6.own_addr()
+
+ dev[0].scan_for_bss(hapd.own_addr(), freq="2412", force_scan=True)
+ ie = bytes.fromhex(dev[0].get_bss(hapd.own_addr())['ie'])
+ params_6ghz = None
+ while len(ie) >= 2:
+ eid, data = ie[0], ie[2:2 + ie[1]]
+ ie = ie[2 + ie[1]:]
+ if eid != 201:
+ continue
+ while len(data) >= 4:
+ count = (data[0] >> 4) + 1
+ tlen = data[1]
+ info = data[4:]
+ for k in range(count):
+ tbtt = info[k * tlen:(k + 1) * tlen]
+ if len(tbtt) >= 12 and tbtt[1:7].hex(':') == bssid6:
+ params_6ghz = tbtt[11]
+ data = info[count * tlen:]
+ if params_6ghz is None:
+ raise Exception("6 GHz AP not in the RNR")
+ if bool(params_6ghz & 0x10) != (member == "1"):
+ raise Exception("Unexpected BSS Parameters 0x%02x" % params_6ghz)
+
+ own = [l for l in hapd6.request("SHOW_NEIGHBOR").splitlines()
+ if l.startswith(bssid6)][0]
+ nr = bytes.fromhex(own.split("nr=")[1].split()[0])
+ bssid_info = int.from_bytes(nr[6:10], 'little')
+ if bool(bssid_info & (1 << 18)) != (member == "1"):
+ raise Exception("Unexpected BSSID Information 0x%08x" % bssid_info)
+ except Exception as e:
+ if isinstance(e, Exception) and str(e) == "AP startup failed":
+ if not he_supported():
+ raise HwsimSkip("HE 6 GHz channel not supported in regulatory information")
+ raise
+ finally:
+ clear_regdom(hapd, dev)
+
+def test_he_6ghz_colocated_ess(dev, apdev):
+ """6 GHz AP member of an ESS with co-located 2.4/5 GHz APs"""
+ run_he_6ghz_colocated_ess(dev, apdev, "1")
+
+def test_he_6ghz_colocated_ess_not_member(dev, apdev):
+ """6 GHz AP not declared a member of a co-located ESS"""
+ run_he_6ghz_colocated_ess(dev, apdev, "0")
--
2.47.3
More information about the Hostap
mailing list