Home | History | Annotate | Download | only in cellular
      1 # Copyright (c) 2013 The Chromium OS Authors. All rights reserved.
      2 # Use of this source code is governed by a BSD-style license that can be
      3 # found in the LICENSE file.
      4 
      5 import os
      6 import urlparse
      7 
      8 
      9 import common
     10 from autotest_lib.client.bin import utils
     11 from autotest_lib.client.common_lib import error
     12 from autotest_lib.client.common_lib.cros import virtual_ethernet_pair
     13 from autotest_lib.client.cros import network, network_chroot
     14 from autotest_lib.client.cros.cellular import test_endpoint
     15 
     16 class PseudoNetInterface(object):
     17     """
     18     PseudoNetInterface provides a pseudo modem network interface.  This
     19     network interface is one end of a virtual Ethernet pair.  The other end
     20     of the virtual Ethernet pair is connected to a minijail that provides DHCP
     21     and DNS services.  Also in the minijail is a test endpoint (web server)
     22     that is needed to pass portal detection and perform data transfer tests.
     23 
     24     """
     25     ARP_ANNOUNCE_CONF = '/proc/sys/net/ipv4/conf/all/arp_announce'
     26     IFACE_NAME = 'pseudomodem0'
     27     PEER_IFACE_NAME = IFACE_NAME + 'p'
     28     IFACE_IP_BASE = '192.168.7'
     29     IFACE_NETWORK_PREFIX = 24
     30     NETWORK_CHROOT_CONFIG = {
     31             'etc/passwd' :
     32                     'root:x:0:0:root:/root:/bin/bash\n'
     33                     'nobody:x:65534:65534:nobody:/var/empty:/bin/false\n',
     34             'etc/group' :
     35                     'nobody::65534:\n'}
     36     SHILL_PORTAL_DETECTION_SERVER = 'www.gstatic.com'
     37 
     38     def __init__(self):
     39         self._arp_announce = 0
     40         peer_ip = self.IFACE_IP_BASE + '.1'
     41         peer_interface_ip = peer_ip + '/' + str(self.IFACE_NETWORK_PREFIX)
     42         self.vif = virtual_ethernet_pair.VirtualEthernetPair(
     43                 interface_name=self.IFACE_NAME,
     44                 peer_interface_name=self.PEER_IFACE_NAME,
     45                 interface_ip=None,
     46                 peer_interface_ip=peer_interface_ip,
     47                 ignore_shutdown_errors=True)
     48         self.chroot = network_chroot.NetworkChroot(self.PEER_IFACE_NAME,
     49                                                    peer_ip,
     50                                                    self.IFACE_NETWORK_PREFIX)
     51         self.chroot.add_config_templates(self.NETWORK_CHROOT_CONFIG)
     52         self.chroot.add_startup_command(
     53                 'iptables -I INPUT -p udp --dport 67 -j ACCEPT')
     54         self.chroot.add_startup_command(
     55                 'iptables -I INPUT -p tcp --dport 80 -j ACCEPT')
     56         self._dnsmasq_command = self._GetDnsmasqCommand(peer_ip)
     57         self.chroot.add_startup_command(self._dnsmasq_command)
     58         self._test_endpoint_command = self._GetTestEndpointCommand()
     59         self.chroot.add_startup_command(self._test_endpoint_command)
     60 
     61     @staticmethod
     62     def _GetDnsmasqCommand(peer_ip):
     63         dnsmasq_command = (
     64                 'dnsmasq '
     65                 '--dhcp-leasefile=/tmp/dnsmasq.leases '
     66                 '--dhcp-range=%s.2,%s.254 '
     67                 '--no-resolv '
     68                 '--no-hosts ' %
     69                 (PseudoNetInterface.IFACE_IP_BASE,
     70                  PseudoNetInterface.IFACE_IP_BASE))
     71         test_fetch_url_host = \
     72                 urlparse.urlparse(network.FETCH_URL_PATTERN_FOR_TEST).netloc
     73         dns_lookup_table = {
     74                 PseudoNetInterface.SHILL_PORTAL_DETECTION_SERVER: peer_ip,
     75                 test_fetch_url_host: peer_ip }
     76         for host, ip in dns_lookup_table.iteritems():
     77             dnsmasq_command += '--address=/%s/%s ' % (host, ip)
     78         return dnsmasq_command
     79 
     80     @staticmethod
     81     def _GetTestEndpointCommand():
     82         test_endpoint_path = os.path.abspath(test_endpoint.__file__)
     83         if test_endpoint_path.endswith('.pyc'):
     84             test_endpoint_path = test_endpoint_path[:-1]
     85         return test_endpoint_path
     86 
     87     def _ChrootRunCmdIgnoreErrors(self, cmd):
     88         try:
     89             self.chroot.run(cmd)
     90         except error.CmdError:
     91             pass
     92 
     93     def BringInterfaceUp(self):
     94         """
     95         Brings up the pseudo modem network interface.
     96 
     97         """
     98         utils.run('sudo ifconfig %s up' % self.IFACE_NAME)
     99 
    100     def BringInterfaceDown(self):
    101         """
    102         Brings down the pseudo modem network interface.
    103 
    104         """
    105         utils.run('sudo ifconfig %s down' % self.IFACE_NAME);
    106 
    107     def Setup(self):
    108         """
    109         Sets up the virtual Ethernet pair and starts dnsmasq.
    110 
    111         """
    112         # Make sure ARP requests for the pseudo modem network addresses
    113         # go out the pseudo modem network interface.
    114         self._arp_announce = utils.system_output(
    115                 'cat %s' % self.ARP_ANNOUNCE_CONF)
    116         utils.run('echo 1 > %s' % self.ARP_ANNOUNCE_CONF)
    117 
    118         self.vif.setup()
    119         self.BringInterfaceDown()
    120         if not self.vif.is_healthy:
    121             raise Exception('Could not initialize virtual ethernet pair')
    122         self.chroot.startup()
    123 
    124     def Teardown(self):
    125         """
    126         Stops dnsmasq and takes down the virtual Ethernet pair.
    127 
    128         """
    129         self._ChrootRunCmdIgnoreErrors(['/bin/bash', '-c', '"pkill dnsmasq"'])
    130         self._ChrootRunCmdIgnoreErrors(['/bin/bash', '-c',
    131                                         '"pkill -f test_endpoint"'])
    132         self.vif.teardown()
    133         self.chroot.shutdown()
    134         utils.run('echo %s > %s' % (self._arp_announce, self.ARP_ANNOUNCE_CONF))
    135 
    136     def Restart(self):
    137         """
    138         Restarts the configuration.
    139 
    140         """
    141         self.Teardown()
    142         self.Setup()
    143 
    144