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 5import os 6import urlparse 7 8 9import common 10from autotest_lib.client.bin import utils 11from autotest_lib.client.common_lib import error 12from autotest_lib.client.common_lib.cros import virtual_ethernet_pair 13from autotest_lib.client.cros import network, network_chroot 14from autotest_lib.client.cros.cellular import test_endpoint 15 16class 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:/dev/null:/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 ip link set %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 ip link set %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