openwrt support for wpa3 and hidden

Bug: 158349179
Test: Verified the changes
Exempt-From-Owner-Approval: Cherry picking changes from git_master
Merged-In: Icc633388bd658b51da67c95d1770f35cb63e995d
Merged-In: I48094dad9483676dfb50d79685f91c6891c66ab8
Change-Id: If25f0f373af150ba2f16443c49e5ff88c34ecf5c
diff --git a/acts/framework/acts/controllers/openwrt_ap.py b/acts/framework/acts/controllers/openwrt_ap.py
index f7154b9..702c4e9 100644
--- a/acts/framework/acts/controllers/openwrt_ap.py
+++ b/acts/framework/acts/controllers/openwrt_ap.py
@@ -1,6 +1,7 @@
 """Controller for Open WRT access point."""
 
 import time
+import yaml
 
 from acts import logger
 from acts.controllers.ap_lib import hostapd_constants
@@ -15,7 +16,10 @@
 PSK_SECURITY = "psk2"
 WEP_SECURITY = "wep"
 ENT_SECURITY = "wpa2"
+OWE_SECURITY = "owe"
+SAE_SECURITY = "sae"
 ENABLE_RADIO = "0"
+PMF_ENABLED = 2
 WIFI_2G = "wifi2g"
 WIFI_5G = "wifi5g"
 
@@ -148,6 +152,58 @@
     self.ssh.run("wifi down")
     time.sleep(9)  # wait for sometime for AP to go down
 
+  def get_bssids_for_wifi_networks(self):
+    """Get BSSIDs for wifi networks configured.
+
+    Returns:
+      Dictionary of SSID - BSSID map for both bands.
+    """
+    bssid_map = {"2g": {}, "5g": {}}
+    for radio in ["radio0", "radio1"]:
+      ssid_ifname_map = self.get_ifnames_for_ssids(radio)
+      if radio == "radio0":
+        for ssid, ifname in ssid_ifname_map.items():
+          bssid_map["5g"][ssid] = self.get_bssid(ifname)
+      elif radio == "radio1":
+        for ssid, ifname in ssid_ifname_map.items():
+          bssid_map["2g"][ssid] = self.get_bssid(ifname)
+    return bssid_map
+
+  def get_ifnames_for_ssids(self, radio):
+    """Get interfaces for wifi networks.
+
+    Args:
+      radio: 2g or 5g radio get the bssids from.
+
+    Returns:
+      dictionary of ssid - ifname mappings.
+    """
+    ssid_ifname_map = {}
+    str_output = self.ssh.run("wifi status %s" % radio).stdout
+    wifi_status = yaml.load(str_output.replace("\t", "").replace("\n", ""),
+                            Loader=yaml.FullLoader)
+    wifi_status = wifi_status[radio]
+    if wifi_status["up"]:
+      interfaces = wifi_status["interfaces"]
+      for config in interfaces:
+        ssid = config["config"]["ssid"]
+        ifname = config["ifname"]
+        ssid_ifname_map[ssid] = ifname
+    return ssid_ifname_map
+
+  def get_bssid(self, ifname):
+    """Get MAC address from an interface.
+
+    Args:
+      ifname: interface name of the corresponding MAC.
+
+    Returns:
+      BSSID of the interface.
+    """
+    ifconfig = self.ssh.run("ifconfig %s" % ifname).stdout
+    mac_addr = ifconfig.split("\n")[0].split()[-1]
+    return mac_addr
+
   def generate_wireless_configs(self, wifi_configs):
     """Generate wireless configs to configure.
 
@@ -190,6 +246,23 @@
                                              config["security"],
                                              hostapd_constants.BAND_2G,
                                              hidden=config["hiddenSSID"]))
+        elif config["security"] == OWE_SECURITY:
+          wireless_configs.append(
+              wireless_config.WirelessConfig("%s%s" % (WIFI_2G, num_2g),
+                                             config["SSID"],
+                                             config["security"],
+                                             hostapd_constants.BAND_2G,
+                                             hidden=config["hiddenSSID"],
+                                             ieee80211w=PMF_ENABLED))
+        elif config["security"] == SAE_SECURITY:
+          wireless_configs.append(
+              wireless_config.WirelessConfig("%s%s" % (WIFI_2G, num_2g),
+                                             config["SSID"],
+                                             config["security"],
+                                             hostapd_constants.BAND_2G,
+                                             password=config["password"],
+                                             hidden=config["hiddenSSID"],
+                                             ieee80211w=PMF_ENABLED))
         elif config["security"] == ENT_SECURITY:
           wireless_configs.append(
               wireless_config.WirelessConfig(
@@ -227,6 +300,23 @@
                                              config["security"],
                                              hostapd_constants.BAND_5G,
                                              hidden=config["hiddenSSID"]))
+        elif config["security"] == OWE_SECURITY:
+          wireless_configs.append(
+              wireless_config.WirelessConfig("%s%s" % (WIFI_5G, num_5g),
+                                             config["SSID"],
+                                             config["security"],
+                                             hostapd_constants.BAND_5G,
+                                             hidden=config["hiddenSSID"],
+                                             ieee80211w=PMF_ENABLED))
+        elif config["security"] == SAE_SECURITY:
+          wireless_configs.append(
+              wireless_config.WirelessConfig("%s%s" % (WIFI_5G, num_5g),
+                                             config["SSID"],
+                                             config["security"],
+                                             hostapd_constants.BAND_5G,
+                                             password=config["password"],
+                                             hidden=config["hiddenSSID"],
+                                             ieee80211w=PMF_ENABLED))
         elif config["security"] == ENT_SECURITY:
           wireless_configs.append(
               wireless_config.WirelessConfig(
diff --git a/acts/framework/acts/controllers/openwrt_lib/wireless_config.py b/acts/framework/acts/controllers/openwrt_lib/wireless_config.py
index ea89636..7810fa2 100644
--- a/acts/framework/acts/controllers/openwrt_lib/wireless_config.py
+++ b/acts/framework/acts/controllers/openwrt_lib/wireless_config.py
@@ -19,6 +19,7 @@
     radius_server_port: Port number of radius server.
     radius_server_secret: Secret key of radius server.
     hidden: Boolean, if the wifi network is hidden.
+    ieee80211w: PMF bit of the wifi network.
   """
 
   def __init__(
@@ -34,7 +35,8 @@
       radius_server_ip=None,
       radius_server_port=None,
       radius_server_secret=None,
-      hidden=False):
+      hidden=False,
+      ieee80211w=None):
     self.name = name
     self.ssid = ssid
     self.security = security
