def start_local_server(self,
                           interface,
                           ap_num=None,
                           server_address_index=None,
                           bridge=None):
        """Start a local server on an interface.

        @param interface string (e.g. wlan0)
        @param ap_num int the ap instance to start the server for
        @param server_address_index int server address index
        @param bridge string (e.g. br0)

        """
        logging.info('Starting up local server...')

        if len(self.local_servers) >= 256:
            raise error.TestFail('Exhausted available local servers')

        # Get an unused server address index if one is not specified.
        # Validate server address index if one is specified.
        if server_address_index is None:
            server_address_index = self._get_unused_server_address_index()
        elif server_address_index in self.server_address_index:
            raise error.TestFail('Server address index %d already in used' %
                                 server_address_index)

        server_addr = netblock.from_addr(
            self.local_server_address(server_address_index), prefix_len=24)

        params = {}
        params['address_index'] = server_address_index
        params['netblock'] = server_addr
        params['dhcp_range'] = ' '.join((server_addr.get_addr_in_block(1),
                                         server_addr.get_addr_in_block(128)))
        params['interface'] = interface
        params['bridge'] = bridge
        params['ip_params'] = (
            '%s broadcast %s dev %s' %
            (server_addr.netblock, server_addr.broadcast, interface))
        if ap_num is None:
            self.local_servers.append(params)
        else:
            self.local_servers.insert(ap_num, params)
        self.server_address_index.append(server_address_index)

        self.router.run('%s addr flush %s' % (self.cmd_ip, interface))
        self.router.run('%s addr add %s' % (self.cmd_ip, params['ip_params']))
        self.router.run('%s link set %s up' % (self.cmd_ip, interface))
        if params['bridge']:
            bridge_addr = netblock.from_addr(
                self.local_bridge_address(server_address_index), prefix_len=24)
            self.router.run("ifconfig %s %s" %
                            (params['bridge'], bridge_addr.netblock))
        self.start_dhcp_server(interface)
    def netblock(self):
        """Return Netblock object for this interface's IPv4 address.

        @return Netblock object (or None if no IPv4 address found).

        """
        netblock_str = self.ipv4_address_and_prefix
        return netblock.from_addr(netblock_str) if netblock_str else None
