1# Roaming tests 2# Copyright (c) 2013-2021, Jouni Malinen <j@w1.fi> 3# 4# This software may be distributed under the terms of the BSD license. 5# See README for more details. 6 7from remotehost import remote_compatible 8import time 9import logging 10logger = logging.getLogger() 11 12import hwsim_utils 13import hostapd 14from wpasupplicant import WpaSupplicant 15 16@remote_compatible 17def test_ap_roam_open(dev, apdev): 18 """Roam between two open APs""" 19 hapd0 = hostapd.add_ap(apdev[0], {"ssid": "test-open"}) 20 dev[0].connect("test-open", key_mgmt="NONE") 21 hwsim_utils.test_connectivity(dev[0], hapd0) 22 hapd1 = hostapd.add_ap(apdev[1], {"ssid": "test-open"}) 23 dev[0].scan(type="ONLY", timeout=30) 24 dev[0].roam(apdev[1]['bssid']) 25 hwsim_utils.test_connectivity(dev[0], hapd1) 26 dev[0].roam(apdev[0]['bssid']) 27 hwsim_utils.test_connectivity(dev[0], hapd0) 28 29def test_ap_ignore_bssid_all(dev, apdev, params): 30 """Ensure we clear the ignore BSSID list if all visible APs reject""" 31 hapd0 = hostapd.add_ap(apdev[0], {"ssid": "test-open", "max_num_sta": "0"}) 32 hapd1 = hostapd.add_ap(apdev[1], {"ssid": "test-open", "max_num_sta": "0"}) 33 bss0 = hapd0.own_addr() 34 bss1 = hapd1.own_addr() 35 36 dev[0].connect("test-open", key_mgmt="NONE", scan_freq="2412", 37 wait_connect=False, bssid=bss0) 38 if not dev[0].wait_event(["CTRL-EVENT-AUTH-REJECT"], timeout=10): 39 raise Exception("AP 0 didn't reject us") 40 dev[0].request("REMOVE_NETWORK all") 41 dev[0].dump_monitor() 42 dev[0].connect("test-open", key_mgmt="NONE", scan_freq="2412", 43 wait_connect=False, bssid=bss1) 44 if not dev[0].wait_event(["CTRL-EVENT-AUTH-REJECT"], timeout=10): 45 raise Exception("AP 1 didn't reject us") 46 ignore_list = get_bssid_ignore_list(dev[0]) 47 logger.info("ignore list: " + str(ignore_list)) 48 dev[0].request("REMOVE_NETWORK all") 49 dev[0].dump_monitor() 50 51 hapd0.set("max_num_sta", "1") 52 # All visible APs were ignored; we should clear the ignore list and find 53 # the AP that now accepts us. 54 dev[0].scan_for_bss(bss0, freq=2412) 55 dev[0].connect("test-open", key_mgmt="NONE", scan_freq="2412", bssid=bss0) 56 57@remote_compatible 58def test_ap_roam_open_failed(dev, apdev): 59 """Roam failure due to rejected authentication""" 60 hapd0 = hostapd.add_ap(apdev[0], {"ssid": "test-open"}) 61 dev[0].connect("test-open", key_mgmt="NONE", scan_freq="2412") 62 hwsim_utils.test_connectivity(dev[0], hapd0) 63 params = {"ssid": "test-open", "max_num_sta": "0"} 64 hapd1 = hostapd.add_ap(apdev[1], params) 65 bssid = hapd1.own_addr() 66 67 dev[0].scan_for_bss(bssid, freq=2412) 68 dev[0].dump_monitor() 69 if "OK" not in dev[0].request("ROAM " + bssid): 70 raise Exception("ROAM failed") 71 72 ev = dev[0].wait_event(["CTRL-EVENT-AUTH-REJECT"], 1) 73 if not ev: 74 raise Exception("CTRL-EVENT-AUTH-REJECT was not seen") 75 76 dev[0].wait_connected(timeout=5) 77 hwsim_utils.test_connectivity(dev[0], hapd0) 78 79def test_ap_roam_open_failed_ssid_mismatch(dev, apdev): 80 """Roam failure due to SSID mismatch""" 81 hapd0 = hostapd.add_ap(apdev[0], {"ssid": "test-open"}) 82 bssid0 = hapd0.own_addr() 83 hapd1 = hostapd.add_ap(apdev[1], {"ssid": "test-open2"}) 84 bssid1 = hapd1.own_addr() 85 dev[0].flush_scan_cache() 86 dev[0].scan_for_bss(bssid0, freq=2412) 87 dev[0].scan_for_bss(bssid1, freq=2412) 88 dev[0].connect("test-open", key_mgmt="NONE", scan_freq="2412") 89 hapd0.wait_sta() 90 bssid = dev[0].get_status_field("bssid") 91 if bssid != bssid0: 92 raise Exception("Unexpected BSSID reported after initial connection: " + bssid) 93 if "FAIL" not in dev[0].request("ROAM " + bssid1): 94 raise Exception("ROAM succeed unexpectedly") 95 bssid = dev[0].get_status_field("bssid") 96 if bssid != bssid0: 97 raise Exception("Unexpected BSSID reported after failed roam attempt: " + bssid) 98 hwsim_utils.test_connectivity(dev[0], hapd0) 99 100@remote_compatible 101def test_ap_roam_wpa2_psk(dev, apdev): 102 """Roam between two WPA2-PSK APs""" 103 params = hostapd.wpa2_params(ssid="test-wpa2-psk", passphrase="12345678") 104 hapd0 = hostapd.add_ap(apdev[0], params) 105 dev[0].connect("test-wpa2-psk", psk="12345678") 106 hapd0.wait_sta() 107 hwsim_utils.test_connectivity(dev[0], hapd0) 108 hapd1 = hostapd.add_ap(apdev[1], params) 109 dev[0].scan(type="ONLY") 110 dev[0].roam(apdev[1]['bssid']) 111 hapd1.wait_sta() 112 hwsim_utils.test_connectivity(dev[0], hapd1) 113 dev[0].roam(apdev[0]['bssid']) 114 hapd0.wait_sta() 115 hwsim_utils.test_connectivity(dev[0], hapd0) 116 117def test_ap_roam_wpa2_psk_pmf_mismatch(dev, apdev): 118 """Roam between two WPA2-PSK APs - PMF mismatch""" 119 params = hostapd.wpa2_params(ssid="test-wpa2-psk", passphrase="12345678") 120 params['ieee80211w'] = '1' 121 hapd0 = hostapd.add_ap(apdev[0], params) 122 bssid0 = hapd0.own_addr() 123 params['ieee80211w'] = '0' 124 hapd1 = hostapd.add_ap(apdev[1], params) 125 bssid1 = hapd1.own_addr() 126 dev[0].flush_scan_cache() 127 dev[0].scan_for_bss(bssid0, freq=2412) 128 dev[0].scan_for_bss(bssid1, freq=2412) 129 dev[0].connect("test-wpa2-psk", psk="12345678", ieee80211w='2') 130 hapd0.wait_sta() 131 bssid = dev[0].get_status_field("bssid") 132 if bssid != bssid0: 133 raise Exception("Unexpected BSSID reported after initial connection: " + bssid) 134 if "OK" not in dev[0].request("ROAM " + apdev[1]['bssid']): 135 raise Exception("ROAM failed") 136 ev = dev[0].wait_event(["CTRL-EVENT-CONNECTED"], timeout=0.5) 137 if ev is not None: 138 raise Exception("Unexpected connection reported") 139 bssid = dev[0].get_status_field("bssid") 140 if bssid != bssid0: 141 raise Exception("Unexpected BSSID reported after failed roam attempt: " + bssid) 142 hwsim_utils.test_connectivity(dev[0], hapd0) 143 144def get_bssid_ignore_list(dev): 145 return dev.request("BSSID_IGNORE").splitlines() 146 147def test_ap_reconnect_auth_timeout(dev, apdev, params): 148 """Reconnect to 2nd AP and authentication times out""" 149 wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5') 150 wpas.interface_add("wlan5", 151 drv_params="force_connect_cmd=1,force_bss_selection=1") 152 wpas.flush_scan_cache() 153 154 params = hostapd.wpa2_params(ssid="test-wpa2-psk", passphrase="12345678") 155 hapd0 = hostapd.add_ap(apdev[0], params) 156 bssid0 = hapd0.own_addr() 157 158 wpas.scan_for_bss(bssid0, freq=2412) 159 id = wpas.connect("test-wpa2-psk", psk="12345678", scan_freq="2412") 160 hapd0.wait_sta() 161 hwsim_utils.test_connectivity(wpas, hapd0) 162 163 hapd1 = hostapd.add_ap(apdev[1], params) 164 bssid1 = hapd1.own_addr() 165 166 wpas.request("BSSID_IGNORE " + bssid0) 167 168 wpas.scan_for_bss(bssid1, freq=2412) 169 wpas.request("DISCONNECT") 170 if "OK" not in wpas.request("SET ignore_auth_resp 1"): 171 raise Exception("SET ignore_auth_resp failed") 172 if "OK" not in wpas.request("ENABLE_NETWORK " + str(id)): 173 raise Exception("ENABLE_NETWORK failed") 174 if "OK" not in wpas.request("SELECT_NETWORK " + str(id)): 175 raise Exception("SELECT_NETWORK failed") 176 177 logger.info("Wait ~10s for auth timeout...") 178 time.sleep(10) 179 ev = wpas.wait_event(["CTRL-EVENT-SCAN-STARTED"], 12) 180 if not ev: 181 raise Exception("CTRL-EVENT-SCAN-STARTED not seen") 182 183 b = get_bssid_ignore_list(wpas) 184 if '00:00:00:00:00:00' in b: 185 raise Exception("Unexpected ignore list contents: " + str(b)) 186 if bssid1 not in b: 187 raise Exception("Unexpected ignore list contents: " + str(b)) 188 189def test_ap_roam_with_reassoc_auth_timeout(dev, apdev, params): 190 """Roam using reassoc between two APs and authentication times out""" 191 wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5') 192 wpas.interface_add("wlan5", 193 drv_params="force_connect_cmd=1,force_bss_selection=1") 194 195 params = hostapd.wpa2_params(ssid="test-wpa2-psk", passphrase="12345678") 196 hapd0 = hostapd.add_ap(apdev[0], params) 197 bssid0 = hapd0.own_addr() 198 199 id = wpas.connect("test-wpa2-psk", psk="12345678", scan_freq="2412") 200 hapd0.wait_sta() 201 hwsim_utils.test_connectivity(wpas, hapd0) 202 203 hapd1 = hostapd.add_ap(apdev[1], params) 204 bssid1 = hapd1.own_addr() 205 wpas.scan_for_bss(bssid1, freq=2412) 206 207 if "OK" not in wpas.request("SET_NETWORK " + str(id) + " bssid " + bssid1): 208 raise Exception("SET_NETWORK failed") 209 if "OK" not in wpas.request("SET ignore_auth_resp 1"): 210 raise Exception("SET ignore_auth_resp failed") 211 if "OK" not in wpas.request("REASSOCIATE"): 212 raise Exception("REASSOCIATE failed") 213 214 logger.info("Wait ~10s for auth timeout...") 215 time.sleep(10) 216 ev = wpas.wait_event(["CTRL-EVENT-SCAN-STARTED"], 12) 217 if not ev: 218 raise Exception("CTRL-EVENT-SCAN-STARTED not seen") 219 220 b = get_bssid_ignore_list(wpas) 221 if bssid0 in b: 222 raise Exception("Unexpected ignore list contents: " + str(b)) 223 224def test_ap_roam_wpa2_psk_failed(dev, apdev, params): 225 """Roam failure with WPA2-PSK AP due to wrong passphrase""" 226 params = hostapd.wpa2_params(ssid="test-wpa2-psk", passphrase="12345678") 227 hapd0 = hostapd.add_ap(apdev[0], params) 228 id = dev[0].connect("test-wpa2-psk", psk="12345678", scan_freq="2412") 229 hapd0.wait_sta() 230 hwsim_utils.test_connectivity(dev[0], hapd0) 231 params['wpa_passphrase'] = "22345678" 232 hapd1 = hostapd.add_ap(apdev[1], params) 233 bssid = hapd1.own_addr() 234 dev[0].scan_for_bss(bssid, freq=2412) 235 236 dev[0].dump_monitor() 237 if "OK" not in dev[0].request("ROAM " + bssid): 238 raise Exception("ROAM failed") 239 240 ev = dev[0].wait_event(["CTRL-EVENT-SSID-TEMP-DISABLED", 241 "CTRL-EVENT-CONNECTED"], 5) 242 if "CTRL-EVENT-CONNECTED" in ev: 243 raise Exception("Got unexpected CTRL-EVENT-CONNECTED") 244 if "CTRL-EVENT-SSID-TEMP-DISABLED" not in ev: 245 raise Exception("CTRL-EVENT-SSID-TEMP-DISABLED not seen") 246 247 if "OK" not in dev[0].request("SELECT_NETWORK id=" + str(id)): 248 raise Exception("SELECT_NETWORK failed") 249 250 ev = dev[0].wait_event(["CTRL-EVENT-SSID-REENABLED"], 3) 251 if not ev: 252 raise Exception("CTRL-EVENT-SSID-REENABLED not seen") 253 254 dev[0].wait_connected(timeout=5) 255 hapd0.wait_sta() 256 hwsim_utils.test_connectivity(dev[0], hapd0) 257 258@remote_compatible 259def test_ap_reassociation_to_same_bss(dev, apdev): 260 """Reassociate to the same BSS""" 261 hapd = hostapd.add_ap(apdev[0], {"ssid": "test-open"}) 262 dev[0].connect("test-open", key_mgmt="NONE") 263 hapd.wait_sta() 264 265 dev[0].request("REASSOCIATE") 266 dev[0].wait_connected(timeout=10, error="Reassociation timed out") 267 hapd.wait_sta() 268 hwsim_utils.test_connectivity(dev[0], hapd) 269 270 dev[0].request("REATTACH") 271 dev[0].wait_connected(timeout=10, error="Reattach timed out") 272 hapd.wait_sta() 273 hwsim_utils.test_connectivity(dev[0], hapd) 274 275 # Wait for previous scan results to expire to trigger new scan 276 time.sleep(5) 277 dev[0].request("REATTACH") 278 dev[0].wait_connected(timeout=10, error="Reattach timed out") 279 hapd.wait_sta() 280 hwsim_utils.test_connectivity(dev[0], hapd) 281 282@remote_compatible 283def test_ap_roam_set_bssid(dev, apdev): 284 """Roam control""" 285 hostapd.add_ap(apdev[0], {"ssid": "test-open"}) 286 hostapd.add_ap(apdev[1], {"ssid": "test-open"}) 287 id = dev[0].connect("test-open", key_mgmt="NONE", bssid=apdev[1]['bssid'], 288 scan_freq="2412") 289 if dev[0].get_status_field('bssid') != apdev[1]['bssid']: 290 raise Exception("Unexpected BSS") 291 # for now, these are just verifying that the code path to indicate 292 # within-ESS roaming changes can be executed; the actual results of those 293 # operations are not currently verified (that would require a test driver 294 # that does BSS selection) 295 dev[0].set_network(id, "bssid", "") 296 dev[0].set_network(id, "bssid", apdev[0]['bssid']) 297 dev[0].set_network(id, "bssid", apdev[1]['bssid']) 298 299@remote_compatible 300def test_ap_roam_wpa2_psk_race(dev, apdev): 301 """Roam between two WPA2-PSK APs and try to hit a disconnection race""" 302 params = hostapd.wpa2_params(ssid="test-wpa2-psk", passphrase="12345678") 303 hapd0 = hostapd.add_ap(apdev[0], params) 304 dev[0].connect("test-wpa2-psk", psk="12345678", scan_freq="2412") 305 hapd0.wait_sta() 306 hwsim_utils.test_connectivity(dev[0], hapd0) 307 308 params['channel'] = '2' 309 hapd1 = hostapd.add_ap(apdev[1], params) 310 dev[0].scan_for_bss(apdev[1]['bssid'], freq=2417) 311 dev[0].roam(apdev[1]['bssid']) 312 hapd1.wait_sta() 313 hwsim_utils.test_connectivity(dev[0], hapd1) 314 dev[0].roam(apdev[0]['bssid']) 315 hapd0.wait_sta() 316 hwsim_utils.test_connectivity(dev[0], hapd0) 317 # Wait at least two seconds to trigger the previous issue with the 318 # disconnection callback. 319 for i in range(3): 320 time.sleep(0.8) 321 hwsim_utils.test_connectivity(dev[0], hapd0) 322 323def test_ap_roam_signal_level_override(dev, apdev): 324 """Roam between two APs based on driver signal level override""" 325 hapd0 = hostapd.add_ap(apdev[0], {"ssid": "test-open"}) 326 bssid0 = apdev[0]['bssid'] 327 hapd1 = hostapd.add_ap(apdev[1], {"ssid": "test-open"}) 328 bssid1 = apdev[1]['bssid'] 329 dev[0].scan_for_bss(bssid0, freq=2412) 330 dev[0].scan_for_bss(bssid1, freq=2412) 331 332 dev[0].connect("test-open", key_mgmt="NONE") 333 bssid = dev[0].get_status_field('bssid') 334 if bssid == bssid0: 335 dst = bssid1 336 src = bssid0 337 else: 338 dst = bssid0 339 src = bssid1 340 341 dev[0].scan(freq=2412) 342 ev = dev[0].wait_event(["CTRL-EVENT-CONNECTED"], 0.5) 343 if ev is not None: 344 raise Exception("Unexpected roam") 345 346 orig_res = dev[0].request("SIGNAL_POLL") 347 dev[0].set("driver_signal_override", src + " -1 -2 -3 -4 -5") 348 res = dev[0].request("SIGNAL_POLL").splitlines() 349 if "RSSI=-1" not in res or \ 350 "AVG_RSSI=-2" not in res or \ 351 "AVG_BEACON_RSSI=-3" not in res or \ 352 "NOISE=-4" not in res: 353 raise Exception("SIGNAL_POLL override did not work: " + str(res)) 354 355 dev[0].set("driver_signal_override", src) 356 new_res = dev[0].request("SIGNAL_POLL") 357 if orig_res != new_res: 358 raise Exception("SIGNAL_POLL restore did not work: " + new_res) 359 360 tests = [("-30 -30 -30 -95 -30", "-30 -30 -30 -95 -30"), 361 ("-30 -30 -30 -95 -30", "-20 -20 -20 -95 -20"), 362 ("-90 -90 -90 -95 -90", "-89 -89 -89 -95 -89"), 363 ("-90 -90 -90 -95 -95", "-89 -89 -89 -95 -89")] 364 for src_override, dst_override in tests: 365 dev[0].set("driver_signal_override", src + " " + src_override) 366 dev[0].set("driver_signal_override", dst + " " + dst_override) 367 dev[0].scan(freq=2412) 368 ev = dev[0].wait_event(["CTRL-EVENT-CONNECTED"], 0.1) 369 if ev is not None: 370 raise Exception("Unexpected roam") 371 dev[0].dump_monitor() 372 373 dev[0].set("driver_signal_override", src + " -90 -90 -90 -95 -90") 374 dev[0].set("driver_signal_override", dst + " -80 -80 -80 -95 -80") 375 dev[0].scan(freq=2412) 376 dev[0].wait_connected() 377 if dst != dev[0].get_status_field('bssid'): 378 raise Exception("Unexpected AP after roam") 379 dev[0].dump_monitor() 380 381def test_ap_roam_during_scan(dev, apdev): 382 """Roam command during a scan operation""" 383 hapd0 = hostapd.add_ap(apdev[0], {"ssid": "test-open"}) 384 dev[0].flush_scan_cache() 385 dev[0].scan_for_bss(hapd0.own_addr(), freq=2412) 386 dev[0].connect("test-open", key_mgmt="NONE") 387 hapd1 = hostapd.add_ap(apdev[1], {"ssid": "test-open"}) 388 dev[0].scan_for_bss(hapd1.own_addr(), freq=2412) 389 if "OK" not in dev[0].request("SCAN"): 390 raise Exception("Failed to start scan") 391 if "OK" not in dev[0].request("ROAM " + hapd1.own_addr()): 392 raise Exception("Failed to issue ROAM") 393 ev = dev[0].wait_event(["CTRL-EVENT-CONNECTED"], timeout=10) 394 if ev is None: 395 raise Exception("Connection not reported after ROAM") 396 if hapd1.own_addr() not in ev: 397 raise Exception("Connected to unexpected AP") 398