@@ -47,4 +49,5 @@
     self.radius_server_port = radius_server_port
     self.radius_server_secret = radius_server_secret
     self.hidden = hidden
+    self.ieee80211w = ieee80211w
 
diff --git a/acts/framework/acts/controllers/openwrt_lib/wireless_settings_applier.py b/acts/framework/acts/controllers/openwrt_lib/wireless_settings_applier.py
index 5edec84..3c896aa 100644
--- a/acts/framework/acts/controllers/openwrt_lib/wireless_settings_applier.py
+++ b/acts/framework/acts/controllers/openwrt_lib/wireless_settings_applier.py
@@ -9,6 +9,8 @@
 PSK_SECURITY = "psk2"
 WEP_SECURITY = "wep"
 ENT_SECURITY = "wpa2"
+OWE_SECURITY = "owe"
+SAE_SECURITY = "sae"
 ENABLE_RADIO = "0"
 DISABLE_RADIO = "1"
 ENABLE_HIDDEN = "1"
@@ -96,7 +98,7 @@
                    (config.name, config.ssid))
       self.ssh.run("uci set wireless.%s.encryption='%s'" %
                    (config.name, config.security))
-      if config.security == PSK_SECURITY:
+      if config.security == PSK_SECURITY or config.security == SAE_SECURITY:
         self.ssh.run("uci set wireless.%s.key='%s'" %
                      (config.name, config.password))
       elif config.security == WEP_SECURITY:
@@ -111,6 +113,9 @@
                      (config.name, config.radius_server_ip))
         self.ssh.run("uci set wireless.%s.auth_port='%s'" %
                      (config.name, config.radius_server_port))
+      if config.ieee80211w:
+        self.ssh.run("uci set wireless.%s.ieee80211w='%s'" %
+                     (config.name, config.ieee80211w))
       if config.hidden:
         self.ssh.run("uci set wireless.%s.hidden='%s'" %
                      (config.name, ENABLE_HIDDEN))
diff --git a/acts/framework/acts/test_utils/wifi/WifiBaseTest.py b/acts/framework/acts/test_utils/wifi/WifiBaseTest.py
index 83a17b5..50ec821 100644
--- a/acts/framework/acts/test_utils/wifi/WifiBaseTest.py
+++ b/acts/framework/acts/test_utils/wifi/WifiBaseTest.py
@@ -160,7 +160,8 @@
                          hidden=False,
                          same_ssid=False,
                          ssid_length_2g=hostapd_constants.AP_SSID_LENGTH_2G,
