| # Copyright (c) 2013 The Chromium OS Authors. All rights reserved. |
| # Use of this source code is governed by a BSD-style license that can be |
| # found in the LICENSE file. |
| |
| import os |
| import urlparse |
| |
| |
| import common |
| from autotest_lib.client.bin import utils |
| from autotest_lib.client.common_lib import error |
| from autotest_lib.client.common_lib.cros import virtual_ethernet_pair |
| from autotest_lib.client.cros import network, network_chroot |
| from autotest_lib.client.cros.cellular import test_endpoint |
| |
| class PseudoNetInterface(object): |
| """ |
| PseudoNetInterface provides a pseudo modem network interface. This |
| network interface is one end of a virtual Ethernet pair. The other end |
| of the virtual Ethernet pair is connected to a minijail that provides DHCP |
| and DNS services. Also in the minijail is a test endpoint (web server) |
| that is needed to pass portal detection and perform data transfer tests. |
| |
| """ |
| ARP_ANNOUNCE_CONF = '/proc/sys/net/ipv4/conf/all/arp_announce' |
| IFACE_NAME = 'pseudomodem0' |
| PEER_IFACE_NAME = IFACE_NAME + 'p' |
| IFACE_IP_BASE = '192.168.7' |
| IFACE_NETWORK_PREFIX = 24 |
| NETWORK_CHROOT_CONFIG = { |
| 'etc/passwd' : |
| 'root:x:0:0:root:/root:/bin/bash\n' |
| 'nobody:x:65534:65534:nobody:/dev/null:/bin/false\n', |
| 'etc/group' : |
| 'nobody::65534:\n'} |
| SHILL_PORTAL_DETECTION_SERVER = 'www.gstatic.com' |
| |
| def __init__(self): |
| self._arp_announce = 0 |
| peer_ip = self.IFACE_IP_BASE + '.1' |
| peer_interface_ip = peer_ip + '/' + str(self.IFACE_NETWORK_PREFIX) |
| self.vif = virtual_ethernet_pair.VirtualEthernetPair( |
| interface_name=self.IFACE_NAME, |
| peer_interface_name=self.PEER_IFACE_NAME, |
| interface_ip=None, |
| peer_interface_ip=peer_interface_ip, |
| ignore_shutdown_errors=True) |
| self.chroot = network_chroot.NetworkChroot(self.PEER_IFACE_NAME, |
| peer_ip, |
| self.IFACE_NETWORK_PREFIX) |
| self.chroot.add_config_templates(self.NETWORK_CHROOT_CONFIG) |
| self.chroot.add_startup_command( |
| 'iptables -I INPUT -p udp --dport 67 -j ACCEPT') |
| self.chroot.add_startup_command( |
| 'iptables -I INPUT -p tcp --dport 80 -j ACCEPT') |
| self._dnsmasq_command = self._GetDnsmasqCommand(peer_ip) |
| self.chroot.add_startup_command(self._dnsmasq_command) |
| self._test_endpoint_command = self._GetTestEndpointCommand() |
| self.chroot.add_startup_command(self._test_endpoint_command) |
| |
| @staticmethod |
| def _GetDnsmasqCommand(peer_ip): |
| dnsmasq_command = ( |
| 'dnsmasq ' |
| '--dhcp-leasefile=/tmp/dnsmasq.leases ' |
| '--dhcp-range=%s.2,%s.254 ' |
| '--no-resolv ' |
| '--no-hosts ' % |
| (PseudoNetInterface.IFACE_IP_BASE, |
| PseudoNetInterface.IFACE_IP_BASE)) |
| test_fetch_url_host = \ |
| urlparse.urlparse(network.FETCH_URL_PATTERN_FOR_TEST).netloc |
| dns_lookup_table = { |
| PseudoNetInterface.SHILL_PORTAL_DETECTION_SERVER: peer_ip, |
| test_fetch_url_host: peer_ip } |
| for host, ip in dns_lookup_table.iteritems(): |
| dnsmasq_command += '--address=/%s/%s ' % (host, ip) |
| return dnsmasq_command |
| |
| @staticmethod |
| def _GetTestEndpointCommand(): |
| test_endpoint_path = os.path.abspath(test_endpoint.__file__) |
| if test_endpoint_path.endswith('.pyc'): |
| test_endpoint_path = test_endpoint_path[:-1] |
| return test_endpoint_path |
| |
| def _ChrootRunCmdIgnoreErrors(self, cmd): |
| try: |
| self.chroot.run(cmd) |
| except error.CmdError: |
| pass |
| |
| def BringInterfaceUp(self): |
| """ |
| Brings up the pseudo modem network interface. |
| |
| """ |
| utils.run('sudo ip link set %s up' % self.IFACE_NAME) |
| |
| def BringInterfaceDown(self): |
| """ |
| Brings down the pseudo modem network interface. |
| |
| """ |
| utils.run('sudo ip link set %s down' % self.IFACE_NAME); |
| |
| def Setup(self): |
| """ |
| Sets up the virtual Ethernet pair and starts dnsmasq. |
| |
| """ |
| # Make sure ARP requests for the pseudo modem network addresses |
| # go out the pseudo modem network interface. |
| self._arp_announce = utils.system_output( |
| 'cat %s' % self.ARP_ANNOUNCE_CONF) |
| utils.run('echo 1 > %s' % self.ARP_ANNOUNCE_CONF) |
| |
| self.vif.setup() |
| self.BringInterfaceDown() |
| if not self.vif.is_healthy: |
| raise Exception('Could not initialize virtual ethernet pair') |
| self.chroot.startup() |
| |
| def Teardown(self): |
| """ |
| Stops dnsmasq and takes down the virtual Ethernet pair. |
| |
| """ |
| self._ChrootRunCmdIgnoreErrors(['/bin/bash', '-c', '"pkill dnsmasq"']) |
| self._ChrootRunCmdIgnoreErrors(['/bin/bash', '-c', |
| '"pkill -f test_endpoint"']) |
| self.vif.teardown() |
| self.chroot.shutdown() |
| utils.run('echo %s > %s' % (self._arp_announce, self.ARP_ANNOUNCE_CONF)) |
| |
| def Restart(self): |
| """ |
| Restarts the configuration. |
| |
| """ |
| self.Teardown() |
| self.Setup() |
| |