[AccessPoint]merge scapy & gale changes.

Bug: 118448792
Test: Tested locally
Change-Id: Ibe183679088b5ef96e5f3958f9795213c34494fc
diff --git a/acts/framework/acts/controllers/access_point.py b/acts/framework/acts/controllers/access_point.py
index 313fcf9..ff6eb12 100755
--- a/acts/framework/acts/controllers/access_point.py
+++ b/acts/framework/acts/controllers/access_point.py
@@ -17,6 +17,7 @@
 import collections
 import ipaddress
 import logging
+import os
 import time
 
 from acts import logger
@@ -38,6 +39,12 @@
 ACTS_CONTROLLER_REFERENCE_NAME = 'access_points'
 _BRCTL = 'brctl'
 
+LIFETIME = 180
+PROC_NET_SNMP6 = '/proc/net/snmp6'
+SCAPY_INSTALL_COMMAND = 'sudo python setup.py install'
+RA_MULTICAST_ADDR = '33:33:00:00:00:01'
+RA_SCRIPT = 'sendra.py'
+
 
 def create(configs):
     """Creates ap controllers from a json config.
@@ -142,6 +149,7 @@
         self.wlan_5g = self.wlan[1]
         self.lan = self.interfaces.get_lan_interface()
         self.__initial_ap()
+        self.scapy_install_path = None
 
     def __initial_ap(self):
         """Initial AP interfaces.
@@ -149,40 +157,27 @@
         Bring down hostapd if instance is running, bring down all bridge
         interfaces.
         """
+        # This is necessary for Gale/Whirlwind flashed with dev channel image
+        # Unused interfaces such as existing hostapd daemon, guest, mesh
+        # interfaces need to be brought down as part of the AP initialization
+        # process, otherwise test would fail.
         try:
-            # This is necessary for Gale/Whirlwind flashed with dev channel image
-            # Unused interfaces such as existing hostapd daemon, guest, mesh
-            # interfaces need to be brought down as part of the AP initialization
-            # process, otherwise test would fail.
-            try:
-                self.ssh.run('stop hostapd')
-            except job.Error:
-                self.log.debug('No hostapd running')
-            # Bring down all wireless interfaces
-            for iface in self.wlan:
-                WLAN_DOWN = 'ifconfig {} down'.format(iface)
-                self.ssh.run(WLAN_DOWN)
-            # Bring down all bridge interfaces
-            bridge_interfaces = self.interfaces.get_bridge_interface()
-            if bridge_interfaces:
-                for iface in bridge_interfaces:
-                    BRIDGE_DOWN = 'ifconfig {} down'.format(iface)
-                    BRIDGE_DEL = 'brctl delbr {}'.format(iface)
-                    self.ssh.run(BRIDGE_DOWN)
-                    self.ssh.run(BRIDGE_DEL)
-        except Exception:
-            # TODO(b/76101464): APs may not clean up properly from previous
-            # runs. Rebooting the AP can put them back into the correct state.
-            self.log.exception('Unable to bring down hostapd. Rebooting.')
-            # Reboot the AP.
-            try:
-                self.ssh.run('reboot')
-                # This sleep ensures the device had time to go down.
-                time.sleep(10)
-                self.ssh.run('echo connected', timeout=300)
-            except Exception as e:
-                self.log.exception("Error in rebooting AP: %s", e)
-                raise
+            self.ssh.run('stop wpasupplicant')
+            self.ssh.run('stop hostapd')
+        except job.Error:
+            self.log.debug('No hostapd running')
+        # Bring down all wireless interfaces
+        for iface in self.wlan:
+            WLAN_DOWN = 'ifconfig {} down'.format(iface)
+            self.ssh.run(WLAN_DOWN)
+        # Bring down all bridge interfaces
+        bridge_interfaces = self.interfaces.get_bridge_interface()
+        if bridge_interfaces:
+            for iface in bridge_interfaces:
+                BRIDGE_DOWN = 'ifconfig {} down'.format(iface)
+                BRIDGE_DEL = 'brctl delbr {}'.format(iface)
+                self.ssh.run(BRIDGE_DOWN)
+                self.ssh.run(BRIDGE_DEL)
 
     def start_ap(self, hostapd_config, additional_parameters=None):
         """Starts as an ap using a set of configurations.
@@ -409,3 +404,60 @@
         configs = (iface_wlan, iface_lan, bridge_ip)
 
         return configs
+
+    def install_scapy(self, scapy_path, send_ra_path):
+        """Install scapy
+
+        Args:
+            scapy_path: path where scapy tar file is located on server
+            send_ra_path: path where sendra path is located on server
+        """
+        self.scapy_install_path = self.ssh.run('mktemp -d').stdout.rstrip()
+        self.log.info("Scapy install path: %s" % self.scapy_install_path)
+        self.ssh.send_file(scapy_path, self.scapy_install_path)
+        self.ssh.send_file(send_ra_path, self.scapy_install_path)
+
+        scapy = os.path.join(self.scapy_install_path, scapy_path.split('/')[-1])
+
+        untar_res = self.ssh.run(
+            'tar -xvf %s -C %s' % (scapy, self.scapy_install_path))
+
+        instl_res = self.ssh.run(
+            'cd %s; %s' % (self.scapy_install_path, SCAPY_INSTALL_COMMAND))
+
+    def cleanup_scapy(self):
+        """ Cleanup scapy """
+        if self.scapy_install_path:
+            cmd = 'rm -rf %s' % self.scapy_install_path
+            self.log.info("Cleaning up scapy %s" % cmd)
+            output = self.ssh.run(cmd)
+            self.scapy_install_path = None
+
+    def send_ra(self, iface, mac=RA_MULTICAST_ADDR, interval=1, count=None,
+                lifetime=LIFETIME, rtt=0):
+        """Invoke scapy and send RA to the device.
+
+        Args:
+          iface: string of the WiFi interface to use for sending packets.
+          mac: string HWAddr/MAC address to send the packets to.
+          interval: int Time to sleep between consecutive packets.
+          count: int Number of packets to be sent.
+          lifetime: int original RA's router lifetime in seconds.
+          rtt: retrans timer of the RA packet
+        """
+        scapy_command = os.path.join(self.scapy_install_path, RA_SCRIPT)
+        options = ' -m %s -i %d -c %d -l %d -in %s -rtt %s' % (
+            mac, interval, count, lifetime, iface, rtt)
+        self.log.info("Scapy cmd: %s" % scapy_command + options)
+        res = self.ssh.run(scapy_command + options)
+
+    def get_icmp6intype134(self):
+        """Read the value of Icmp6InType134 and return integer.
+
+        Returns:
+            Integer value >0 if grep is successful; 0 otherwise.
+        """
+        ra_count_str = self.ssh.run('grep Icmp6InType134 %s || true' %
+                                    PROC_NET_SNMP6).stdout
+        if ra_count_str:
+            return int(ra_count_str.split()[1])