| # Copyright (c) 2013 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. |
| |
| import logging |
| import time |
| |
| from autotest_lib.client.common_lib import error |
| from autotest_lib.client.common_lib import utils |
| from autotest_lib.client.cros import dhcp_test_base |
| from autotest_lib.client.cros import radvd_server |
| from autotest_lib.client.cros.networking import shill_proxy |
| |
| class network_Ipv6SimpleNegotiation(dhcp_test_base.DhcpTestBase): |
| """ |
| The test subclass that implements IPv6 negotiation. This test |
| starts an IPv6 router, then performs a series of tests on the |
| IPv6 addresses and IPv6 DNS addresses that the DUT should have |
| gained. |
| """ |
| |
| def _get_ip6_addresses(self): |
| """Gets the list of client IPv6 addresses. |
| |
| Retrieve IPv6 addresses associated with the "client side" of the |
| pseudo-interface pair. Returns a dict keyed by the IPv6 address, |
| with the values being the array of attribute strings that follow |
| int the "ip addr show" output. For example, a line containing: |
| |
| inet6 fe80::ae16:2dff:fe01:0203/64 scope link |
| |
| will turn into a dict key: |
| |
| 'fe80::ae16:2dff:fe01:0203/64': [ 'scope', 'link' ] |
| |
| """ |
| addr_output = utils.system_output( |
| "ip -6 addr show dev %s" % self.ethernet_pair.peer_interface_name) |
| addresses = {} |
| for line in addr_output.splitlines(): |
| parts = line.lstrip().split() |
| if parts[0] != 'inet6' or 'deprecated' in parts: |
| continue |
| addresses[parts[1]] = parts[2:] |
| return addresses |
| |
| |
| def _get_link_address(self): |
| """Get the client MAC address. |
| |
| Retrieve the MAC address associated with the "client side" of the |
| pseudo-interface pair. For example, the "ip link show" output: |
| |
| link/ether 01:02:03:04:05:05 brd ff:ff:ff:ff:ff:ff |
| |
| will cause a return of "01:02:03:04:05:05" |
| |
| """ |
| addr_output = utils.system_output( |
| 'ip link show %s' % self.ethernet_pair.peer_interface_name) |
| for line in addr_output.splitlines(): |
| parts = line.lstrip().split(' ') |
| if parts[0] == 'link/ether': |
| return parts[1] |
| |
| |
| def _get_ipconfig_properties(self): |
| for ipconfig in self.get_interface_ipconfig_objects( |
| self.ethernet_pair.peer_interface_name): |
| ipconfig_properties = shill_proxy.ShillProxy.dbus2primitive( |
| ipconfig.GetProperties(utf8_strings=True)) |
| if 'Method' not in ipconfig_properties: |
| continue |
| |
| if ipconfig_properties['Method'] != 'ipv6': |
| continue |
| |
| return ipconfig_properties |
| else: |
| raise error.TestError('Found no IPv6 IPConfig entries') |
| |
| |
| def verify_ipv6_addresses(self): |
| """Verify IPv6 configuration. |
| |
| Perform various tests to validate the IPv6 addresses acquired by |
| the client. |
| |
| """ |
| addresses = self._get_ip6_addresses() |
| logging.info('Got addresses %r', addresses) |
| global_addresses = [key for key in addresses |
| if 'global' in addresses[key]] |
| |
| if len(global_addresses) != 2: |
| raise error.TestError('Expected 2 global address but got %d' % |
| len(global_addresses)) |
| |
| prefix = radvd_server.RADVD_DEFAULT_PREFIX |
| prefix = prefix[:prefix.index('::')] |
| for address in global_addresses: |
| if not address.startswith(prefix): |
| raise error.TestError('Global address %s does not start with ' |
| 'expected prefix %s' % |
| address, prefix) |
| |
| # One globally scoped address should be based on the last 3 octets |
| # of the MAC adddress, while the other should not. For example, |
| # for MAC address "01:02:03:04:05:06", we should see an address |
| # that ends with "4:506/64" (the "/64" is the default radvd suffix). |
| link_parts = [int(b, 16) for b in self._get_link_address().split(':')] |
| address_suffix = '%x:%x%s' % (link_parts[3], |
| (link_parts[4] << 8) | link_parts[5], |
| radvd_server.RADVD_DEFAULT_SUFFIX) |
| mac_related_addresses = [addr for addr in global_addresses |
| if addr.endswith(address_suffix)] |
| if len(mac_related_addresses) != 1: |
| raise error.TestError('Expected 1 mac-related global address but ' |
| 'got %d' % len(mac_related_addresses)) |
| mac_related_address = mac_related_addresses[0] |
| |
| local_address_count = len(addresses) - len(global_addresses) |
| if local_address_count <= 0: |
| raise error.TestError('Expected at least 1 non-global address but ' |
| 'got %d' % local_address_count) |
| |
| temporary_address = [addr for addr in global_addresses |
| if addr != mac_related_address][0] |
| self.verify_ipconfig_contains(temporary_address) |
| |
| |
| def verify_ipconfig_contains(self, address_and_prefix): |
| """Verify that shill has an IPConfig entry with the specified address. |
| |
| @param address_and_prefix string with address/prefix to search for. |
| |
| """ |
| address, prefix_str = address_and_prefix.split('/') |
| prefix = int(prefix_str) |
| ipconfig_properties = self._get_ipconfig_properties() |
| |
| for property, value in (('Address', address), ('Prefixlen', prefix)): |
| if property not in ipconfig_properties: |
| raise error.TestError('IPv6 IPConfig entry does not ' |
| 'contain property %s' % property) |
| if ipconfig_properties[property] != value: |
| raise error.TestError('IPv6 IPConfig property %s does not ' |
| 'contain the expected value %s; ' |
| 'instead it is %s' % |
| (property, value, |
| ipconfig_properties[property])) |
| |
| |
| def verify_ipconfig_name_servers(self, name_servers): |
| """Verify that shill has an IPConfig entry with the specified name |
| servers. |
| |
| @param name_servers list of expected name servers. |
| |
| """ |
| ipconfig_properties = self._get_ipconfig_properties() |
| |
| if ipconfig_properties['NameServers'] != name_servers: |
| raise error.TestError('IPv6 name servers mismatched: ' |
| 'expected %r actual %r' % |
| name_servers, |
| ipconfig_properties['NameServers']) |
| |
| |
| def test_body(self): |
| """The main body for this test.""" |
| server = radvd_server.RadvdServer(self.ethernet_pair.interface_name) |
| server.start_server() |
| |
| try: |
| # Wait for IPv6 negotiation to complete. |
| time.sleep(radvd_server.RADVD_DEFAULT_MAX_ADV_INTERVAL) |
| |
| # In this time, we should have also acquired an IPv6 address. |
| self.verify_ipv6_addresses() |
| self.verify_ipconfig_name_servers( |
| radvd_server.RADVD_DEFAULT_RDNSS_SERVERS.split(' ')) |
| finally: |
| server.stop_server() |