• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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