1#!/usr/bin/env python3 2# 3# Copyright 2017 Google, Inc. 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 17import logging 18import time 19from acts import utils 20from acts.libs.proc import job 21from acts.controllers.ap_lib import bridge_interface as bi 22from acts.test_utils.wifi import wifi_test_utils as wutils 23from acts.controllers.ap_lib import hostapd_security 24from acts.controllers.ap_lib import hostapd_ap_preset 25 26# http://www.secdev.org/projects/scapy/ 27# On ubuntu, sudo pip3 install scapy 28import scapy.all as scapy 29 30GET_FROM_PHONE = 'get_from_dut' 31GET_FROM_AP = 'get_from_ap' 32ENABLED_MODULATED_DTIM = 'gEnableModulatedDTIM=' 33MAX_MODULATED_DTIM = 'gMaxLIModulatedDTIM=' 34 35 36def change_dtim(ad, gEnableModulatedDTIM, gMaxLIModulatedDTIM=10): 37 """Function to change the DTIM setting in the phone. 38 39 Args: 40 ad: the target android device, AndroidDevice object 41 gEnableModulatedDTIM: Modulated DTIM, int 42 gMaxLIModulatedDTIM: Maximum modulated DTIM, int 43 """ 44 # First trying to find the ini file with DTIM settings 45 ini_file_phone = ad.adb.shell('ls /vendor/firmware/wlan/*/*.ini') 46 ini_file_local = ini_file_phone.split('/')[-1] 47 48 # Pull the file and change the DTIM to desired value 49 ad.adb.pull('{} {}'.format(ini_file_phone, ini_file_local)) 50 51 with open(ini_file_local, 'r') as fin: 52 for line in fin: 53 if ENABLED_MODULATED_DTIM in line: 54 gE_old = line.strip('\n') 55 gEDTIM_old = line.strip(ENABLED_MODULATED_DTIM).strip('\n') 56 if MAX_MODULATED_DTIM in line: 57 gM_old = line.strip('\n') 58 gMDTIM_old = line.strip(MAX_MODULATED_DTIM).strip('\n') 59 fin.close() 60 if int(gEDTIM_old) == gEnableModulatedDTIM and int( 61 gMDTIM_old) == gMaxLIModulatedDTIM: 62 ad.log.info('Current DTIM is already the desired value,' 63 'no need to reset it') 64 return 0 65 66 gE_new = ENABLED_MODULATED_DTIM + str(gEnableModulatedDTIM) 67 gM_new = MAX_MODULATED_DTIM + str(gMaxLIModulatedDTIM) 68 69 sed_gE = 'sed -i \'s/{}/{}/g\' {}'.format(gE_old, gE_new, ini_file_local) 70 sed_gM = 'sed -i \'s/{}/{}/g\' {}'.format(gM_old, gM_new, ini_file_local) 71 job.run(sed_gE) 72 job.run(sed_gM) 73 74 # Push the file to the phone 75 push_file_to_phone(ad, ini_file_local, ini_file_phone) 76 ad.log.info('DTIM changes checked in and rebooting...') 77 ad.reboot() 78 # Wait for auto-wifi feature to start 79 time.sleep(20) 80 ad.adb.shell('dumpsys battery set level 100') 81 ad.log.info('DTIM updated and device back from reboot') 82 return 1 83 84 85def push_file_to_phone(ad, file_local, file_phone): 86 """Function to push local file to android phone. 87 88 Args: 89 ad: the target android device 90 file_local: the locla file to push 91 file_phone: the file/directory on the phone to be pushed 92 """ 93 ad.adb.root() 94 cmd_out = ad.adb.remount() 95 if 'Permission denied' in cmd_out: 96 ad.log.info('Need to disable verity first and reboot') 97 ad.adb.disable_verity() 98 time.sleep(1) 99 ad.reboot() 100 ad.log.info('Verity disabled and device back from reboot') 101 ad.adb.root() 102 ad.adb.remount() 103 time.sleep(1) 104 ad.adb.push('{} {}'.format(file_local, file_phone)) 105 106 107def ap_setup(ap, network, bandwidth=80): 108 """Set up the whirlwind AP with provided network info. 109 110 Args: 111 ap: access_point object of the AP 112 network: dict with information of the network, including ssid, password 113 bssid, channel etc. 114 bandwidth: the operation bandwidth for the AP, default 80MHz 115 Returns: 116 brconfigs: the bridge interface configs 117 """ 118 log = logging.getLogger() 119 bss_settings = [] 120 ssid = network[wutils.WifiEnums.SSID_KEY] 121 if "password" in network.keys(): 122 password = network["password"] 123 security = hostapd_security.Security( 124 security_mode="wpa", password=password) 125 else: 126 security = hostapd_security.Security(security_mode=None, password=None) 127 channel = network["channel"] 128 config = hostapd_ap_preset.create_ap_preset( 129 channel=channel, 130 ssid=ssid, 131 security=security, 132 bss_settings=bss_settings, 133 vht_bandwidth=bandwidth, 134 profile_name='whirlwind', 135 iface_wlan_2g=ap.wlan_2g, 136 iface_wlan_5g=ap.wlan_5g) 137 config_bridge = ap.generate_bridge_configs(channel) 138 brconfigs = bi.BridgeInterfaceConfigs(config_bridge[0], config_bridge[1], 139 config_bridge[2]) 140 ap.bridge.startup(brconfigs) 141 ap.start_ap(config) 142 log.info("AP started on channel {} with SSID {}".format(channel, ssid)) 143 return brconfigs 144 145 146def run_iperf_client_nonblocking(ad, server_host, extra_args=""): 147 """Start iperf client on the device with nohup. 148 149 Return status as true if iperf client start successfully. 150 And data flow information as results. 151 152 Args: 153 ad: the android device under test 154 server_host: Address of the iperf server. 155 extra_args: A string representing extra arguments for iperf client, 156 e.g. "-i 1 -t 30". 157 158 """ 159 log = logging.getLogger() 160 ad.adb.shell_nb("nohup >/dev/null 2>&1 sh -c 'iperf3 -c {} {} &'".format( 161 server_host, extra_args)) 162 log.info("IPerf client started") 163 164 165def get_wifi_rssi(ad): 166 """Get the RSSI of the device. 167 168 Args: 169 ad: the android device under test 170 Returns: 171 RSSI: the rssi level of the device 172 """ 173 RSSI = ad.droid.wifiGetConnectionInfo()['rssi'] 174 return RSSI 175 176 177def get_phone_ip(ad): 178 """Get the WiFi IP address of the phone. 179 180 Args: 181 ad: the android device under test 182 Returns: 183 IP: IP address of the phone for WiFi, as a string 184 """ 185 IP = ad.droid.connectivityGetIPv4Addresses('wlan0')[0] 186 187 return IP 188 189 190def get_phone_mac(ad): 191 """Get the WiFi MAC address of the phone. 192 193 Args: 194 ad: the android device under test 195 Returns: 196 mac: MAC address of the phone for WiFi, as a string 197 """ 198 mac = ad.droid.wifiGetConnectionInfo()["mac_address"] 199 200 return mac 201 202 203def get_phone_ipv6(ad): 204 """Get the WiFi IPV6 address of the phone. 205 206 Args: 207 ad: the android device under test 208 Returns: 209 IPv6: IPv6 address of the phone for WiFi, as a string 210 """ 211 IPv6 = ad.droid.connectivityGetLinkLocalIpv6Address('wlan0')[:-6] 212 213 return IPv6 214 215 216def get_if_addr6(intf, address_type): 217 """Returns the Ipv6 address from a given local interface. 218 219 Returns the desired IPv6 address from the interface 'intf' in human 220 readable form. The address type is indicated by the IPv6 constants like 221 IPV6_ADDR_LINKLOCAL, IPV6_ADDR_GLOBAL, etc. If no address is found, 222 None is returned. 223 224 Args: 225 intf: desired interface name 226 address_type: addrees typle like LINKLOCAL or GLOBAL 227 228 Returns: 229 Ipv6 address of the specified interface in human readable format 230 """ 231 for if_list in scapy.in6_getifaddr(): 232 if if_list[2] == intf and if_list[1] == address_type: 233 return if_list[0] 234 235 return None 236 237 238def wait_for_dhcp(interface_name): 239 """Wait the DHCP address assigned to desired interface. 240 241 Getting DHCP address takes time and the wait time isn't constant. Utilizing 242 utils.timeout to keep trying until success 243 244 Args: 245 interface_name: desired interface name 246 Returns: 247 ip: ip address of the desired interface name 248 Raise: 249 TimeoutError: After timeout, if no DHCP assigned, raise 250 """ 251 log = logging.getLogger() 252 reset_host_interface(interface_name) 253 start_time = time.time() 254 time_limit_seconds = 60 255 ip = '0.0.0.0' 256 while start_time + time_limit_seconds > time.time(): 257 ip = scapy.get_if_addr(interface_name) 258 if ip == '0.0.0.0': 259 time.sleep(1) 260 else: 261 log.info( 262 'DHCP address assigned to %s as %s' % (interface_name, ip)) 263 return ip 264 raise TimeoutError('Timed out while getting if_addr after %s seconds.' % 265 time_limit_seconds) 266 267 268def reset_host_interface(intferface_name): 269 """Reset the host interface. 270 271 Args: 272 intferface_name: the desired interface to reset 273 """ 274 log = logging.getLogger() 275 intf_down_cmd = 'ifconfig %s down' % intferface_name 276 intf_up_cmd = 'ifconfig %s up' % intferface_name 277 try: 278 job.run(intf_down_cmd) 279 time.sleep(10) 280 job.run(intf_up_cmd) 281 log.info('{} has been reset'.format(intferface_name)) 282 except job.Error: 283 raise Exception('No such interface') 284 285 286def bringdown_host_interface(intferface_name): 287 """Reset the host interface. 288 289 Args: 290 intferface_name: the desired interface to reset 291 """ 292 log = logging.getLogger() 293 intf_down_cmd = 'ifconfig %s down' % intferface_name 294 try: 295 job.run(intf_down_cmd) 296 time.sleep(2) 297 log.info('{} has been brought down'.format(intferface_name)) 298 except job.Error: 299 raise Exception('No such interface') 300 301 302def create_pkt_config(test_class): 303 """Creates the config for generating multicast packets 304 305 Args: 306 test_class: object with all networking paramters 307 308 Returns: 309 Dictionary with the multicast packet config 310 """ 311 addr_type = (scapy.IPV6_ADDR_LINKLOCAL 312 if test_class.ipv6_src_type == 'LINK_LOCAL' else 313 scapy.IPV6_ADDR_GLOBAL) 314 315 mac_dst = test_class.mac_dst 316 if GET_FROM_PHONE in test_class.mac_dst: 317 mac_dst = get_phone_mac(test_class.dut) 318 319 ipv4_dst = test_class.ipv4_dst 320 if GET_FROM_PHONE in test_class.ipv4_dst: 321 ipv4_dst = get_phone_ip(test_class.dut) 322 323 ipv6_dst = test_class.ipv6_dst 324 if GET_FROM_PHONE in test_class.ipv6_dst: 325 ipv6_dst = get_phone_ipv6(test_class.dut) 326 327 ipv4_gw = test_class.ipv4_gwt 328 if GET_FROM_AP in test_class.ipv4_gwt: 329 ipv4_gw = test_class.access_point.ssh_settings.hostname 330 331 pkt_gen_config = { 332 'interf': test_class.pkt_sender.interface, 333 'subnet_mask': test_class.sub_mask, 334 'src_mac': test_class.mac_src, 335 'dst_mac': mac_dst, 336 'src_ipv4': test_class.ipv4_src, 337 'dst_ipv4': ipv4_dst, 338 'src_ipv6': test_class.ipv6_src, 339 'src_ipv6_type': addr_type, 340 'dst_ipv6': ipv6_dst, 341 'gw_ipv4': ipv4_gw 342 } 343 return pkt_gen_config 344