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