Beispiel #3
0
class ChrootedAvahi(object):
    """Helper object to start up avahi in a network chroot.

    Creates a virtual ethernet pair to enable communication with avahi.
    Does the necessary work to make avahi appear on DBus and allow it
    to claim its canonical service name.

    """

    SERVICES_TO_STOP = ['avahi']
    # This side has to be called something special to avoid shill touching it.
    MONITOR_IF_IP = netblock.from_addr('10.9.8.1/24')
    # We'll drop the Avahi side into our network namespace.
    AVAHI_IF_IP = netblock.from_addr('10.9.8.2/24')
    AVAHI_IF_NAME = 'pseudoethernet0'
    TCPDUMP_FILE_PATH = '/var/log/peerd_dump.pcap'
    AVAHI_CONFIG_FILE = 'etc/avahi/avahi-daemon.conf'
    AVAHI_CONFIGS = {
        AVAHI_CONFIG_FILE:
        '[server]\n'
        'host-name-from-machine-id=yes\n'
        'browse-domains=\n'
        'use-ipv4=yes\n'
        'use-ipv6=no\n'
        'ratelimit-interval-usec=1000000\n'
        'ratelimit-burst=1000\n'
        '[wide-area]\n'
        'enable-wide-area=no\n'
        '[publish]\n'
        'publish-hinfo=no\n'
        'publish-workstation=no\n'
        'publish-aaaa-on-ipv4=no\n'
        'publish-a-on-ipv6=no\n'
        '[rlimits]\n'
        'rlimit-core=0\n'
        'rlimit-data=4194304\n'
        'rlimit-fsize=1024\n'
        'rlimit-nofile=768\n'
        'rlimit-stack=4194304\n'
        'rlimit-nproc=10\n',
        'etc/passwd':
        'root:x:0:0:root:/root:/bin/bash\n'
        'avahi:*:238:238::/dev/null:/bin/false\n',
        'etc/group':
        'avahi:x:238:\n',
    }
    AVAHI_LOG_FILE = '/var/log/avahi.log'
    AVAHI_PID_FILE = 'run/avahi-daemon/pid'
    AVAHI_UP_TIMEOUT_SECONDS = 10

    def __init__(self, unchrooted_interface_name='pseudoethernet1'):
        """Construct a chrooted instance of Avahi.

        @param unchrooted_interface_name: string name of interface to leave
                outside the network chroot.  This interface will be connected
                to the end Avahi is listening on.

        """
        self._unchrooted_interface_name = unchrooted_interface_name
        self._services = None
        self._vif = None
        self._tcpdump = None
        self._chroot = None

    @property
    def unchrooted_interface_name(self):
        """Get the name of the end of the VirtualEthernetPair not in the chroot.

        The network chroot works by isolating avahi inside with one end of a
        virtual ethernet pair.  The outside world needs to interact with the
        other end in order to talk to avahi.

        @return name of interface not inside the chroot.

        """
        return self._unchrooted_interface_name

    @property
    def avahi_interface_addr(self):
        """@return string ip address of interface belonging to avahi."""
        return self.AVAHI_IF_IP.addr

    @property
    def hostname(self):
        """@return string hostname claimed by avahi on |self.dns_domain|."""
        return avahi_utils.avahi_get_hostname()

    @property
    def dns_domain(self):
        """@return string DNS domain in use by avahi (e.g. 'local')."""
        return avahi_utils.avahi_get_domain_name()

    def start(self):
        """Start up the chrooted Avahi instance."""
        # Prevent weird interactions between services which talk to Avahi.
        # TODO(wiley) Does Chrome need to die here as well?
        self._services = service_stopper.ServiceStopper(self.SERVICES_TO_STOP)
        self._services.stop_services()
        # We don't want Avahi talking to the real world, so give it a nice
        # fake interface to use.  We'll watch the other half of the pair.
        self._vif = virtual_ethernet_pair.VirtualEthernetPair(
            interface_name=self.unchrooted_interface_name,
            peer_interface_name=self.AVAHI_IF_NAME,
            interface_ip=self.MONITOR_IF_IP.netblock,
            peer_interface_ip=self.AVAHI_IF_IP.netblock,
            # Moving one end into the chroot causes errors.
            ignore_shutdown_errors=True)
        self._vif.setup()
        if not self._vif.is_healthy:
            raise error.TestError('Failed to setup virtual ethernet pair.')
        # By default, take a packet capture of everything Avahi sends out.
        self._tcpdump = tcpdump.Tcpdump(self.unchrooted_interface_name,
                                        self.TCPDUMP_FILE_PATH)
        # We're going to run Avahi in a network namespace to avoid interactions
        # with the outside world.
        self._chroot = network_chroot.NetworkChroot(
            self.AVAHI_IF_NAME, self.AVAHI_IF_IP.addr,
            self.AVAHI_IF_IP.prefix_len)
        self._chroot.add_config_templates(self.AVAHI_CONFIGS)
        self._chroot.add_root_directories(['etc/avahi', 'etc/avahi/services'])
        self._chroot.add_copied_config_files(
            ['etc/resolv.conf', 'etc/avahi/hosts'])
        self._chroot.add_startup_command(
            '/usr/sbin/avahi-daemon --file=/%s >%s 2>&1' %
            (self.AVAHI_CONFIG_FILE, self.AVAHI_LOG_FILE))
        self._chroot.bridge_dbus_namespaces()
        self._chroot.startup()
        # Wait for Avahi to come up, claim its DBus name, settle on a hostname.
        start_time = time.time()
        while time.time() - start_time < self.AVAHI_UP_TIMEOUT_SECONDS:
            if avahi_utils.avahi_ping():
                break
            time.sleep(0.2)
        else:
            raise error.TestFail('Avahi did not come up in time.')

    def close(self):
        """Clean up the chrooted Avahi instance."""
        if self._chroot:
            # TODO(wiley) This is sloppy.  Add a helper to move the logs over.
            for line in self._chroot.get_log_contents().splitlines():
                logging.debug(line)
            self._chroot.kill_pid_file(self.AVAHI_PID_FILE)
            self._chroot.shutdown()
        if self._tcpdump:
            self._tcpdump.stop()
        if self._vif:
            self._vif.teardown()
        if self._services:
            self._services.restore_services()
