[PATCH v2 3/3] tests: FT-SAE RSNXE Used without RSNXE in Beacon or request

Koral Ilgun koralilgun at gmail.com
Thu Oct 29 09:34:11 PDT 2026


From: Koral Ilgun <koral.ilgun at adtran.com>

Cover rsn_override_omit_rsnxe with a STA that uses the hunting-and-pecking
loop, and RSNXE Used toward a STA that sends no RSNXE with and without
ft_rsnxe_used_only_with_rsnxe.

Signed-off-by: Koral Ilgun <koral.ilgun at adtran.com>
---
 tests/hwsim/test_ap_ft.py | 57 ++++++++++++++++++++++++++++++++++++++-
 1 file changed, 56 insertions(+), 1 deletion(-)

diff --git a/tests/hwsim/test_ap_ft.py b/tests/hwsim/test_ap_ft.py
index 29bec3de9..69472755d 100644
--- a/tests/hwsim/test_ap_ft.py
+++ b/tests/hwsim/test_ap_ft.py
@@ -1140,7 +1140,8 @@ def start_ft_sae(dev, apdev, wpa_ptk_rekey=None, sae_pwe=None,
                  no_beacon_rsnxe2=False, ext_key_id=False,
                  skip_prune_assoc=False, ft_rsnxe_used=False,
                  sae_transition=False, ext_key=False, sae_groups=None,
-                 wpa_gtk_rekey=None):
+                 wpa_gtk_rekey=None, rsn_override_omit_rsnxe=False,
+                 ft_rsnxe_used_only_with_rsnxe=False):
     check_sae_capab(dev)
     ssid = "test-ft"
     passphrase = "12345678"
@@ -1167,6 +1168,10 @@ def start_ft_sae(dev, apdev, wpa_ptk_rekey=None, sae_pwe=None,
         params['ft_rsnxe_used'] = '1'
     if sae_groups:
         params['sae_groups'] = sae_groups
+    if rsn_override_omit_rsnxe:
+        params['rsn_override_omit_rsnxe'] = '1'
+    if ft_rsnxe_used_only_with_rsnxe:
+        params['ft_rsnxe_used_only_with_rsnxe'] = '1'
     hapd0 = hostapd.add_ap(apdev[0], params)
     params = ft_params2(ssid=ssid, passphrase=passphrase)
     if not sae_transition:
@@ -1191,6 +1196,10 @@ def start_ft_sae(dev, apdev, wpa_ptk_rekey=None, sae_pwe=None,
         params['ft_rsnxe_used'] = '1'
     if sae_groups:
         params['sae_groups'] = sae_groups
+    if rsn_override_omit_rsnxe:
+        params['rsn_override_omit_rsnxe'] = '1'
+    if ft_rsnxe_used_only_with_rsnxe:
+        params['ft_rsnxe_used_only_with_rsnxe'] = '1'
     hapd1 = hostapd.add_ap(apdev[1], params)
     key_mgmt = hapd1.get_config()['key_mgmt']
     if key_mgmt.split(' ')[0] != key_mgmt and not sae_transition:
@@ -1476,6 +1485,52 @@ def test_ap_ft_sae_rsnxe_used_mismatch2(dev, apdev):
     finally:
         dev[0].set("sae_pwe", "0")
 
+def test_ap_ft_sae_omit_rsnxe(dev, apdev):
+    """FT-SAE AP with rsn_override_omit_rsnxe and STA using loop"""
+    try:
+        hapd0, hapd1 = start_ft_sae(dev[0], apdev, sae_pwe="2",
+                                    rsn_override_omit_rsnxe=True)
+        dev[0].set("sae_pwe", "0")
+        run_roams(dev[0], apdev, hapd0, hapd1, "test-ft", "12345678",
+                  sae=True)
+    finally:
+        dev[0].set("sae_pwe", "0")
+
+def run_ft_sae_no_beacon_rsnxe_sta_loop(dev, apdev, only_with_rsnxe):
+    hapd0, hapd1 = start_ft_sae(dev[0], apdev, sae_pwe="2",
+                                no_beacon_rsnxe2=True,
+                                ft_rsnxe_used_only_with_rsnxe=only_with_rsnxe)
+    dev[0].set("sae_pwe", "0")
+    run_roams(dev[0], apdev, hapd0, hapd1, "test-ft", "12345678", sae=True,
+              force_initial_conn_to_first_ap=True,
+              return_after_initial=True)
+    dev[0].scan_for_bss(hapd1.own_addr(), freq="2412")
+    if "OK" not in dev[0].request("ROAM " + hapd1.own_addr()):
+        raise Exception("ROAM command failed")
+    ev = dev[0].wait_event(["CTRL-EVENT-CONNECTED"], timeout=10)
+    return ev is not None and hapd1.own_addr() in ev
+
+def test_ap_ft_sae_rsnxe_used_no_rsnxe(dev, apdev):
+    """FT-SAE AP sets RSNXE Used for a STA without RSNXE by default"""
+    try:
+        # The STA sends no RSNXE and the target AP has no RSNXE in its
+        # Beacon frames. By default, the response still has RSNXE Used set,
+        # and the STA rejects it.
+        if run_ft_sae_no_beacon_rsnxe_sta_loop(dev, apdev, False):
+            raise Exception("Roaming succeeded unexpectedly")
+    finally:
+        dev[0].set("sae_pwe", "0")
+
+def test_ap_ft_sae_rsnxe_used_only_with_rsnxe(dev, apdev):
+    """FT-SAE AP with ft_rsnxe_used_only_with_rsnxe"""
+    try:
+        # With the option, the response to a STA without RSNXE has RSNXE Used
+        # cleared, so the STA accepts it even without RSNXE in the Beacon.
+        if not run_ft_sae_no_beacon_rsnxe_sta_loop(dev, apdev, True):
+            raise Exception("Roaming failed")
+    finally:
+        dev[0].set("sae_pwe", "0")
+
 def test_ap_ft_sae_pw_id(dev, apdev):
     """FT-SAE with Password Identifier"""
     check_sae_capab(dev[0])
-- 
2.43.0




More information about the Hostap mailing list