[PATCH 5/5] tests: FT-SAE roaming in WPA3-Personal Compatibility Mode
Gopi Raga
ragagopi7 at gmail.com
Thu Jul 16 05:11:15 PDT 2026
Add hwsim coverage for 802.11r FT-SAE roaming when the BSS runs in
WPA3-Personal Compatibility Mode (RSN Overriding): the base RSNE
advertises legacy WPA2-PSK/FT-PSK with PMF disabled while the RSN
Override elements advertise SAE/FT-SAE with PMF required. This exercises
the FT paths that have to negotiate the AKM, RSNXE, and PMF policy
through the RSN Override machinery rather than the base RSNE/RSNXE.
Two variants are covered:
test_ap_ft_sae_rsn_override - rsn_override_omit_rsnxe=1
test_ap_ft_sae_rsn_override_with_rsnxe - base RSNXE also present
Both associate a STA and roam it between the two BSSes, asserting that
reassociation completes (no reason=3/reason=13 teardown) with key_mgmt
FT-SAE and PMF negotiated.
Signed-off-by: Gopi Raga <ragagopi7 at gmail.com>
---
tests/hwsim/test_ap_ft.py | 64 +++++++++++++++++++++++++++++++++++++++
1 file changed, 64 insertions(+)
diff --git a/tests/hwsim/test_ap_ft.py b/tests/hwsim/test_ap_ft.py
index 29bec3de9..8d0e05667 100644
--- a/tests/hwsim/test_ap_ft.py
+++ b/tests/hwsim/test_ap_ft.py
@@ -1234,6 +1234,70 @@ def test_ap_ft_sae_h2e_and_loop2(dev, apdev):
finally:
dev[0].set("sae_pwe", "0")
+def start_ft_sae_rsn_override(dev, apdev, omit_rsnxe=True):
+ """Start two FT BSSes in WPA3-Personal Compatibility Mode.
+
+ The base RSNE advertises legacy WPA2-PSK/FT-PSK (PMF disabled) while the
+ RSN Override elements advertise SAE/FT-SAE with PMF required. This mirrors
+ the WPA3-Personal transition ("Compatibility") mode where 802.11r has to
+ negotiate the AKM, RSNXE, and PMF policy through the RSN Override machinery
+ rather than the base RSNE/RSNXE.
+ """
+ check_sae_capab(dev)
+ ssid = "test-ft"
+ passphrase = "12345678"
+
+ def compat_params(base):
+ base['wpa_key_mgmt'] = 'WPA-PSK FT-PSK'
+ base['rsn_pairwise'] = 'CCMP'
+ base['ieee80211w'] = '0'
+ base['rsn_override_key_mgmt'] = 'SAE FT-SAE'
+ base['rsn_override_pairwise'] = 'CCMP'
+ base['rsn_override_mfp'] = '2'
+ base['sae_require_mfp'] = '1'
+ base['sae_pwe'] = '2'
+ if omit_rsnxe:
+ base['rsn_override_omit_rsnxe'] = '1'
+ return base
+
+ hapd0 = hostapd.add_ap(apdev[0], compat_params(ft_params1(ssid=ssid,
+ passphrase=passphrase)))
+ hapd1 = hostapd.add_ap(apdev[1], compat_params(ft_params2(ssid=ssid,
+ passphrase=passphrase)))
+
+ dev.request("SET sae_groups ")
+ return hapd0, hapd1
+
+def test_ap_ft_sae_rsn_override(dev, apdev):
+ """FT-SAE roaming in WPA3-Personal Compatibility Mode (RSN Overriding)"""
+ hapd0, hapd1 = start_ft_sae_rsn_override(dev[0], apdev)
+ try:
+ dev[0].set("rsn_overriding", "1")
+ dev[0].set("sae_pwe", "2")
+ run_roams(dev[0], apdev, hapd0, hapd1, "test-ft", "12345678",
+ sae=True, ieee80211w="2")
+ status = dev[0].get_status()
+ logger.debug("dev[0] STATUS:\n" + str(status))
+ if status['key_mgmt'] != 'FT-SAE':
+ raise Exception("Unexpected key_mgmt: " + status['key_mgmt'])
+ if status.get('pmf') != '2':
+ raise Exception("PMF not negotiated: " + str(status.get('pmf')))
+ finally:
+ dev[0].set("sae_pwe", "0")
+ dev[0].set("rsn_overriding", "0")
+
+def test_ap_ft_sae_rsn_override_with_rsnxe(dev, apdev):
+ """FT-SAE roam in WPA3-Personal Compatibility Mode with base RSNXE present"""
+ hapd0, hapd1 = start_ft_sae_rsn_override(dev[0], apdev, omit_rsnxe=False)
+ try:
+ dev[0].set("rsn_overriding", "1")
+ dev[0].set("sae_pwe", "2")
+ run_roams(dev[0], apdev, hapd0, hapd1, "test-ft", "12345678",
+ sae=True, ieee80211w="2")
+ finally:
+ dev[0].set("sae_pwe", "0")
+ dev[0].set("rsn_overriding", "0")
+
def test_ap_ft_rsnxe_only_from_sta(dev, apdev):
"""FT with RSNXE only from STA"""
hapd0, hapd1 = start_ft_sae(dev[0], apdev, sae_pwe="0")
--
2.43.0
More information about the Hostap
mailing list