1# Copyright (c) 2012 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""" 5VirtualEthernetPair provides methods for setting up and tearing down a virtual 6ethernet interface for use in tests. You will probably need to be root on test 7devices to use this class. The constructor allows you to specify your IP's to 8assign to both ends of the pair, however, if you wish to leave the interface 9unconfigured, simply pass None. You may also specify the subnet of your ip 10addresses. Failing to do so leaves them with default in ifconfig. 11 12Example usage: 13vif = virtual_ethernet_pair.VirtualEthernetPair(interface_name="master", 14 peer_interface_name="peer", 15 interface_ip="10.9.8.1/24", 16 peer_interface_ip=None) 17vif.setup() 18if not vif.is_healthy: 19 # bad things happened while creating the interface 20 # ... abort gracefully 21 22interface_name = vif.interface_name 23peer_interface_name = vif.peer_interface_name 24#... do things with your interface 25 26# You must call this if you want to leave the system in a good state. 27vif.teardown() 28 29Alternatively: 30 31with virtual_ethernet_pair.VirtualEthernetPair(...) as vif: 32 if not vif.is_healthy: 33 # bad things happened while creating the interface 34 # ... abort gracefully 35 36 interface_name = vif.interface_name 37 peer_interface_name = vif.peer_interface_name 38 #... do things with your interface 39 40""" 41 42import logging 43 44from autotest_lib.client.bin import utils 45from autotest_lib.client.common_lib.cros.network import interface 46 47class VirtualEthernetPair(object): 48 """ Class for configuring virtual ethernet device pair. """ 49 50 def __init__(self, 51 interface_name='veth_master', 52 peer_interface_name='veth_slave', 53 interface_ip='10.9.8.1/24', 54 peer_interface_ip='10.9.8.2/24', 55 interface_ipv6=None, 56 peer_interface_ipv6=None, 57 ignore_shutdown_errors=False, 58 host=None): 59 """ 60 Construct a object managing a virtual ethernet pair. One end of the 61 interface will be called |interface_name|, and the peer end 62 |peer_interface_name|. You may get the interface names later with 63 VirtualEthernetPair.get_[peer_]interface_name(). The ends of the 64 interface are manually configured with the given IPv4 address strings 65 (like "10.9.8.2/24"). You may skip the IP configuration by passing None 66 as the address for either interface. 67 """ 68 super(VirtualEthernetPair, self).__init__() 69 self._is_healthy = True 70 self._interface_name = interface_name 71 self._peer_interface_name = peer_interface_name 72 self._interface_ip = interface_ip 73 self._peer_interface_ip = peer_interface_ip 74 self._interface_ipv6 = interface_ipv6 75 self._peer_interface_ipv6 = peer_interface_ipv6 76 self._ignore_shutdown_errors = ignore_shutdown_errors 77 self._run = utils.run 78 self._host = host 79 if host is not None: 80 self._run = host.run 81 82 83 def setup(self): 84 """ 85 Installs a virtual ethernet interface and configures one side with an IP 86 address. First does some sanity checking and tries to remove an 87 existing interface by the same name, and logs messages on failures. 88 """ 89 self._is_healthy = False 90 if self._either_interface_exists(): 91 logging.warning('At least one test interface already existed.' 92 ' Attempting to remove.') 93 self._remove_test_interface() 94 if self._either_interface_exists(): 95 logging.error('Failed to remove unexpected test ' 96 'interface. Aborting.') 97 return 98 99 self._create_test_interface() 100 if not self._interface_exists(self._interface_name): 101 logging.error('Failed to create master test interface.') 102 return 103 104 if not self._interface_exists(self._peer_interface_name): 105 logging.error('Failed to create peer test interface.') 106 return 107 # Unless you tell the firewall about the interface, you're not going to 108 # get any IP traffic through. Since this is basically a loopback 109 # device, just allow all traffic. 110 for name in (self._interface_name, self._peer_interface_name): 111 status = self._run('iptables -I INPUT -i %s -j ACCEPT' % name, 112 ignore_status=True) 113 if status.exit_status != 0: 114 logging.error('iptables rule addition failed for interface %s', 115 name) 116 self._is_healthy = True 117 118 119 def teardown(self): 120 """ 121 Removes the interface installed by VirtualEthernetPair.setup(), with 122 some simple sanity checks that print warnings when either the interface 123 isn't there or fails to be removed. 124 """ 125 for name in (self._interface_name, self._peer_interface_name): 126 self._run('iptables -D INPUT -i %s -j ACCEPT' % name, 127 ignore_status=True) 128 if not self._either_interface_exists(): 129 logging.warning('VirtualEthernetPair.teardown() called, ' 130 'but no interface was found.') 131 return 132 133 self._remove_test_interface() 134 if self._either_interface_exists(): 135 logging.error('Failed to destroy test interface.') 136 137 138 @property 139 def is_healthy(self): 140 """@return True if virtual ethernet pair is configured.""" 141 return self._is_healthy 142 143 144 @property 145 def interface_name(self): 146 """@return string name of the interface.""" 147 return self._interface_name 148 149 150 @property 151 def peer_interface_name(self): 152 """@return string name of the peer interface.""" 153 return self._peer_interface_name 154 155 156 @property 157 def interface_ip(self): 158 """@return string IPv4 address of the interface.""" 159 return interface.Interface(self.interface_name).ipv4_address 160 161 162 @property 163 def peer_interface_ip(self): 164 """@return string IPv4 address of the peer interface.""" 165 return interface.Interface(self.peer_interface_name).ipv4_address 166 167 168 @property 169 def interface_subnet_mask(self): 170 """@return string IPv4 subnet mask of the interface.""" 171 return interface.Interface(self.interface_name).ipv4_subnet_mask 172 173 174 @property 175 def interface_prefix(self): 176 """@return int IPv4 prefix length.""" 177 return interface.Interface(self.interface_name).ipv4_prefix 178 179 180 @property 181 def peer_interface_subnet_mask(self): 182 """@return string IPv4 subnet mask of the peer interface.""" 183 return interface.Interface(self.peer_interface_name).ipv4_subnet_mask 184 185 186 @property 187 def interface_mac(self): 188 """@return string MAC address of the interface.""" 189 return interface.Interface(self.interface_name).mac_address 190 191 192 @property 193 def peer_interface_mac(self): 194 """@return string MAC address of the peer interface.""" 195 return interface.Interface(self._peer_interface_name).mac_address 196 197 198 def __enter__(self): 199 self.setup() 200 return self 201 202 203 def __exit__(self, exc_type, exc_value, traceback): 204 self.teardown() 205 206 207 def _interface_exists(self, interface_name): 208 """ 209 Returns True iff we found an interface with name |interface_name|. 210 """ 211 return interface.Interface(interface_name, host=self._host).exists 212 213 214 def _either_interface_exists(self): 215 return (self._interface_exists(self._interface_name) or 216 self._interface_exists(self._peer_interface_name)) 217 218 219 def _remove_test_interface(self): 220 """ 221 Remove the virtual ethernet device installed by 222 _create_test_interface(). 223 """ 224 self._run('ifconfig %s down' % self._interface_name, 225 ignore_status=self._ignore_shutdown_errors) 226 self._run('ifconfig %s down' % self._peer_interface_name, 227 ignore_status=self._ignore_shutdown_errors) 228 self._run('ip link delete %s >/dev/null 2>&1' % self._interface_name, 229 ignore_status=self._ignore_shutdown_errors) 230 231 # Under most normal circumstances a successful deletion of 232 # |_interface_name| should also remove |_peer_interface_name|, 233 # but if we elected to ignore failures above, that may not be 234 # the case. 235 self._run('ip link delete %s >/dev/null 2>&1' % 236 self._peer_interface_name, ignore_status=True) 237 238 239 def _create_test_interface(self): 240 """ 241 Set up a virtual ethernet device and configure the host side with a 242 fake IP address. 243 """ 244 self._run('ip link add name %s ' 245 'type veth peer name %s >/dev/null 2>&1' % 246 (self._interface_name, self._peer_interface_name)) 247 self._run('ip link set %s up' % self._interface_name) 248 self._run('ip link set %s up' % self._peer_interface_name) 249 if self._interface_ip is not None: 250 self._run('ifconfig %s %s' % (self._interface_name, 251 self._interface_ip)) 252 if self._peer_interface_ip is not None: 253 self._run('ifconfig %s %s' % (self._peer_interface_name, 254 self._peer_interface_ip)) 255 if self._interface_ipv6 is not None: 256 self._run('ip -6 addr add %s dev %s' % (self._interface_ipv6, 257 self._interface_name)) 258 if self._peer_interface_ipv6 is not None: 259 self._run('ip -6 addr add %s dev %s' % (self._peer_interface_ipv6, 260 self._peer_interface_name))