[PATCH 5/5] tests: Add NAN-STOPPED event tests on radio destruction
Andrei Otcheretianski
andrei.otcheretianski at intel.com
Tue May 12 23:59:08 PDT 2026
From: Ilan Peer <ilan.peer at intel.com>
Add tests to verify that NAN-STOPPED is reported when the underlying
radio is destroyed while NAN is active:
- test_nan_stopped_on_iface_removal: Verifies NAN-STOPPED after radio
destruction during service discovery, then recovers NAN on a new
radio.
- test_nan_stopped_on_iface_removal_with_ndp: Same scenario but with
an active NDP (NAN Data Path) including connectivity verification.
Signed-off-by: Ilan Peer <ilan.peer at intel.com>
---
tests/hwsim/test_nan.py | 144 +++++++++++++++++++++++++++++++++++++++-
1 file changed, 143 insertions(+), 1 deletion(-)
diff --git a/tests/hwsim/test_nan.py b/tests/hwsim/test_nan.py
index 691da50032..c2905c9933 100644
--- a/tests/hwsim/test_nan.py
+++ b/tests/hwsim/test_nan.py
@@ -10,9 +10,10 @@ logger = logging.getLogger()
from utils import *
import string
import hwsim_utils
-from hwsim import HWSimRadio
+from hwsim import HWSimRadio, HWSimController
from contextlib import contextmanager, ExitStack
from test_p2p_channel import set_country
+import time
@contextmanager
def hwsim_nan_radios(count=2, n_channels=3):
@@ -2125,3 +2126,144 @@ def test_nan_override_potential_availability(dev, apdev, params):
# expect any changes during the test execution.
if potential != old_potential:
raise Exception("Potential availability did not revert to original after clearing override")
+
+def test_nan_stopped_on_iface_removal(dev, apdev, params):
+ """NAN cluster and discovery followed by radio destruction reports NAN-STOPPED"""
+ controller = HWSimController()
+ radio1 = HWSimRadio(n_channels=3, use_nan=True)
+ radio1_id, ifname1 = radio1.__enter__()
+ radio1_destroyed = False
+
+ try:
+ with HWSimRadio(n_channels=3, use_nan=True) as (radio2_id, ifname2):
+ wpas1 = WpaSupplicant(global_iface="/tmp/wpas-wlan5")
+ wpas1.interface_add(ifname1)
+ wpas2 = WpaSupplicant(global_iface="/tmp/wpas-wlan6")
+ wpas2.interface_add(ifname2)
+
+ nan_ifname = "nan0"
+
+ logger.info("Starting NAN on publisher")
+ pub = NanDevice(wpas1, nan_ifname)
+ pub.start()
+
+ with NanDevice(wpas2, "nan1") as sub:
+ logger.info("Verifying service discovery")
+ nan_sync_discovery(pub, sub, "test_service",
+ pssi="aabbccdd", sssi="ddbbccaa")
+
+ logger.info("Destroying publisher radio")
+ wpas1.dump_monitor()
+ controller.destroy_radio(radio1_id)
+ radio1_destroyed = True
+
+ logger.info("Waiting for NAN-STOPPED event on publisher")
+ ev = wpas1.wait_global_event(["NAN-STOPPED"], timeout=5)
+ if ev is None:
+ raise Exception("NAN-STOPPED event not received after radio destruction")
+ if f"ifname={nan_ifname}" not in ev:
+ raise Exception(f"Unexpected NAN-STOPPED event content: {ev}")
+
+ logger.info("Removing old interface and adding new radio")
+ sub.wpas.dump_monitor()
+ wpas1.interface_remove(ifname1)
+
+ radio1 = HWSimRadio(n_channels=3, use_nan=True)
+ radio1_id, ifname1_new = radio1.__enter__()
+ radio1_destroyed = False
+ wpas1.interface_add(ifname1_new)
+
+ logger.info("Starting NAN on re-added radio")
+ pub = NanDevice(wpas1, nan_ifname)
+ pub.start()
+
+ logger.info("Verifying service discovery on re-added radio")
+ nan_sync_discovery(pub, sub, "test_service2",
+ pssi="11223344", sssi="44332211")
+ finally:
+ if not radio1_destroyed:
+ radio1.__exit__(None, None, None)
+
+def test_nan_stopped_on_iface_removal_with_ndp(dev, apdev, params):
+ """NAN cluster, NDP establishment, then radio destruction reports NAN-STOPPED"""
+ set_country("US")
+ controller = HWSimController()
+ radio1 = HWSimRadio(n_channels=3, use_nan=True)
+ radio1_id, ifname1 = radio1.__enter__()
+ radio1_destroyed = False
+
+ try:
+ with HWSimRadio(n_channels=3, use_nan=True) as (radio2_id, ifname2):
+ wpas1 = WpaSupplicant(global_iface="/tmp/wpas-wlan5")
+ wpas1.interface_add(ifname1)
+ wpas2 = WpaSupplicant(global_iface="/tmp/wpas-wlan6")
+ wpas2.interface_add(ifname2)
+
+ nan_ifname = "nan0"
+ ndi_name = "ndi0"
+
+ logger.info("Starting NAN on publisher with NDI")
+ pub = NanDevice(wpas1, nan_ifname, ndi_name)
+ pub.start()
+
+ with NanDevice(wpas2, "nan1", "ndi1") as sub:
+ logger.info("Establishing NDP between publisher and subscriber")
+ pid, sid, paddr, saddr = _nan_discover_service(
+ pub, sub, "test_service", "aabbccdd", "ddbbccaa",
+ data_path=True)
+ ndp_id, init_ndi = _nan_ndp_request_and_accept(
+ pub, sub, pid, sid, paddr, saddr,
+ req_ssi="aabbcc", resp_ssi="ddeeff")
+
+ logger.info("Verifying connectivity over NDP")
+ _nan_test_connectivity(pub, sub)
+
+ logger.info("Destroying publisher radio while NDP is active")
+ wpas1.dump_monitor()
+ sub.wpas.dump_monitor()
+ controller.destroy_radio(radio1_id)
+ radio1_destroyed = True
+
+ logger.info("Waiting for NAN-STOPPED event on publisher")
+ ev = wpas1.wait_global_event(["NAN-STOPPED"], timeout=5)
+ if ev is None:
+ raise Exception("NAN-STOPPED event not received after radio destruction")
+ if f"ifname={nan_ifname}" not in ev:
+ raise Exception(f"Unexpected NAN-STOPPED event content: {ev}")
+
+ logger.info("Terminating NDP on subscriber")
+ sub.ndp_terminate(paddr, init_ndi, ndp_id)
+ ev = sub.wpas.wait_event(["NAN-NDP-DISCONNECTED"], timeout=5)
+ if ev is None:
+ raise Exception("NAN-NDP-DISCONNECTED not received on subscriber")
+
+ logger.info("Removing old interface and adding new radio")
+ sub.wpas.dump_monitor()
+ wpas1.interface_remove(ifname1)
+
+ radio1 = HWSimRadio(n_channels=3, use_nan=True)
+ radio1_id, ifname1_new = radio1.__enter__()
+ radio1_destroyed = False
+
+ wpas1.interface_add(ifname1_new)
+
+ logger.info("Starting NAN on re-added radio with NDI")
+ pub = NanDevice(wpas1, nan_ifname, ndi_name)
+ pub.start()
+
+ logger.info("Establishing NDP on re-added radio")
+ pid, sid, paddr, saddr = _nan_discover_service(
+ pub, sub, "test_service2", "11223344", "44332211",
+ data_path=True)
+ ndp_id, init_ndi = _nan_ndp_request_and_accept(
+ pub, sub, pid, sid, paddr, saddr,
+ req_ssi="aabbcc", resp_ssi="ddeeff")
+
+ logger.info("Verifying connectivity on re-added radio")
+ _nan_test_connectivity(pub, sub)
+
+ logger.info("NDP recovery after radio destruction verified successfully")
+ finally:
+ if not radio1_destroyed:
+ radio1.__exit__(None, None, None)
+ set_country("00")
--
2.53.0
More information about the Hostap
mailing list