From 4e6e1bc5684f9ffac885d4aa3a523ecd3eb4057d Mon Sep 17 00:00:00 2001 From: Abtin Keshavarzian Date: Tue, 3 Jul 2018 22:14:55 -0700 Subject: [PATCH] [toranj] test-case for adding IPv6 addresses (#2853) This commit adds a `toranj` test for adding/removing IPv6 addresses on routers, FEDs, and SEDs (on network interface). The test then covers the following: - Verify "IPv6:AllAddresses" wpantund property. - Verify that prefixes are present in network data corresponding to added addresses (on all nodes). - Verify that a SED does register the address with its parents ("Thread:ChildTable:Addresses" wpantund property). - Verify that addresses/prefixes are retained by wpantund over NPC resets. - Verify that when an address is removed on network interface its corresponding prefix is also removed (on all nodes). --- tests/toranj/start.sh | 1 + tests/toranj/test-014-ip6-address-add.py | 186 +++++++++++++++++++++++ tests/toranj/wpan.py | 21 ++- 3 files changed, 205 insertions(+), 3 deletions(-) create mode 100644 tests/toranj/test-014-ip6-address-add.py diff --git a/tests/toranj/start.sh b/tests/toranj/start.sh index 83fed50b7..dfb517cbb 100755 --- a/tests/toranj/start.sh +++ b/tests/toranj/start.sh @@ -126,6 +126,7 @@ run test-010-on-mesh-prefix-config-gateway.py run test-011-child-table.py run test-012-multi-hop-traffic.py run test-013-off-mesh-route-traffic.py +run test-014-ip6-address-add.py run test-100-mcu-power-state.py run test-600-channel-manager-properties.py run test-601-channel-manager-channel-change.py diff --git a/tests/toranj/test-014-ip6-address-add.py b/tests/toranj/test-014-ip6-address-add.py new file mode 100644 index 000000000..dff054831 --- /dev/null +++ b/tests/toranj/test-014-ip6-address-add.py @@ -0,0 +1,186 @@ +#!/usr/bin/env python +# +# Copyright (c) 2018, The OpenThread Authors. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. Neither the name of the copyright holder nor the +# names of its contributors may be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. + +import time +import wpan +from wpan import verify + +#----------------------------------------------------------------------------------------------------------------------- +# Test description: Adding/Removing IPv6 addresses on routers and SEDs on network interface. +# +# Test topology: +# +# r1 ---- r2 +# | | +# | | +# fed1 sed2 +# +# IPv6 addresses are added as follows: +# - On `r2` add `IP6_ADDR_1` with prefix `IP6_PREFIX_1` +# - On `fed1` add `IP6_ADDR_2` with prefix `IP6_PREFIX_2` +# - On `sed2` add `IP6_ADDR_3` with prefix `IP6_PREFIX_3` +# +# The test then covers the following: +# - Verify that the addresses are present in "IPv6:AllAddresses" wpantund property on the corresponding node. +# - Verify that all prefixes are present in network data with correct configuration flags (on all nodes). +# - Verify that `sed2`'s address is present in `r2` (its parent) "Thread:ChildTable:Addresses". +# - Verify that addresses/prefixes are retained by wpantund over NCP reset. +# - Verify that when an IPv6 address is removed from network interface, its corresponding prefix is also removed from +# all nodes. + +test_name = __file__[:-3] if __file__.endswith('.py') else __file__ +print '-' * 120 +print 'Starting \'{}\''.format(test_name) + +#----------------------------------------------------------------------------------------------------------------------- +# Creating `wpan.Nodes` instances + +speedup = 4 +wpan.Node.set_time_speedup_factor(speedup) + +r1 = wpan.Node() +fed1 = wpan.Node() +r2 = wpan.Node() +sed2 = wpan.Node() + +all_nodes = [r1, fed1, r2, sed2] + +#----------------------------------------------------------------------------------------------------------------------- +# Init all nodes + +wpan.Node.init_all_nodes() +for node in all_nodes: + node.set(wpan.WPAN_OT_LOG_LEVEL, '0') + +#----------------------------------------------------------------------------------------------------------------------- +# Build network topology +# +# r1 ---- r2 +# | | +# | | +# fed1 sed2 + + +r1.whitelist_node(r2) +r2.whitelist_node(r1) + +r1.whitelist_node(fed1) +fed1.whitelist_node(r1) + +r2.whitelist_node(sed2) +sed2.whitelist_node(r2) + +r1.form("ip-addr") +r2.join_node(r1, wpan.JOIN_TYPE_ROUTER) + +fed1.join_node(r1, wpan.JOIN_TYPE_END_DEVICE) +sed2.join_node(r2, wpan.JOIN_TYPE_SLEEPY_END_DEVICE) + +sed2.set(wpan.WPAN_POLL_INTERVAL, '300') + +#----------------------------------------------------------------------------------------------------------------------- +# Test implementation + +IP6_PREFIX_1 = "fd00:c0de::" +IP6_PREFIX_2 = "fd00:deed::" +IP6_PREFIX_3 = "fd00:beef::" + +IP6_ADDR_1 = IP6_PREFIX_1 + "1" +IP6_ADDR_2 = IP6_PREFIX_2 + "2" +IP6_ADDR_3 = IP6_PREFIX_3 + "3" + +# On `r2` add `IP6_ADDR_1` with prefix `IP6_PREFIX_1` +# On `fed1` add `IP6_ADDR_2` with prefix `IP6_PREFIX_2` +# On `sed2` add `IP6_ADDR_3` with prefix `IP6_PREFIX_3` + +r2.add_ip6_address_on_interface(IP6_ADDR_1, prefix_len=64) +fed1.add_ip6_address_on_interface(IP6_ADDR_2, prefix_len=64) +sed2.add_ip6_address_on_interface(IP6_ADDR_3, prefix_len=64) + +for iter in range(2): + + time.sleep(1.5) + + # Verify that the addresses are present in "IPv6:AllAddresses" wpantund property on the corresponding node. + + verify(r2.find_ip6_address_with_prefix(IP6_PREFIX_1) == IP6_ADDR_1) + verify(fed1.find_ip6_address_with_prefix(IP6_PREFIX_2) == IP6_ADDR_2) + verify(sed2.find_ip6_address_with_prefix(IP6_PREFIX_3) == IP6_ADDR_3) + + # Verify that all prefixes are present in network data on all nodes (with correct flags). + + for prefix in [IP6_PREFIX_1, IP6_PREFIX_2, IP6_PREFIX_3]: + for node in all_nodes: + prefixes = wpan.parse_on_mesh_prefix_result(node.get(wpan.WPAN_THREAD_ON_MESH_PREFIXES)) + for p in prefixes: + if p.prefix == prefix: + verify(p.prefix_len == '64') + verify(p.is_stable()) + verify(p.is_on_mesh() == True) + verify(p.is_preferred() == True) + verify(p.is_def_route() == False) + verify(p.is_slaac() == False) + verify(p.is_dhcp() == False) + verify(p.is_config() == False) + verify(p.priority == "med") + break + else: # `for` loop finished without finding the prefix. + print 'Did not find prefix {} on node {}'.format(prefix, node) + exit(1) + + # Verify that IPv6 address of `sed2` is present on `r2` (its parent) "Thread:ChildTable:Addresses". + + addr_str = r2.get(wpan.WPAN_THREAD_CHILD_TABLE_ADDRESSES) + # search for index on address in the `addr_str` and ensure it is non-negative. + verify(addr_str.find(IP6_ADDR_3) >= 0) + + if iter == 0: + # Reset the nodes and verify that all addresses/prefixes are preserved. + fed1.reset() + sed2.reset() + + +# Remove address from `r2` + +r2.remove_ip6_address_on_interface(IP6_ADDR_1, prefix_len=64) + +time.sleep(1.5) + +# Verify that address is removed from r2 +verify(r2.find_ip6_address_with_prefix(IP6_PREFIX_1) == '') + +# Verify that the related prefix is also removed on all nodes +for node in all_nodes: + prefixes = wpan.parse_on_mesh_prefix_result(node.get(wpan.WPAN_THREAD_ON_MESH_PREFIXES)) + for p in prefixes: + verify(p.prefix != IP6_PREFIX_1) + +#----------------------------------------------------------------------------------------------------------------------- +# Test finished + +print '\'{}\' passed.'.format(test_name) diff --git a/tests/toranj/wpan.py b/tests/toranj/wpan.py index b1a540ec9..761ef5107 100644 --- a/tests/toranj/wpan.py +++ b/tests/toranj/wpan.py @@ -453,16 +453,31 @@ class Node(object): matched_addr = [addr for addr in all_addrs if addr.startswith(prefix)] return matched_addr[0] if len(matched_addr) >= 1 else '' - def add_ip6_address_on_interface(self, address): + def add_ip6_address_on_interface(self, address, prefix_len=64): """Adds an IPv6 interface on the network interface. - `address` should be string containing the IPv6 address + `address` should be string containing the IPv6 address. + `prefix_len` is an `int` specifying the prefix length. NOTE: this method uses linux `ip` command. """ - cmd = 'ip -6 addr add '+ address + ' dev ' + self.interface_name + cmd = 'ip -6 addr add '+ address + '/{} dev '.format(prefix_len) + self.interface_name if self._verbose: _log('$ Node{} \'{}\')'.format(self._index, cmd)) result = subprocess.check_output(cmd, shell=True, stderr=subprocess.STDOUT) + return result + + def remove_ip6_address_on_interface(self, address, prefix_len=64): + """Removes an IPv6 interface on the network interface. + `address` should be string containing the IPv6 address. + `prefix_len` is an `int` specifying the prefix length. + NOTE: this method uses linux `ip` command. + """ + cmd = 'ip -6 addr del '+ address + '/{} dev '.format(prefix_len) + self.interface_name + if self._verbose: + _log('$ Node{} \'{}\')'.format(self._index, cmd)) + + result = subprocess.check_output(cmd, shell=True, stderr=subprocess.STDOUT) + return result #------------------------------------------------------------------------------------------------------------------ # class methods