Beispiel #4
0
    def run_once(self, host):
        helper = privet_helper.PrivetHelper(host=host)
        logging.info('Looking for privet bootstrapping network from DUT.')
        scan_interface = self._router.get_wlanif(2437, 'managed')
        self._router.host.run('%s link set %s up' %
                              (self._router.cmd_ip, scan_interface))
        start_time = time.time()
        privet_bss = None
        while time.time() - start_time < PRIVET_AP_STARTUP_TIMEOUT_SECONDS:
            bss_list = self._router.iw_runner.scan(scan_interface)
            for bss in bss_list or []:
                if helper.is_softap_ssid(bss.ssid):
                    privet_bss = bss
        if privet_bss is None:
            raise error.TestFail('Device did not start soft AP in time.')
        self._router.release_interface(scan_interface)

        # Get the netblock of the interface running the AP.
        dut_iw_runner = iw_runner.IwRunner(remote_host=host)
        devs = dut_iw_runner.list_interfaces(desired_if_type='AP')
        if not devs:
            raise error.TestFail('No AP devices on DUT?')
        ap_interface = interface.Interface(devs[0].if_name, host=host)
        ap_netblock = netblock.from_addr(ap_interface.ipv4_address_and_prefix)

        # Set up an AP on the router in the 5Ghz range with WPA2 security.
        wpa_config = xmlrpc_security_types.WPAConfig(
                psk=PASSPHRASE,
                wpa_mode=xmlrpc_security_types.WPAConfig.MODE_PURE_WPA2,
                wpa2_ciphers=[xmlrpc_security_types.WPAConfig.CIPHER_CCMP])
        router_conf = hostap_config.HostapConfig(
                frequency=5240, security_config=wpa_config,
                mode=hostap_config.HostapConfig.MODE_11N_PURE)
        self._router.hostap_configure(router_conf)

        # Connect the other interface on the router to the AP on the client
        # at a hardcoded IP address.
        self._router.configure_managed_station(
                privet_bss.ssid, privet_bss.frequency,
                ap_netblock.get_addr_in_block(200))
        station_interface = self._router.get_station_interface(instance=0)
        logging.debug('Set up station on %s', station_interface)
        self._router.ping(ping_runner.PingConfig(ap_netblock.addr, count=3))

        logging.info('Looking for privet webserver in mDNS records.')
        start_time = time.time()
        while time.time() - start_time < PRIVET_MDNS_RECORD_TIMEOUT_SECONDS:
            all_records = avahi_utils.avahi_browse(host=self._router.host)
            records = [record for record in all_records
                       if (record.interface == station_interface and
                           record.record_type == '_privet._tcp')]
            if records:
                break
            time.sleep(POLLING_PERIOD)
        if not records:
            raise error.TestFail('Did not find privet mDNS records in time.')
        if len(records) > 1:
            raise error.TestFail('Should not see multiple privet records.')
        privet_record = records[0]
        # TODO(wiley) pull the HTTPs port number out of the /info API.
        helper = privet_helper.PrivetdHelper(
                host=self._router.host,
                hostname=privet_record.address,
                http_port=int(privet_record.port))
        helper.ping_server()

        # Now configure the client with WiFi credentials.
        auth_token = helper.privet_auth()
        ssid = self._router.get_ssid()
        data = helper.setup_add_wifi_credentials(ssid, PASSPHRASE)
        helper.setup_start(data, auth_token)

        logging.info('Waiting for DUT to connect to router network.')
        start_time = time.time()
        # Wait for the DUT to take down the AP.
        while time.time() - start_time < PRIVET_CONNECT_TIMEOUT_SECONDS:
            if not dut_iw_runner.list_interfaces(desired_if_type='AP'):
                break
            time.sleep(POLLING_PERIOD)
        else:
            raise error.TestFail('Timeout waiting for DUT to take down AP.')

        # But we should be able to ping the client from the router's AP.
        while time.time() - start_time < PRIVET_CONNECT_TIMEOUT_SECONDS:
            if dut_iw_runner.list_interfaces(desired_if_type='managed'):
                break
            time.sleep(POLLING_PERIOD)
        else:
            raise error.TestFail('Timeout waiting for DUT managerd interface.')

        while time.time() - start_time < PRIVET_CONNECT_TIMEOUT_SECONDS:
            devs = dut_iw_runner.list_interfaces(desired_if_type='managed')
            if devs:
                managed_interface = interface.Interface(devs[0].if_name,
                                                        host=host)
                # Check if we have an IP yet.
                if managed_interface.ipv4_address_and_prefix:
                    break
            time.sleep(POLLING_PERIOD)
        else:
            raise error.TestFail('Timeout waiting for DUT managerd interface.')

        managed_netblock = netblock.from_addr(
                managed_interface.ipv4_address_and_prefix)
        while time.time() - start_time < PRIVET_CONNECT_TIMEOUT_SECONDS:
            PING_COUNT = 3
            result = self._router.ping(
                    ping_runner.PingConfig(managed_netblock.addr,
                                           ignore_result=True,
                                           count=PING_COUNT))
            if result.received == PING_COUNT:
                break
            time.sleep(POLLING_PERIOD)
        else:
            raise error.TestFail('Timeout before ping was successful.')

        # And buffet should think it is online as well.
        helper = privet_helper.PrivetdHelper(
                host=host, hostname=managed_netblock.addr,
                http_port=int(privet_record.port))
        helper.ping_server()
        if not helper.wifi_setup_was_successful(ssid, auth_token):
            raise error.TestFail('Device claims to be offline, but is online.')