1#!/usr/bin/env python3 2# 3# Copyright (c) 2019, The OpenThread Authors. 4# All rights reserved. 5# 6# Redistribution and use in source and binary forms, with or without 7# modification, are permitted provided that the following conditions are met: 8# 1. Redistributions of source code must retain the above copyright 9# notice, this list of conditions and the following disclaimer. 10# 2. Redistributions in binary form must reproduce the above copyright 11# notice, this list of conditions and the following disclaimer in the 12# documentation and/or other materials provided with the distribution. 13# 3. Neither the name of the copyright holder nor the 14# names of its contributors may be used to endorse or promote products 15# derived from this software without specific prior written permission. 16# 17# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 21# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 25# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 27# POSSIBILITY OF SUCH DAMAGE. 28 29import time 30import wpan 31from wpan import verify 32 33# ----------------------------------------------------------------------------------------------------------------------- 34# Test description: SLAAC address 35# 36# This test covers the behavior of SLAAC module in OpenThread (NCP): 37# 38# - Verify that adding prefix (with SLAAC flag) causes a corresponding SLAAC IPv6 address to be added. 39# - Verify that removing the prefix, would remove the SLAAC address. 40# - Verify behavior when same prefix is added/removed on multiple nodes (with or without SLAAC flag). 41# - Check behavior when a user-added address with the same prefix already exists. 42# - Check behavior when a user-added address with same prefix is removed (SLAAC module should add a SLAAC address). 43# - Ensure removal of prefix does not remove user-added address with same prefix. 44# - Ensure disabling SLAAC module removes previously added SLAAC addresses, and re-enabling it adds them back. 45# - Check behavior when prefix is added while SLAAC module is disabled and then enabled later. 46 47test_name = __file__[:-3] if __file__.endswith('.py') else __file__ 48print('-' * 120) 49print('Starting \'{}\''.format(test_name)) 50 51# ----------------------------------------------------------------------------------------------------------------------- 52# Utility functions 53 54 55def verify_address(node_list, prefix): 56 """ 57 This function verifies that all nodes in the `node_list` contain an IPv6 address with the given `prefix`. 58 """ 59 for node in node_list: 60 all_addrs = wpan.parse_list(node.get(wpan.WPAN_IP6_ALL_ADDRESSES)) 61 verify(any([addr.startswith(prefix[:-1]) for addr in all_addrs])) 62 63 64def verify_no_address(node_list, prefix): 65 """ 66 This function verifies that none of nodes in the `node_list` contain an IPv6 address with the given `prefix`. 67 """ 68 for node in node_list: 69 all_addrs = wpan.parse_list(node.get(wpan.WPAN_IP6_ALL_ADDRESSES)) 70 verify(all([not addr.startswith(prefix[:-1]) for addr in all_addrs])) 71 72 73def verify_prefix( 74 node_list, 75 prefix, 76 prefix_len=64, 77 stable=True, 78 priority='med', 79 on_mesh=False, 80 slaac=False, 81 dhcp=False, 82 configure=False, 83 default_route=False, 84 preferred=False, 85): 86 """ 87 This function verifies that the `prefix` is present on all nodes in the `node_list`. 88 """ 89 for node in node_list: 90 prefixes = wpan.parse_on_mesh_prefix_result(node.get(wpan.WPAN_THREAD_ON_MESH_PREFIXES)) 91 for p in prefixes: 92 if p.prefix == prefix: 93 if (int(p.prefix_len) == prefix_len and p.is_stable() == stable and p.is_on_mesh() == on_mesh and 94 p.is_def_route() == default_route and p.is_slaac() == slaac and p.is_dhcp() == dhcp and 95 p.is_config() == configure and p.is_preferred() == preferred and p.priority == priority): 96 break 97 else: 98 raise wpan.VerifyError("Did not find prefix {} on node {}".format(prefix, node)) 99 100 101def verify_no_prefix(node_list, prefix): 102 """ 103 This function verifies that the `prefix` is NOT present on any node in the `node_list`. 104 """ 105 for node in node_list: 106 prefixes = wpan.parse_on_mesh_prefix_result(node.get(wpan.WPAN_THREAD_ON_MESH_PREFIXES)) 107 for p in prefixes: 108 verify(not p.prefix == prefix) 109 110 111# ----------------------------------------------------------------------------------------------------------------------- 112# Creating `wpan.Nodes` instances 113 114speedup = 4 115wpan.Node.set_time_speedup_factor(speedup) 116 117r1 = wpan.Node() 118r2 = wpan.Node() 119c2 = wpan.Node() 120 121all_nodes = [r1, r2, c2] 122 123# ----------------------------------------------------------------------------------------------------------------------- 124# Init all nodes 125 126wpan.Node.init_all_nodes() 127 128# ----------------------------------------------------------------------------------------------------------------------- 129# Build network topology 130 131r1.form('slaac-ncp') 132 133r1.allowlist_node(r2) 134r2.allowlist_node(r1) 135 136r2.join_node(r1, node_type=wpan.JOIN_TYPE_ROUTER) 137 138c2.allowlist_node(r2) 139r2.allowlist_node(c2) 140 141c2.join_node(r2, node_type=wpan.JOIN_TYPE_END_DEVICE) 142 143# ----------------------------------------------------------------------------------------------------------------------- 144# Test implementation 145 146# This test covers the SLAAC address management by NCP. So before starting the test we ensure that SLAAC module 147# on NCP is enabled on all nodes, and disable it on wpantund. 148 149for node in all_nodes: 150 verify(node.get(wpan.WPAN_OT_SLAAC_ENABLED) == 'true') 151 node.set("Daemon:IPv6:AutoAddSLAACAddress", 'false') 152 verify(node.get("Daemon:IPv6:AutoAddSLAACAddress") == 'false') 153 154WAIT_INTERVAL = 5 155 156PREFIX = 'fd00:1234::' 157 158# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 159# Add prefix and check all nodes get the prefix and add a corresponding 160# SLAAC address. 161 162r1.add_prefix(PREFIX, stable=True, on_mesh=True, slaac=True) 163 164 165def check_prefix_and_slaac_address_are_added(): 166 verify_prefix(all_nodes, PREFIX, stable=True, on_mesh=True, slaac=True) 167 verify_address(all_nodes, PREFIX) 168 169 170wpan.verify_within(check_prefix_and_slaac_address_are_added, WAIT_INTERVAL) 171 172# Save the assigned SLAAC addresses. 173slaac_addrs = [node.find_ip6_address_with_prefix(PREFIX) for node in all_nodes] 174 175# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 176# Check recovery after resetting r1 and c1 (same SLAAC address to be added) 177 178r1.reset() 179wpan.verify_within(check_prefix_and_slaac_address_are_added, WAIT_INTERVAL) 180verify([node.find_ip6_address_with_prefix(PREFIX) for node in all_nodes] == slaac_addrs) 181 182# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 183# Remove the prefix on r1 and verify that the address and prefix are 184# removed on all nodes. 185 186r1.remove_prefix(PREFIX) 187 188 189def check_prefix_and_slaac_address_are_removed(): 190 verify_no_prefix(all_nodes, PREFIX) 191 verify_no_address(all_nodes, PREFIX) 192 193 194wpan.verify_within(check_prefix_and_slaac_address_are_removed, WAIT_INTERVAL) 195 196# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 197# Add the prefix on both r1 and r2, then remove from r1 and ensure SLAAC 198# addresses are assigned on all nodes. 199 200# Add prefix on r2 201r2.add_prefix(PREFIX, stable=True, on_mesh=True, slaac=True) 202wpan.verify_within(check_prefix_and_slaac_address_are_added, WAIT_INTERVAL) 203verify([node.find_ip6_address_with_prefix(PREFIX) for node in all_nodes] == slaac_addrs) 204 205# Add same prefix on r1 and verify prefix and addresses stay as before 206r1.add_prefix(PREFIX, stable=True, on_mesh=True, slaac=True) 207wpan.verify_within(check_prefix_and_slaac_address_are_added, WAIT_INTERVAL) 208verify([node.find_ip6_address_with_prefix(PREFIX) for node in all_nodes] == slaac_addrs) 209 210# Remove on r1, addresses and prefixes should stay as before (r2 still has 211# the same prefix) 212r1.remove_prefix(PREFIX) 213time.sleep(0.5) 214wpan.verify_within(check_prefix_and_slaac_address_are_added, WAIT_INTERVAL) 215verify([node.find_ip6_address_with_prefix(PREFIX) for node in all_nodes] == slaac_addrs) 216 217# Remove the prefix on r2 and verify that the address and prefix are now 218# removed on all nodes. 219r2.remove_prefix(PREFIX) 220wpan.verify_within(check_prefix_and_slaac_address_are_removed, WAIT_INTERVAL) 221 222# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 223# Add the prefix on r1 (without SLAAC flag) and r2 (with SLAAC flag) 224 225# Add prefix on r1 without SLAAC flag, and on r2 with SLAAC flag 226r1.add_prefix(PREFIX, stable=True, on_mesh=True, slaac=False) 227r2.add_prefix(PREFIX, stable=True, on_mesh=True, slaac=True) 228wpan.verify_within(check_prefix_and_slaac_address_are_added, WAIT_INTERVAL) 229verify([node.find_ip6_address_with_prefix(PREFIX) for node in all_nodes] == slaac_addrs) 230 231# Now remove the prefix on r2 and verify that SLAAC address is removed 232r2.remove_prefix(PREFIX) 233 234 235def check_slaac_address_is_removed(): 236 verify_no_address(all_nodes, PREFIX) 237 238 239wpan.verify_within(check_slaac_address_is_removed, WAIT_INTERVAL) 240 241r1.remove_prefix(PREFIX) 242wpan.verify_within(check_prefix_and_slaac_address_are_removed, WAIT_INTERVAL) 243 244# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 245# Check behavior when a user-added address with same prefix already exists. 246 247IP_ADDRESS = PREFIX + "1234" 248 249# Explicitly add an address with the prefix on r1 250r1.add_ip6_address_on_interface(IP_ADDRESS) 251 252# Afterwards, add the prefix on r2 (with SLAAC flag) 253r2.add_prefix(PREFIX, stable=True, on_mesh=True, slaac=True) 254wpan.verify_within(check_prefix_and_slaac_address_are_added, WAIT_INTERVAL) 255 256# Verify that on r1 we do see the user-added address 257r1_addrs = wpan.parse_list(r1.get(wpan.WPAN_IP6_ALL_ADDRESSES)) 258verify(IP_ADDRESS in r1_addrs) 259 260# Also verify that adding the prefix did not add a SLAAC address for same 261# prefix on r1 262r1_addrs.remove(IP_ADDRESS) 263verify(all([not addr.startswith(PREFIX[:-1]) for addr in r1_addrs])) 264 265# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 266# Check behavior when a user-added address with same prefix is removed 267# (SLAAC module should add a SLAAC address). 268 269r1.remove_ip6_address_on_interface(IP_ADDRESS) 270wpan.verify_within(check_prefix_and_slaac_address_are_added, WAIT_INTERVAL) 271verify([node.find_ip6_address_with_prefix(PREFIX) for node in all_nodes] == slaac_addrs) 272 273# Re-add the address 274r1.add_ip6_address_on_interface(IP_ADDRESS) 275 276# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 277# Ensure removal of prefix does not remove user-added address with same prefix. 278 279r2.remove_prefix(PREFIX) 280 281 282def check_ip6_addresses(): 283 # Verify that SLAAC addresses are removed on r2 and c2 284 verify_no_address([r2, c2], PREFIX) 285 # And that user-added address matching the prefix is not removed on r1 286 r1_addrs = wpan.parse_list(r1.get(wpan.WPAN_IP6_ALL_ADDRESSES)) 287 verify(IP_ADDRESS in r1_addrs) 288 289 290wpan.verify_within(check_ip6_addresses, WAIT_INTERVAL) 291 292r1.remove_ip6_address_on_interface(IP_ADDRESS) 293 294# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 295# Ensure disabling SLAAC module removes previously added SLAAC addresses, 296# and re-enabling it adds them back. 297 298r1.add_prefix(PREFIX, stable=True, on_mesh=True, slaac=True) 299wpan.verify_within(check_prefix_and_slaac_address_are_added, WAIT_INTERVAL) 300verify([node.find_ip6_address_with_prefix(PREFIX) for node in all_nodes] == slaac_addrs) 301 302for node in all_nodes: 303 node.set(wpan.WPAN_OT_SLAAC_ENABLED, 'false') 304wpan.verify_within(check_slaac_address_is_removed, WAIT_INTERVAL) 305 306# Re-enable SLAAC support on NCP and verify addresses are re-added back. 307for node in all_nodes: 308 node.set(wpan.WPAN_OT_SLAAC_ENABLED, 'true') 309wpan.verify_within(check_prefix_and_slaac_address_are_added, WAIT_INTERVAL) 310verify([node.find_ip6_address_with_prefix(PREFIX) for node in all_nodes] == slaac_addrs) 311 312r1.remove_prefix(PREFIX) 313wpan.verify_within(check_prefix_and_slaac_address_are_removed, WAIT_INTERVAL) 314 315# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 316# Check behavior when prefix is added while SLAAC is disabled and then 317# enabled later. 318 319for node in all_nodes: 320 node.set(wpan.WPAN_OT_SLAAC_ENABLED, 'false') 321 322# Add prefix and verify that there is no address added 323r1.add_prefix(PREFIX, stable=True, on_mesh=True, slaac=True) 324wpan.verify_within(check_slaac_address_is_removed, WAIT_INTERVAL) 325 326for node in all_nodes: 327 node.set(wpan.WPAN_OT_SLAAC_ENABLED, 'true') 328wpan.verify_within(check_prefix_and_slaac_address_are_added, WAIT_INTERVAL) 329verify([node.find_ip6_address_with_prefix(PREFIX) for node in all_nodes] == slaac_addrs) 330 331# ----------------------------------------------------------------------------------------------------------------------- 332# Test finished 333 334wpan.Node.finalize_all_nodes() 335 336print('\'{}\' passed.'.format(test_name)) 337