| # Copyright (c) 2012 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. |
| """ |
| VirtualEthernetPair provides methods for setting up and tearing down a virtual |
| ethernet interface for use in tests. You will probably need to be root on test |
| devices to use this class. The constructor allows you to specify your IP's to |
| assign to both ends of the pair, however, if you wish to leave the interface |
| unconfigured, simply pass None. You may also specify the subnet of your ip |
| addresses. Failing to do so leaves them with default in ifconfig. |
| |
| Example usage: |
| vif = virtual_ethernet_pair.VirtualEthernetPair(interface_name="master", |
| peer_interface_name="peer", |
| interface_ip="10.9.8.1/24", |
| peer_interface_ip=None) |
| vif.setup() |
| if not vif.is_healthy: |
| # bad things happened while creating the interface |
| # ... abort gracefully |
| |
| interface_name = vif.interface_name |
| peer_interface_name = vif.peer_interface_name |
| #... do things with your interface |
| |
| # You must call this if you want to leave the system in a good state. |
| vif.teardown() |
| |
| Alternatively: |
| |
| with virtual_ethernet_pair.VirtualEthernetPair(...) as vif: |
| if not vif.is_healthy: |
| # bad things happened while creating the interface |
| # ... abort gracefully |
| |
| interface_name = vif.interface_name |
| peer_interface_name = vif.peer_interface_name |
| #... do things with your interface |
| |
| """ |
| |
| import logging |
| |
| from autotest_lib.client.bin import utils |
| from autotest_lib.client.common_lib.cros.network import interface |
| |
| class VirtualEthernetPair(object): |
| """ Class for configuring virtual ethernet device pair. """ |
| |
| def __init__(self, |
| interface_name='veth_master', |
| peer_interface_name='veth_slave', |
| interface_ip='10.9.8.1/24', |
| peer_interface_ip='10.9.8.2/24', |
| interface_ipv6=None, |
| peer_interface_ipv6=None, |
| ignore_shutdown_errors=False, |
| host=None): |
| """ |
| Construct a object managing a virtual ethernet pair. One end of the |
| interface will be called |interface_name|, and the peer end |
| |peer_interface_name|. You may get the interface names later with |
| VirtualEthernetPair.get_[peer_]interface_name(). The ends of the |
| interface are manually configured with the given IPv4 address strings |
| (like "10.9.8.2/24"). You may skip the IP configuration by passing None |
| as the address for either interface. |
| """ |
| super(VirtualEthernetPair, self).__init__() |
| self._is_healthy = True |
| self._interface_name = interface_name |
| self._peer_interface_name = peer_interface_name |
| self._interface_ip = interface_ip |
| self._peer_interface_ip = peer_interface_ip |
| self._interface_ipv6 = interface_ipv6 |
| self._peer_interface_ipv6 = peer_interface_ipv6 |
| self._ignore_shutdown_errors = ignore_shutdown_errors |
| self._run = utils.run |
| self._host = host |
| if host is not None: |
| self._run = host.run |
| |
| |
| def setup(self): |
| """ |
| Installs a virtual ethernet interface and configures one side with an IP |
| address. First does some sanity checking and tries to remove an |
| existing interface by the same name, and logs messages on failures. |
| """ |
| self._is_healthy = False |
| if self._either_interface_exists(): |
| logging.warning('At least one test interface already existed.' |
| ' Attempting to remove.') |
| self._remove_test_interface() |
| if self._either_interface_exists(): |
| logging.error('Failed to remove unexpected test ' |
| 'interface. Aborting.') |
| return |
| |
| self._create_test_interface() |
| if not self._interface_exists(self._interface_name): |
| logging.error('Failed to create master test interface.') |
| return |
| |
| if not self._interface_exists(self._peer_interface_name): |
| logging.error('Failed to create peer test interface.') |
| return |
| # Unless you tell the firewall about the interface, you're not going to |
| # get any IP traffic through. Since this is basically a loopback |
| # device, just allow all traffic. |
| for name in (self._interface_name, self._peer_interface_name): |
| status = self._run('iptables -I INPUT -i %s -j ACCEPT' % name, |
| ignore_status=True) |
| if status.exit_status != 0: |
| logging.error('iptables rule addition failed for interface %s', |
| name) |
| self._is_healthy = True |
| |
| |
| def teardown(self): |
| """ |
| Removes the interface installed by VirtualEthernetPair.setup(), with |
| some simple sanity checks that print warnings when either the interface |
| isn't there or fails to be removed. |
| """ |
| for name in (self._interface_name, self._peer_interface_name): |
| self._run('iptables -D INPUT -i %s -j ACCEPT' % name, |
| ignore_status=True) |
| if not self._either_interface_exists(): |
| logging.warning('VirtualEthernetPair.teardown() called, ' |
| 'but no interface was found.') |
| return |
| |
| self._remove_test_interface() |
| if self._either_interface_exists(): |
| logging.error('Failed to destroy test interface.') |
| |
| |
| @property |
| def is_healthy(self): |
| """@return True if virtual ethernet pair is configured.""" |
| return self._is_healthy |
| |
| |
| @property |
| def interface_name(self): |
| """@return string name of the interface.""" |
| return self._interface_name |
| |
| |
| @property |
| def peer_interface_name(self): |
| """@return string name of the peer interface.""" |
| return self._peer_interface_name |
| |
| |
| @property |
| def interface_ip(self): |
| """@return string IPv4 address of the interface.""" |
| return interface.Interface(self.interface_name).ipv4_address |
| |
| |
| @property |
| def peer_interface_ip(self): |
| """@return string IPv4 address of the peer interface.""" |
| return interface.Interface(self.peer_interface_name).ipv4_address |
| |
| |
| @property |
| def interface_subnet_mask(self): |
| """@return string IPv4 subnet mask of the interface.""" |
| return interface.Interface(self.interface_name).ipv4_subnet_mask |
| |
| |
| @property |
| def interface_prefix(self): |
| """@return int IPv4 prefix length.""" |
| return interface.Interface(self.interface_name).ipv4_prefix |
| |
| |
| @property |
| def peer_interface_subnet_mask(self): |
| """@return string IPv4 subnet mask of the peer interface.""" |
| return interface.Interface(self.peer_interface_name).ipv4_subnet_mask |
| |
| |
| @property |
| def interface_mac(self): |
| """@return string MAC address of the interface.""" |
| return interface.Interface(self.interface_name).mac_address |
| |
| |
| @property |
| def peer_interface_mac(self): |
| """@return string MAC address of the peer interface.""" |
| return interface.Interface(self._peer_interface_name).mac_address |
| |
| |
| def __enter__(self): |
| self.setup() |
| return self |
| |
| |
| def __exit__(self, exc_type, exc_value, traceback): |
| self.teardown() |
| |
| |
| def _interface_exists(self, interface_name): |
| """ |
| Returns True iff we found an interface with name |interface_name|. |
| """ |
| return interface.Interface(interface_name, host=self._host).exists |
| |
| |
| def _either_interface_exists(self): |
| return (self._interface_exists(self._interface_name) or |
| self._interface_exists(self._peer_interface_name)) |
| |
| |
| def _remove_test_interface(self): |
| """ |
| Remove the virtual ethernet device installed by |
| _create_test_interface(). |
| """ |
| self._run('ifconfig %s down' % self._interface_name, |
| ignore_status=self._ignore_shutdown_errors) |
| self._run('ifconfig %s down' % self._peer_interface_name, |
| ignore_status=self._ignore_shutdown_errors) |
| self._run('ip link delete %s >/dev/null 2>&1' % self._interface_name, |
| ignore_status=self._ignore_shutdown_errors) |
| |
| # Under most normal circumstances a successful deletion of |
| # |_interface_name| should also remove |_peer_interface_name|, |
| # but if we elected to ignore failures above, that may not be |
| # the case. |
| self._run('ip link delete %s >/dev/null 2>&1' % |
| self._peer_interface_name, ignore_status=True) |
| |
| |
| def _create_test_interface(self): |
| """ |
| Set up a virtual ethernet device and configure the host side with a |
| fake IP address. |
| """ |
| self._run('ip link add name %s ' |
| 'type veth peer name %s >/dev/null 2>&1' % |
| (self._interface_name, self._peer_interface_name)) |
| self._run('ip link set %s up' % self._interface_name) |
| self._run('ip link set %s up' % self._peer_interface_name) |
| if self._interface_ip is not None: |
| self._run('ifconfig %s %s' % (self._interface_name, |
| self._interface_ip)) |
| if self._peer_interface_ip is not None: |
| self._run('ifconfig %s %s' % (self._peer_interface_name, |
| self._peer_interface_ip)) |
| if self._interface_ipv6 is not None: |
| self._run('ip -6 addr add %s dev %s' % (self._interface_ipv6, |
| self._interface_name)) |
| if self._peer_interface_ipv6 is not None: |
| self._run('ip -6 addr add %s dev %s' % (self._peer_interface_ipv6, |
| self._peer_interface_name)) |