-                         ssid_length_5g=hostapd_constants.AP_SSID_LENGTH_5G):
+                         ssid_length_5g=hostapd_constants.AP_SSID_LENGTH_5G,
+                         security_mode='none'):
         """Generates SSIDs for a open network using a random generator.
 
         Args:
@@ -171,6 +172,7 @@
                        SSID.
             ssid_length_2g: Int, number of characters to use for 2G SSID.
             ssid_length_5g: Int, number of characters to use for 5G SSID.
+            security_mode: 'none' for open and 'OWE' for WPA3 OWE.
 
         Returns: A dict of 2G and 5G network lists for hostapd configuration.
 
@@ -188,13 +190,13 @@
 
         network_dict_2g = {
             "SSID": open_2g_ssid,
-            "security": 'none',
+            "security": security_mode,
             "hiddenSSID": hidden
         }
 
         network_dict_5g = {
             "SSID": open_5g_ssid,
-            "security": 'none',
+            "security": security_mode,
             "hiddenSSID": hidden
         }
 
@@ -348,6 +350,8 @@
             wep_network=False,
             ent_network=False,
             ent_network_pwd=False,
+            owe_network=False,
+            sae_network=False,
             radius_conf_2g=None,
             radius_conf_5g=None,
             radius_conf_pwd=None,
@@ -367,18 +371,26 @@
             wep_network: Boolean, to check if wep network should be configured.
             ent_network: Boolean, to check if ent network should be configured.
             ent_network_pwd: Boolean, to check if ent pwd network should be configured.
+            owe_network: Boolean, to check if owe network should be configured.
+            sae_network: Boolean, to check if sae network should be configured.
             radius_conf_2g: dictionary with enterprise radius server details.
             radius_conf_5g: dictionary with enterprise radius server details.
             radius_conf_pwd: dictionary with enterprise radiuse server details.
             ap_count: APs to configure.
         """
+        if mirror_ap and ap_count == 1:
+             raise ValueError("ap_count cannot be 1 if mirror_ap is True.")
+
         self.reference_networks = []
         self.wpa_networks = []
         self.wep_networks = []
         self.ent_networks = []
         self.ent_networks_pwd = []
         self.open_network = []
-        for _ in range(ap_count):
+        self.owe_networks = []
+        self.sae_networks = []
+        self.bssid_map = []
+        for i in range(ap_count):
             network_list = []
             if wpa_network:
                 wpa_dict = self.get_psk_network(mirror_ap,
@@ -433,10 +445,44 @@
                                                   ssid_length_2g,
                                                   ssid_length_5g)
                 network_list.append(open_dict)
-            self.access_points[_].configure_ap(network_list,
+            if owe_network:
+                owe_dict = self.get_open_network(mirror_ap,
+                                                 self.owe_networks,
+                                                 hidden,
+                                                 same_ssid,
+                                                 ssid_length_2g,
+                                                 ssid_length_5g,
+                                                 "OWE")
+                owe_dict[hostapd_constants.BAND_2G]["security"] = "owe"
+                owe_dict[hostapd_constants.BAND_5G]["security"] = "owe"
+                network_list.append(owe_dict)
+            if sae_network:
+                sae_dict = self.get_psk_network(mirror_ap,
+                                                self.sae_networks,
+                                                hidden,
+                                                same_ssid,
+                                                hostapd_constants.WPA3_KEY_MGMT,
+                                                ssid_length_2g,
+                                                ssid_length_5g,
+                                                passphrase_length_2g,
+                                                passphrase_length_5g)
+                sae_dict[hostapd_constants.BAND_2G]["security"] = "sae"
+                sae_dict[hostapd_constants.BAND_5G]["security"] = "sae"
+                network_list.append(sae_dict)
+            self.access_points[i].configure_ap(network_list,
                                                channel_2g,
                                                channel_5g)
-            self.access_points[_].start_ap()
+            self.access_points[i].start_ap()
+            self.bssid_map.append(
+                self.access_points[i].get_bssids_for_wifi_networks())
+            if mirror_ap:
+                self.access_points[i+1].configure_ap(network_list,
+                                                     channel_2g,
+                                                     channel_5g)
+                self.access_points[i+1].start_ap()
+                self.bssid_map.append(
+                    self.access_points[i+1].get_bssids_for_wifi_networks())
+                break
 
     def legacy_configure_ap_and_start(
             self,