1#!/usr/bin/env python3 2# 3# Copyright 2017 - Google 4# 5# Licensed under the Apache License, Version 2.0 (the "License"); 6# you may not use this file except in compliance with the License. 7# You may obtain a copy of the License at 8# 9# http://www.apache.org/licenses/LICENSE-2.0 10# 11# Unless required by applicable law or agreed to in writing, software 12# distributed under the License is distributed on an "AS IS" BASIS, 13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14# See the License for the specific language governing permissions and 15# limitations under the License. 16""" 17 Base Class for Defining Common WiFi Test Functionality 18""" 19 20import copy 21import itertools 22import time 23 24import acts.controllers.access_point as ap 25 26from acts import asserts 27from acts import utils 28from acts.base_test import BaseTestClass 29from acts.signals import TestSignal 30from acts.controllers import android_device 31from acts.controllers.ap_lib import hostapd_ap_preset 32from acts.controllers.ap_lib import hostapd_bss_settings 33from acts.controllers.ap_lib import hostapd_constants 34from acts.controllers.ap_lib import hostapd_security 35 36AP_1 = 0 37AP_2 = 1 38MAX_AP_COUNT = 2 39 40 41class WifiBaseTest(BaseTestClass): 42 def __init__(self, controllers): 43 if not hasattr(self, 'android_devices'): 44 BaseTestClass.__init__(self, controllers) 45 if hasattr(self, 'attenuators') and self.attenuators: 46 for attenuator in self.attenuators: 47 attenuator.set_atten(0) 48 49 def get_psk_network( 50 self, 51 mirror_ap, 52 reference_networks, 53 hidden=False, 54 same_ssid=False, 55 security_mode=hostapd_constants.WPA2_STRING, 56 ssid_length_2g=hostapd_constants.AP_SSID_LENGTH_2G, 57 ssid_length_5g=hostapd_constants.AP_SSID_LENGTH_5G, 58 passphrase_length_2g=hostapd_constants.AP_PASSPHRASE_LENGTH_2G, 59 passphrase_length_5g=hostapd_constants.AP_PASSPHRASE_LENGTH_5G): 60 """Generates SSID and passphrase for a WPA2 network using random 61 generator. 62 63 Args: 64 mirror_ap: Boolean, determines if both APs use the same hostapd 65 config or different configs. 66 reference_networks: List of PSK networks. 67 same_ssid: Boolean, determines if both bands on AP use the same 68 SSID. 69 ssid_length_2gecond AP Int, number of characters to use for 2G SSID. 70 ssid_length_5g: Int, number of characters to use for 5G SSID. 71 passphrase_length_2g: Int, length of password for 2G network. 72 passphrase_length_5g: Int, length of password for 5G network. 73 74 Returns: A dict of 2G and 5G network lists for hostapd configuration. 75 76 """ 77 network_dict_2g = {} 78 network_dict_5g = {} 79 ref_5g_security = security_mode 80 ref_2g_security = security_mode 81 82 if same_ssid: 83 ref_2g_ssid = 'xg_%s' % utils.rand_ascii_str(ssid_length_2g) 84 ref_5g_ssid = ref_2g_ssid 85 86 ref_2g_passphrase = utils.rand_ascii_str(passphrase_length_2g) 87 ref_5g_passphrase = ref_2g_passphrase 88 89 else: 90 ref_2g_ssid = '2g_%s' % utils.rand_ascii_str(ssid_length_2g) 91 ref_2g_passphrase = utils.rand_ascii_str(passphrase_length_2g) 92 93 ref_5g_ssid = '5g_%s' % utils.rand_ascii_str(ssid_length_5g) 94 ref_5g_passphrase = utils.rand_ascii_str(passphrase_length_5g) 95 96 network_dict_2g = { 97 "SSID": ref_2g_ssid, 98 "security": ref_2g_security, 99 "password": ref_2g_passphrase, 100 "hiddenSSID": hidden 101 } 102 103 network_dict_5g = { 104 "SSID": ref_5g_ssid, 105 "security": ref_5g_security, 106 "password": ref_5g_passphrase, 107 "hiddenSSID": hidden 108 } 109 110 ap = 0 111 for ap in range(MAX_AP_COUNT): 112 reference_networks.append({ 113 "2g": copy.copy(network_dict_2g), 114 "5g": copy.copy(network_dict_5g) 115 }) 116 if not mirror_ap: 117 break 118 return {"2g": network_dict_2g, "5g": network_dict_5g} 119 120 def get_open_network(self, 121 mirror_ap, 122 open_network, 123 hidden=False, 124 same_ssid=False, 125 ssid_length_2g=hostapd_constants.AP_SSID_LENGTH_2G, 126 ssid_length_5g=hostapd_constants.AP_SSID_LENGTH_5G): 127 """Generates SSIDs for a open network using a random generator. 128 129 Args: 130 mirror_ap: Boolean, determines if both APs use the same hostapd 131 config or different configs. 132 open_network: List of open networks. 133 same_ssid: Boolean, determines if both bands on AP use the same 134 SSID. 135 ssid_length_2g: Int, number of characters to use for 2G SSID. 136 ssid_length_5g: Int, number of characters to use for 5G SSID. 137 138 Returns: A dict of 2G and 5G network lists for hostapd configuration. 139 140 """ 141 network_dict_2g = {} 142 network_dict_5g = {} 143 144 if same_ssid: 145 open_2g_ssid = 'xg_%s' % utils.rand_ascii_str(ssid_length_2g) 146 open_5g_ssid = open_2g_ssid 147 148 else: 149 open_2g_ssid = '2g_%s' % utils.rand_ascii_str(ssid_length_2g) 150 open_5g_ssid = '5g_%s' % utils.rand_ascii_str(ssid_length_5g) 151 152 network_dict_2g = { 153 "SSID": open_2g_ssid, 154 "security": 'none', 155 "hiddenSSID": hidden 156 } 157 158 network_dict_5g = { 159 "SSID": open_5g_ssid, 160 "security": 'none', 161 "hiddenSSID": hidden 162 } 163 164 ap = 0 165 for ap in range(MAX_AP_COUNT): 166 open_network.append({ 167 "2g": copy.copy(network_dict_2g), 168 "5g": copy.copy(network_dict_5g) 169 }) 170 if not mirror_ap: 171 break 172 return {"2g": network_dict_2g, "5g": network_dict_5g} 173 174 def get_wep_network( 175 self, 176 mirror_ap, 177 networks, 178 hidden=False, 179 same_ssid=False, 180 ssid_length_2g=hostapd_constants.AP_SSID_LENGTH_2G, 181 ssid_length_5g=hostapd_constants.AP_SSID_LENGTH_5G, 182 passphrase_length_2g=hostapd_constants.AP_PASSPHRASE_LENGTH_2G, 183 passphrase_length_5g=hostapd_constants.AP_PASSPHRASE_LENGTH_5G): 184 """Generates SSID and passphrase for a WEP network using random 185 generator. 186 187 Args: 188 mirror_ap: Boolean, determines if both APs use the same hostapd 189 config or different configs. 190 networks: List of WEP networks. 191 same_ssid: Boolean, determines if both bands on AP use the same 192 SSID. 193 ssid_length_2gecond AP Int, number of characters to use for 2G SSID. 194 ssid_length_5g: Int, number of characters to use for 5G SSID. 195 passphrase_length_2g: Int, length of password for 2G network. 196 passphrase_length_5g: Int, length of password for 5G network. 197 198 Returns: A dict of 2G and 5G network lists for hostapd configuration. 199 200 """ 201 network_dict_2g = {} 202 network_dict_5g = {} 203 ref_5g_security = hostapd_constants.WEP_STRING 204 ref_2g_security = hostapd_constants.WEP_STRING 205 206 if same_ssid: 207 ref_2g_ssid = 'xg_%s' % utils.rand_ascii_str(ssid_length_2g) 208 ref_5g_ssid = ref_2g_ssid 209 210 ref_2g_passphrase = utils.rand_hex_str(passphrase_length_2g) 211 ref_5g_passphrase = ref_2g_passphrase 212 213 else: 214 ref_2g_ssid = '2g_%s' % utils.rand_ascii_str(ssid_length_2g) 215 ref_2g_passphrase = utils.rand_hex_str(passphrase_length_2g) 216 217 ref_5g_ssid = '5g_%s' % utils.rand_ascii_str(ssid_length_5g) 218 ref_5g_passphrase = utils.rand_hex_str(passphrase_length_5g) 219 220 network_dict_2g = { 221 "SSID": ref_2g_ssid, 222 "security": ref_2g_security, 223 "wepKeys": [ref_2g_passphrase] * 4, 224 "hiddenSSID": hidden 225 } 226 227 network_dict_5g = { 228 "SSID": ref_5g_ssid, 229 "security": ref_5g_security, 230 "wepKeys": [ref_2g_passphrase] * 4, 231 "hiddenSSID": hidden 232 } 233 234 ap = 0 235 for ap in range(MAX_AP_COUNT): 236 networks.append({ 237 "2g": copy.copy(network_dict_2g), 238 "5g": copy.copy(network_dict_5g) 239 }) 240 if not mirror_ap: 241 break 242 return {"2g": network_dict_2g, "5g": network_dict_5g} 243 244 def update_bssid(self, ap_instance, ap, network, band): 245 """Get bssid and update network dictionary. 246 247 Args: 248 ap_instance: Accesspoint index that was configured. 249 ap: Accesspoint object corresponding to ap_instance. 250 network: Network dictionary. 251 band: Wifi networks' band. 252 253 """ 254 bssid = ap.get_bssid_from_ssid(network["SSID"], band) 255 256 if network["security"] == hostapd_constants.WPA2_STRING: 257 # TODO:(bamahadev) Change all occurances of reference_networks 258 # to wpa_networks. 259 self.reference_networks[ap_instance][band]["bssid"] = bssid 260 if network["security"] == hostapd_constants.WPA_STRING: 261 self.wpa_networks[ap_instance][band]["bssid"] = bssid 262 if network["security"] == hostapd_constants.WEP_STRING: 263 self.wep_networks[ap_instance][band]["bssid"] = bssid 264 if network["security"] == hostapd_constants.ENT_STRING: 265 if "bssid" not in self.ent_networks[ap_instance][band]: 266 self.ent_networks[ap_instance][band]["bssid"] = bssid 267 else: 268 self.ent_networks_pwd[ap_instance][band]["bssid"] = bssid 269 if network["security"] == 'none': 270 self.open_network[ap_instance][band]["bssid"] = bssid 271 272 def populate_bssid(self, ap_instance, ap, networks_5g, networks_2g): 273 """Get bssid for a given SSID and add it to the network dictionary. 274 275 Args: 276 ap_instance: Accesspoint index that was configured. 277 ap: Accesspoint object corresponding to ap_instance. 278 networks_5g: List of 5g networks configured on the APs. 279 networks_2g: List of 2g networks configured on the APs. 280 281 """ 282 283 if not (networks_5g or networks_2g): 284 return 285 286 for network in networks_5g: 287 if 'channel' in network: 288 continue 289 self.update_bssid(ap_instance, ap, network, 290 hostapd_constants.BAND_5G) 291 292 for network in networks_2g: 293 if 'channel' in network: 294 continue 295 self.update_bssid(ap_instance, ap, network, 296 hostapd_constants.BAND_2G) 297 298 def legacy_configure_ap_and_start( 299 self, 300 channel_5g=hostapd_constants.AP_DEFAULT_CHANNEL_5G, 301 channel_2g=hostapd_constants.AP_DEFAULT_CHANNEL_2G, 302 max_2g_networks=hostapd_constants.AP_DEFAULT_MAX_SSIDS_2G, 303 max_5g_networks=hostapd_constants.AP_DEFAULT_MAX_SSIDS_5G, 304 ap_ssid_length_2g=hostapd_constants.AP_SSID_LENGTH_2G, 305 ap_passphrase_length_2g=hostapd_constants.AP_PASSPHRASE_LENGTH_2G, 306 ap_ssid_length_5g=hostapd_constants.AP_SSID_LENGTH_5G, 307 ap_passphrase_length_5g=hostapd_constants.AP_PASSPHRASE_LENGTH_5G, 308 hidden=False, 309 same_ssid=False, 310 mirror_ap=True, 311 wpa_network=False, 312 wep_network=False, 313 ent_network=False, 314 radius_conf_2g=None, 315 radius_conf_5g=None, 316 ent_network_pwd=False, 317 radius_conf_pwd=None, 318 ap_count=1): 319 asserts.assert_true( 320 len(self.user_params["AccessPoint"]) == 2, 321 "Exactly two access points must be specified. \ 322 Each access point has 2 radios, one each for 2.4GHZ \ 323 and 5GHz. A test can choose to use one or both APs.") 324 325 config_count = 1 326 count = 0 327 328 # For example, the NetworkSelector tests use 2 APs and require that 329 # both APs are not mirrored. 330 if not mirror_ap and ap_count == 1: 331 raise ValueError("ap_count cannot be 1 if mirror_ap is False.") 332 333 if not mirror_ap: 334 config_count = ap_count 335 336 self.user_params["reference_networks"] = [] 337 self.user_params["open_network"] = [] 338 if wpa_network: 339 self.user_params["wpa_networks"] = [] 340 if wep_network: 341 self.user_params["wep_networks"] = [] 342 if ent_network: 343 self.user_params["ent_networks"] = [] 344 if ent_network_pwd: 345 self.user_params["ent_networks_pwd"] = [] 346 347 for count in range(config_count): 348 349 network_list_2g = [] 350 network_list_5g = [] 351 352 orig_network_list_2g = [] 353 orig_network_list_5g = [] 354 355 network_list_2g.append({"channel": channel_2g}) 356 network_list_5g.append({"channel": channel_5g}) 357 358 networks_dict = self.get_psk_network( 359 mirror_ap, 360 self.user_params["reference_networks"], 361 hidden=hidden, 362 same_ssid=same_ssid) 363 self.reference_networks = self.user_params["reference_networks"] 364 365 network_list_2g.append(networks_dict["2g"]) 366 network_list_5g.append(networks_dict["5g"]) 367 368 # When same_ssid is set, only configure one set of WPA networks. 369 # We cannot have more than one set because duplicate interface names 370 # are not allowed. 371 # TODO(bmahadev): Provide option to select the type of network, 372 # instead of defaulting to WPA. 373 if not same_ssid: 374 networks_dict = self.get_open_network( 375 mirror_ap, 376 self.user_params["open_network"], 377 hidden=hidden, 378 same_ssid=same_ssid) 379 self.open_network = self.user_params["open_network"] 380 381 network_list_2g.append(networks_dict["2g"]) 382 network_list_5g.append(networks_dict["5g"]) 383 384 if wpa_network: 385 networks_dict = self.get_psk_network( 386 mirror_ap, 387 self.user_params["wpa_networks"], 388 hidden=hidden, 389 same_ssid=same_ssid, 390 security_mode=hostapd_constants.WPA_STRING) 391 self.wpa_networks = self.user_params["wpa_networks"] 392 393 network_list_2g.append(networks_dict["2g"]) 394 network_list_5g.append(networks_dict["5g"]) 395 396 if wep_network: 397 networks_dict = self.get_wep_network( 398 mirror_ap, 399 self.user_params["wep_networks"], 400 hidden=hidden, 401 same_ssid=same_ssid) 402 self.wep_networks = self.user_params["wep_networks"] 403 404 network_list_2g.append(networks_dict["2g"]) 405 network_list_5g.append(networks_dict["5g"]) 406 407 if ent_network: 408 networks_dict = self.get_open_network( 409 mirror_ap, 410 self.user_params["ent_networks"], 411 hidden=hidden, 412 same_ssid=same_ssid) 413 networks_dict["2g"]["security"] = hostapd_constants.ENT_STRING 414 networks_dict["2g"].update(radius_conf_2g) 415 networks_dict["5g"]["security"] = hostapd_constants.ENT_STRING 416 networks_dict["5g"].update(radius_conf_5g) 417 self.ent_networks = self.user_params["ent_networks"] 418 419 network_list_2g.append(networks_dict["2g"]) 420 network_list_5g.append(networks_dict["5g"]) 421 422 if ent_network_pwd: 423 networks_dict = self.get_open_network( 424 mirror_ap, 425 self.user_params["ent_networks_pwd"], 426 hidden=hidden, 427 same_ssid=same_ssid) 428 networks_dict["2g"]["security"] = hostapd_constants.ENT_STRING 429 networks_dict["2g"].update(radius_conf_pwd) 430 networks_dict["5g"]["security"] = hostapd_constants.ENT_STRING 431 networks_dict["5g"].update(radius_conf_pwd) 432 self.ent_networks_pwd = self.user_params["ent_networks_pwd"] 433 434 network_list_2g.append(networks_dict["2g"]) 435 network_list_5g.append(networks_dict["5g"]) 436 437 orig_network_list_5g = copy.copy(network_list_5g) 438 orig_network_list_2g = copy.copy(network_list_2g) 439 440 if len(network_list_5g) > 1: 441 self.config_5g = self._generate_legacy_ap_config(network_list_5g) 442 if len(network_list_2g) > 1: 443 self.config_2g = self._generate_legacy_ap_config(network_list_2g) 444 445 self.access_points[count].start_ap(self.config_2g) 446 self.access_points[count].start_ap(self.config_5g) 447 self.populate_bssid(count, self.access_points[count], orig_network_list_5g, 448 orig_network_list_2g) 449 450 # Repeat configuration on the second router. 451 if mirror_ap and ap_count == 2: 452 self.access_points[AP_2].start_ap(self.config_2g) 453 self.access_points[AP_2].start_ap(self.config_5g) 454 self.populate_bssid(AP_2, self.access_points[AP_2], 455 orig_network_list_5g, orig_network_list_2g) 456 457 def _generate_legacy_ap_config(self, network_list): 458 bss_settings = [] 459 wlan_2g = self.access_points[AP_1].wlan_2g 460 wlan_5g = self.access_points[AP_1].wlan_5g 461 ap_settings = network_list.pop(0) 462 # TODO:(bmahadev) This is a bug. We should not have to pop the first 463 # network in the list and treat it as a separate case. Instead, 464 # create_ap_preset() should be able to take NULL ssid and security and 465 # build config based on the bss_Settings alone. 466 hostapd_config_settings = network_list.pop(0) 467 for network in network_list: 468 if "password" in network: 469 bss_settings.append( 470 hostapd_bss_settings.BssSettings( 471 name=network["SSID"], 472 ssid=network["SSID"], 473 hidden=network["hiddenSSID"], 474 security=hostapd_security.Security( 475 security_mode=network["security"], 476 password=network["password"]))) 477 elif "wepKeys" in network: 478 bss_settings.append( 479 hostapd_bss_settings.BssSettings( 480 name=network["SSID"], 481 ssid=network["SSID"], 482 hidden=network["hiddenSSID"], 483 security=hostapd_security.Security( 484 security_mode=network["security"], 485 password=network["wepKeys"][0]))) 486 elif network["security"] == hostapd_constants.ENT_STRING: 487 bss_settings.append( 488 hostapd_bss_settings.BssSettings( 489 name=network["SSID"], 490 ssid=network["SSID"], 491 hidden=network["hiddenSSID"], 492 security=hostapd_security.Security( 493 security_mode=network["security"], 494 radius_server_ip=network["radius_server_ip"], 495 radius_server_port=network["radius_server_port"], 496 radius_server_secret=network["radius_server_secret"]))) 497 else: 498 bss_settings.append( 499 hostapd_bss_settings.BssSettings( 500 name=network["SSID"], 501 ssid=network["SSID"], 502 hidden=network["hiddenSSID"])) 503 if "password" in hostapd_config_settings: 504 config = hostapd_ap_preset.create_ap_preset( 505 iface_wlan_2g=wlan_2g, 506 iface_wlan_5g=wlan_5g, 507 channel=ap_settings["channel"], 508 ssid=hostapd_config_settings["SSID"], 509 hidden=hostapd_config_settings["hiddenSSID"], 510 security=hostapd_security.Security( 511 security_mode=hostapd_config_settings["security"], 512 password=hostapd_config_settings["password"]), 513 bss_settings=bss_settings) 514 elif "wepKeys" in hostapd_config_settings: 515 config = hostapd_ap_preset.create_ap_preset( 516 iface_wlan_2g=wlan_2g, 517 iface_wlan_5g=wlan_5g, 518 channel=ap_settings["channel"], 519 ssid=hostapd_config_settings["SSID"], 520 hidden=hostapd_config_settings["hiddenSSID"], 521 security=hostapd_security.Security( 522 security_mode=hostapd_config_settings["security"], 523 password=hostapd_config_settings["wepKeys"][0]), 524 bss_settings=bss_settings) 525 else: 526 config = hostapd_ap_preset.create_ap_preset( 527 iface_wlan_2g=wlan_2g, 528 iface_wlan_5g=wlan_5g, 529 channel=ap_settings["channel"], 530 ssid=hostapd_config_settings["SSID"], 531 hidden=hostapd_config_settings["hiddenSSID"], 532 bss_settings=bss_settings) 533 return config 534 535 def configure_packet_capture( 536 self, 537 channel_5g=hostapd_constants.AP_DEFAULT_CHANNEL_5G, 538 channel_2g=hostapd_constants.AP_DEFAULT_CHANNEL_2G): 539 """Configure packet capture for 2G and 5G bands. 540 541 Args: 542 channel_5g: Channel to set the monitor mode to for 5G band. 543 channel_2g: Channel to set the monitor mode to for 2G band. 544 """ 545 self.packet_capture = self.packet_capture[0] 546 result = self.packet_capture.configure_monitor_mode( 547 hostapd_constants.BAND_2G, channel_2g) 548 if not result: 549 raise ValueError("Failed to configure channel for 2G band") 550 551 result = self.packet_capture.configure_monitor_mode( 552 hostapd_constants.BAND_5G, channel_5g) 553 if not result: 554 raise ValueError("Failed to configure channel for 5G band.") 555