1# Test cases for Multi-AP 2# Copyright (c) 2018, The Linux Foundation 3# 4# This software may be distributed under the terms of the BSD license. 5# See README for more details. 6 7import hostapd 8from wpasupplicant import WpaSupplicant 9from utils import * 10 11def test_multi_ap_association(dev, apdev): 12 """Multi-AP association in backhaul BSS""" 13 run_multi_ap_association(dev, apdev, 1) 14 dev[1].connect("multi-ap", psk="12345678", scan_freq="2412", 15 wait_connect=False) 16 ev = dev[1].wait_event(["CTRL-EVENT-DISCONNECTED", 17 "CTRL-EVENT-CONNECTED", 18 "CTRL-EVENT-ASSOC-REJECT"], 19 timeout=5) 20 dev[1].request("DISCONNECT") 21 if ev is None: 22 raise Exception("Connection result not reported") 23 if "CTRL-EVENT-ASSOC-REJECT" not in ev: 24 raise Exception("Association rejection not reported") 25 if "status_code=12" not in ev: 26 raise Exception("Unexpected association status code: " + ev) 27 28def test_multi_ap_association_shared_bss(dev, apdev): 29 """Multi-AP association in backhaul BSS (with fronthaul BSS enabled)""" 30 run_multi_ap_association(dev, apdev, 3) 31 dev[1].connect("multi-ap", psk="12345678", scan_freq="2412") 32 33def test_multi_ap_backhaul_shared_bss(dev, apdev): 34 """Multi-AP backhaul to backhaul+fronthaul BSS""" 35 hapd = run_multi_ap_association(dev, apdev, 3, wait_connect=False, 36 wds_sta=True) 37 ev = hapd.wait_event(["WDS-STA-INTERFACE-ADDED"], timeout=10) 38 if ev is None: 39 raise Exception("No WDS-STA-INTERFACE-ADDED event seen") 40 41def run_multi_ap_association(dev, apdev, multi_ap, wait_connect=True, 42 wds_sta=False): 43 params = hostapd.wpa2_params(ssid="multi-ap", passphrase="12345678") 44 if multi_ap: 45 params["multi_ap"] = str(multi_ap) 46 if wds_sta: 47 params["wds_sta"] = "1" 48 hapd = hostapd.add_ap(apdev[0], params) 49 50 dev[0].connect("multi-ap", psk="12345678", scan_freq="2412", 51 multi_ap_backhaul_sta="1", wait_connect=wait_connect) 52 return hapd 53 54def test_multi_ap_backhaul_roam_with_bridge(dev, apdev): 55 """Multi-AP backhaul BSS reassociation to another BSS with bridge""" 56 br_ifname = 'sta-br0' 57 ifname = 'wlan5' 58 try: 59 run_multi_ap_backhaul_roam_with_bridge(dev, apdev) 60 finally: 61 subprocess.call(['ip', 'link', 'set', 'dev', br_ifname, 'down']) 62 subprocess.call(['brctl', 'delif', br_ifname, ifname]) 63 subprocess.call(['brctl', 'delbr', br_ifname]) 64 subprocess.call(['iw', ifname, 'set', '4addr', 'off']) 65 66def run_multi_ap_backhaul_roam_with_bridge(dev, apdev): 67 br_ifname = 'sta-br0' 68 ifname = 'wlan5' 69 wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5') 70 subprocess.call(['brctl', 'addbr', br_ifname]) 71 subprocess.call(['brctl', 'setfd', br_ifname, '0']) 72 subprocess.call(['ip', 'link', 'set', 'dev', br_ifname, 'up']) 73 subprocess.call(['iw', ifname, 'set', '4addr', 'on']) 74 subprocess.check_call(['brctl', 'addif', br_ifname, ifname]) 75 wpas.interface_add(ifname, br_ifname=br_ifname) 76 wpas.flush_scan_cache() 77 78 params = hostapd.wpa2_params(ssid="multi-ap", passphrase="12345678") 79 params["multi_ap"] = "1" 80 hapd = hostapd.add_ap(apdev[0], params) 81 82 wpas.connect("multi-ap", psk="12345678", scan_freq="2412", 83 multi_ap_backhaul_sta="1") 84 85 hapd2 = hostapd.add_ap(apdev[1], params) 86 bssid2 = hapd2.own_addr() 87 wpas.scan_for_bss(bssid2, freq="2412", force_scan=True) 88 wpas.roam(bssid2) 89 90def test_multi_ap_disabled_on_ap(dev, apdev): 91 """Multi-AP association attempt when disabled on AP""" 92 run_multi_ap_association(dev, apdev, 0, wait_connect=False) 93 ev = dev[0].wait_event(["CTRL-EVENT-DISCONNECTED", 94 "CTRL-EVENT-CONNECTED"], 95 timeout=5) 96 dev[0].request("DISCONNECT") 97 if ev is None: 98 raise Exception("Connection result not reported") 99 if "CTRL-EVENT-DISCONNECTED" not in ev: 100 raise Exception("Unexpected connection result") 101 102def test_multi_ap_fronthaul_on_ap(dev, apdev): 103 """Multi-AP association attempt when only fronthaul BSS on AP""" 104 run_multi_ap_association(dev, apdev, 2, wait_connect=False) 105 ev = dev[0].wait_event(["CTRL-EVENT-DISCONNECTED", 106 "CTRL-EVENT-CONNECTED", 107 "CTRL-EVENT-ASSOC-REJECT"], 108 timeout=5) 109 dev[0].request("DISCONNECT") 110 if ev is None: 111 raise Exception("Connection result not reported") 112 if "CTRL-EVENT-DISCONNECTED" not in ev: 113 raise Exception("Unexpected connection result") 114 115def remove_apdev(dev, ifname): 116 hglobal = hostapd.HostapdGlobal() 117 hglobal.remove(ifname) 118 dev.cmd_execute(['iw', ifname, 'del']) 119 120def run_multi_ap_wps(dev, apdev, params, params_backhaul=None, add_apdev=False, 121 run_csa=False, allow_csa_fail=False): 122 """Helper for running Multi-AP WPS tests 123 124 dev[0] does multi_ap WPS, dev[1] does normal WPS. apdev[0] is the fronthaul 125 BSS. If there is a separate backhaul BSS, it must have been set up by the 126 caller. params are the normal SSID parameters, they will be extended with 127 the WPS parameters. multi_ap_bssid must be given if it is not equal to the 128 fronthaul BSSID.""" 129 130 wpas_apdev = None 131 132 if params_backhaul: 133 hapd_backhaul = hostapd.add_ap(apdev[1], params_backhaul) 134 multi_ap_bssid = hapd_backhaul.own_addr() 135 else: 136 multi_ap_bssid = apdev[0]['bssid'] 137 138 params.update({"wps_state": "2", "eap_server": "1"}) 139 140 # WPS with multi-ap station dev[0] 141 hapd = hostapd.add_ap(apdev[0], params) 142 conf = hapd.request("GET_CONFIG").splitlines() 143 if "ssid=" + params['ssid'] not in conf: 144 raise Exception("GET_CONFIG did not show correct ssid entry") 145 if "multi_ap" in params and \ 146 "multi_ap=" + params["multi_ap"] not in conf: 147 raise Exception("GET_CONFIG did not show correct multi_ap entry") 148 if "multi_ap_backhaul_ssid" in params and \ 149 "multi_ap_backhaul_ssid=" + params["multi_ap_backhaul_ssid"].strip('"') not in conf: 150 raise Exception("GET_CONFIG did not show correct multi_ap_backhaul_ssid entry") 151 if "wpa" in params and "multi_ap_backhaul_wpa_passphrase" in params and \ 152 "multi_ap_backhaul_wpa_passphrase=" + params["multi_ap_backhaul_wpa_passphrase"] not in conf: 153 raise Exception("GET_CONFIG did not show correct multi_ap_backhaul_wpa_passphrase entry") 154 if "multi_ap_backhaul_wpa_psk" in params and \ 155 "multi_ap_backhaul_wpa_psk=" + params["multi_ap_backhaul_wpa_psk"] not in conf: 156 raise Exception("GET_CONFIG did not show correct multi_ap_backhaul_wpa_psk entry") 157 hapd.request("WPS_PBC") 158 if "PBC Status: Active" not in hapd.request("WPS_GET_STATUS"): 159 raise Exception("PBC status not shown correctly") 160 161 dev[0].request("WPS_PBC multi_ap=1") 162 dev[0].wait_connected(timeout=20) 163 status = dev[0].get_status() 164 if status['wpa_state'] != 'COMPLETED' or status['bssid'] != multi_ap_bssid: 165 raise Exception("Not fully connected") 166 if status['ssid'] != params['multi_ap_backhaul_ssid'].strip('"'): 167 raise Exception("Unexpected SSID %s != %s" % (status['ssid'], params["multi_ap_backhaul_ssid"])) 168 if status['pairwise_cipher'] != 'CCMP': 169 raise Exception("Unexpected encryption configuration %s" % status['pairwise_cipher']) 170 if status['key_mgmt'] != 'WPA2-PSK': 171 raise Exception("Unexpected key_mgmt") 172 173 status = hapd.request("WPS_GET_STATUS") 174 if "PBC Status: Disabled" not in status: 175 raise Exception("PBC status not shown correctly") 176 if "Last WPS result: Success" not in status: 177 raise Exception("Last WPS result not shown correctly") 178 if "Peer Address: " + dev[0].own_addr() not in status: 179 raise Exception("Peer address not shown correctly") 180 181 if len(dev[0].list_networks()) != 1: 182 raise Exception("Unexpected number of network blocks") 183 184 # WPS with non-Multi-AP station dev[1] 185 hapd.request("WPS_PBC") 186 if "PBC Status: Active" not in hapd.request("WPS_GET_STATUS"): 187 raise Exception("PBC status not shown correctly") 188 189 dev[1].request("WPS_PBC") 190 dev[1].wait_connected(timeout=20) 191 status = dev[1].get_status() 192 if status['wpa_state'] != 'COMPLETED' or status['bssid'] != apdev[0]['bssid']: 193 raise Exception("Not fully connected") 194 if status['ssid'] != params["ssid"]: 195 raise Exception("Unexpected SSID") 196 # Fronthaul may be something else than WPA2-PSK so don't test it. 197 198 status = hapd.request("WPS_GET_STATUS") 199 if "PBC Status: Disabled" not in status: 200 raise Exception("PBC status not shown correctly") 201 if "Last WPS result: Success" not in status: 202 raise Exception("Last WPS result not shown correctly") 203 if "Peer Address: " + dev[1].own_addr() not in status: 204 raise Exception("Peer address not shown correctly") 205 206 if len(dev[1].list_networks()) != 1: 207 raise Exception("Unexpected number of network blocks") 208 209 try: 210 # Add apdev to the same phy that dev[0] 211 if add_apdev: 212 wpas_apdev = {} 213 wpas_apdev['ifname'] = dev[0].ifname + "_ap" 214 status, buf = dev[0].cmd_execute(['iw', dev[0].ifname, 215 'interface', 'add', 216 wpas_apdev['ifname'], 217 'type', 'managed']) 218 if status != 0: 219 raise Exception("iw interface add failed") 220 wpas_hapd = hostapd.add_ap(wpas_apdev, params) 221 222 if run_csa: 223 if 'OK' not in hapd.request("CHAN_SWITCH 5 2462 ht"): 224 raise Exception("chan switch request failed") 225 226 ev = hapd.wait_event(["AP-CSA-FINISHED"], timeout=5) 227 if not ev: 228 raise Exception("chan switch failed") 229 230 # now check station 231 ev = dev[0].wait_event(["CTRL-EVENT-CHANNEL-SWITCH", 232 "CTRL-EVENT-DISCONNECTED"], timeout=5) 233 if not ev: 234 raise Exception("sta - no chanswitch event") 235 if "CTRL-EVENT-CHANNEL-SWITCH" not in ev and not allow_csa_fail: 236 raise Exception("Received disconnection event instead of channel switch event") 237 238 if add_apdev: 239 remove_apdev(dev[0], wpas_apdev['ifname']) 240 except: 241 if wpas_apdev: 242 remove_apdev(dev[0], wpas_apdev['ifname']) 243 raise 244 245 return hapd 246 247def test_multi_ap_wps_shared(dev, apdev): 248 """WPS on shared fronthaul/backhaul AP""" 249 ssid = "multi-ap-wps" 250 passphrase = "12345678" 251 params = hostapd.wpa2_params(ssid=ssid, passphrase=passphrase) 252 params.update({"multi_ap": "3", 253 "multi_ap_backhaul_ssid": '"%s"' % ssid, 254 "multi_ap_backhaul_wpa_passphrase": passphrase}) 255 hapd = run_multi_ap_wps(dev, apdev, params) 256 # Verify WPS parameter update with Multi-AP 257 if "OK" not in hapd.request("RELOAD"): 258 raise Exception("hostapd RELOAD failed") 259 dev[0].wait_disconnected() 260 dev[0].request("REMOVE_NETWORK all") 261 hapd.request("WPS_PBC") 262 dev[0].request("WPS_PBC multi_ap=1") 263 dev[0].wait_connected(timeout=20) 264 265def test_multi_ap_wps_shared_csa(dev, apdev): 266 """WPS on shared fronthaul/backhaul AP, run CSA""" 267 ssid = "multi-ap-wps-csa" 268 passphrase = "12345678" 269 params = hostapd.wpa2_params(ssid=ssid, passphrase=passphrase) 270 params.update({"multi_ap": "3", 271 "multi_ap_backhaul_ssid": '"%s"' % ssid, 272 "multi_ap_backhaul_wpa_passphrase": passphrase}) 273 run_multi_ap_wps(dev, apdev, params, run_csa=True) 274 275def test_multi_ap_wps_shared_apdev_csa(dev, apdev): 276 """WPS on shared fronthaul/backhaul AP add apdev on same phy and run CSA""" 277 ssid = "multi-ap-wps-apdev-csa" 278 passphrase = "12345678" 279 params = hostapd.wpa2_params(ssid=ssid, passphrase=passphrase) 280 params.update({"multi_ap": "3", 281 "multi_ap_backhaul_ssid": '"%s"' % ssid, 282 "multi_ap_backhaul_wpa_passphrase": passphrase}) 283 # This case is currently failing toc omplete CSA on the station interface. 284 # For the time being, ignore that to avoid always failing tests. Full 285 # validation can be enabled once the issue behind this is fixed. 286 run_multi_ap_wps(dev, apdev, params, add_apdev=True, run_csa=True, 287 allow_csa_fail=True) 288 289def test_multi_ap_wps_shared_psk(dev, apdev): 290 """WPS on shared fronthaul/backhaul AP using PSK""" 291 ssid = "multi-ap-wps" 292 psk = "1234567890abcdef0123456789abcdef0123456789abcdef0123456789abcdef" 293 params = hostapd.wpa2_params(ssid=ssid) 294 params.update({"wpa_psk": psk, 295 "multi_ap": "3", 296 "multi_ap_backhaul_ssid": '"%s"' % ssid, 297 "multi_ap_backhaul_wpa_psk": psk}) 298 run_multi_ap_wps(dev, apdev, params) 299 300def test_multi_ap_wps_split(dev, apdev): 301 """WPS on split fronthaul and backhaul AP""" 302 backhaul_ssid = "multi-ap-backhaul-wps" 303 backhaul_passphrase = "87654321" 304 params = hostapd.wpa2_params(ssid="multi-ap-fronthaul-wps", 305 passphrase="12345678") 306 params.update({"multi_ap": "2", 307 "multi_ap_backhaul_ssid": '"%s"' % backhaul_ssid, 308 "multi_ap_backhaul_wpa_passphrase": backhaul_passphrase}) 309 params_backhaul = hostapd.wpa2_params(ssid=backhaul_ssid, 310 passphrase=backhaul_passphrase) 311 params_backhaul.update({"multi_ap": "1"}) 312 313 run_multi_ap_wps(dev, apdev, params, params_backhaul) 314 315def test_multi_ap_wps_split_psk(dev, apdev): 316 """WPS on split fronthaul and backhaul AP""" 317 backhaul_ssid = "multi-ap-backhaul-wps" 318 backhaul_psk = "1234567890abcdef0123456789abcdef0123456789abcdef0123456789abcdef" 319 params = hostapd.wpa2_params(ssid="multi-ap-fronthaul-wps", 320 passphrase="12345678") 321 params.update({"multi_ap": "2", 322 "multi_ap_backhaul_ssid": '"%s"' % backhaul_ssid, 323 "multi_ap_backhaul_wpa_psk": backhaul_psk}) 324 params_backhaul = hostapd.wpa2_params(ssid=backhaul_ssid) 325 params_backhaul.update({"multi_ap": "1", "wpa_psk": backhaul_psk}) 326 327 run_multi_ap_wps(dev, apdev, params, params_backhaul) 328 329def test_multi_ap_wps_split_mixed(dev, apdev): 330 """WPS on split fronthaul and backhaul AP with mixed-mode fronthaul""" 331 skip_without_tkip(dev[0]) 332 backhaul_ssid = "multi-ap-backhaul-wps" 333 backhaul_passphrase = "87654321" 334 params = hostapd.wpa_mixed_params(ssid="multi-ap-fronthaul-wps", 335 passphrase="12345678") 336 params.update({"multi_ap": "2", 337 "multi_ap_backhaul_ssid": '"%s"' % backhaul_ssid, 338 "multi_ap_backhaul_wpa_passphrase": backhaul_passphrase}) 339 params_backhaul = hostapd.wpa2_params(ssid=backhaul_ssid, 340 passphrase=backhaul_passphrase) 341 params_backhaul.update({"multi_ap": "1"}) 342 343 run_multi_ap_wps(dev, apdev, params, params_backhaul) 344 345def test_multi_ap_wps_split_open(dev, apdev): 346 """WPS on split fronthaul and backhaul AP with open fronthaul""" 347 backhaul_ssid = "multi-ap-backhaul-wps" 348 backhaul_passphrase = "87654321" 349 params = {"ssid": "multi-ap-wps-fronthaul", "multi_ap": "2", 350 "multi_ap_backhaul_ssid": '"%s"' % backhaul_ssid, 351 "multi_ap_backhaul_wpa_passphrase": backhaul_passphrase} 352 params_backhaul = hostapd.wpa2_params(ssid=backhaul_ssid, 353 passphrase=backhaul_passphrase) 354 params_backhaul.update({"multi_ap": "1"}) 355 356 run_multi_ap_wps(dev, apdev, params, params_backhaul) 357 358def test_multi_ap_wps_fail_non_multi_ap(dev, apdev): 359 """Multi-AP WPS on non-WPS AP fails""" 360 361 params = hostapd.wpa2_params(ssid="non-multi-ap-wps", passphrase="12345678") 362 params.update({"wps_state": "2", "eap_server": "1"}) 363 364 hapd = hostapd.add_ap(apdev[0], params) 365 hapd.request("WPS_PBC") 366 if "PBC Status: Active" not in hapd.request("WPS_GET_STATUS"): 367 raise Exception("PBC status not shown correctly") 368 369 dev[0].scan_for_bss(apdev[0]['bssid'], freq="2412") 370 dev[0].request("WPS_PBC %s multi_ap=1" % apdev[0]['bssid']) 371 # Since we will fail to associate and WPS doesn't even get started, there 372 # isn't much we can do except wait for timeout. For PBC, it is not possible 373 # to change the timeout from 2 minutes. Instead of waiting for the timeout, 374 # just check that WPS doesn't finish within reasonable time. 375 for i in range(2): 376 ev = dev[0].wait_event(["WPS-SUCCESS", "WPS-FAIL", 377 "CTRL-EVENT-DISCONNECTED"], timeout=10) 378 if ev and "WPS-" in ev: 379 raise Exception("WPS operation completed: " + ev) 380 dev[0].request("WPS_CANCEL") 381