1#!/usr/bin/env python3 2# 3# Copyright (c) 2023, 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 29from cli import verify 30from cli import verify_within 31import cli 32import time 33 34# ----------------------------------------------------------------------------------------------------------------------- 35# Test description: Validate SLAAC module, addition/deprecation/removal of SLAAC addresses. 36# 37# Network topology 38# 39# r1 ---- r2 40# 41 42test_name = __file__[:-3] if __file__.endswith('.py') else __file__ 43print('-' * 120) 44print('Starting \'{}\''.format(test_name)) 45 46# ----------------------------------------------------------------------------------------------------------------------- 47# Creating `cli.Node` instances 48 49speedup = 40 50cli.Node.set_time_speedup_factor(speedup) 51 52r1 = cli.Node() 53r2 = cli.Node() 54c2 = cli.Node() 55 56# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 57 58 59def verify_slaac_address_for_prefix(prefix, preferred, nodes=[r1, r2], origin='slaac'): 60 # Verify that both nodes have SLAAC address based on `prefix`. 61 for node in nodes: 62 ip_addrs = node.get_ip_addrs('-v') 63 matched_addrs = [addr for addr in ip_addrs if addr.startswith(prefix)] 64 verify(len(matched_addrs) == 1) 65 addr = matched_addrs[0] 66 verify('origin:' + origin in addr) 67 verify('plen:64' in addr) 68 verify('valid:1' in addr) 69 if (preferred): 70 verify('preferred:1' in addr) 71 else: 72 verify('preferred:0' in addr) 73 74 75def verify_no_slaac_address_for_prefix(prefix): 76 for node in [r1, r2]: 77 ip_addrs = node.get_ip_addrs('-v') 78 matched_addrs = [addr for addr in ip_addrs if addr.startswith(prefix)] 79 verify(len(matched_addrs) == 0) 80 81 82def check_num_netdata_prefixes(num_prefixes): 83 for node in [r1, r2]: 84 verify(len(node.get_netdata_prefixes()) == num_prefixes) 85 86 87def check_num_netdata_routes(num_routes): 88 for node in [r1, r2]: 89 verify(len(node.get_netdata_routes()) == num_routes) 90 91 92# ----------------------------------------------------------------------------------------------------------------------- 93# Form topology 94 95r1.allowlist_node(r2) 96r2.allowlist_node(r1) 97r2.allowlist_node(c2) 98c2.allowlist_node(r2) 99 100r1.form('slaac') 101r2.join(r1) 102c2.join(r2, cli.JOIN_TYPE_END_DEVICE) 103 104verify(r1.get_state() == 'leader') 105verify(r2.get_state() == 'router') 106verify(c2.get_state() == 'child') 107 108# Ensure `c2` is attached to `r2` as its parent 109verify(int(c2.get_parent_info()['Rloc'], 16) == int(r2.get_rloc16(), 16)) 110 111# ----------------------------------------------------------------------------------------------------------------------- 112# Test Implementation 113 114# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 115# Add first prefix `fd00:11::/64` and check that SLAAC 116# addressed are added based on it on all nodes. 117 118r1.add_prefix("fd00:11:0:0::/64", "paos") 119r1.register_netdata() 120verify_within(check_num_netdata_prefixes, 100, 1) 121 122verify_slaac_address_for_prefix('fd00:11:0:0:', preferred=True) 123 124# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 125# Add `fd00:22::/64` and check its related SLAAC addresses. 126 127r2.add_prefix("fd00:22:0:0::/64", "paos") 128r2.register_netdata() 129 130verify_within(check_num_netdata_prefixes, 100, 2) 131 132verify_slaac_address_for_prefix('fd00:11:0:0:', preferred=True) 133verify_slaac_address_for_prefix('fd00:22:0:0:', preferred=True) 134 135# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 136# Add prefix `fd00:11::/64` now from `r2`, and since this was 137# previously added by `r1`, there should not change to SLAAC 138# addresses. 139 140r2.add_prefix("fd00:11:0:0::/64", "paos") 141r2.register_netdata() 142 143verify_within(check_num_netdata_prefixes, 100, 3) 144 145verify_slaac_address_for_prefix('fd00:11:0:0:', preferred=True) 146verify_slaac_address_for_prefix('fd00:22:0:0:', preferred=True) 147 148# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 149# Remove prefix `fd00:11::/64` from `r2`, and since this is 150# also added by `r1`, there should not change to SLAAC addresses. 151 152r2.remove_prefix("fd00:11:0:0::/64") 153r2.register_netdata() 154 155verify_within(check_num_netdata_prefixes, 100, 2) 156 157verify_slaac_address_for_prefix('fd00:11:0:0:', preferred=True) 158verify_slaac_address_for_prefix('fd00:22:0:0:', preferred=True) 159 160# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 161# Remove all prefixes and validate that all SLAAC addresses 162# start deprecating. 163 164r1.remove_prefix("fd00:11:0:0::/64") 165r1.register_netdata() 166r2.remove_prefix("fd00:22:0:0::/64") 167r2.register_netdata() 168 169verify_within(check_num_netdata_prefixes, 100, 0) 170 171verify_slaac_address_for_prefix('fd00:11:0:0:', preferred=False) 172verify_slaac_address_for_prefix('fd00:22:0:0:', preferred=False) 173 174# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 175# Validate that the deprecating address can still be used 176# and proper route look up happens when it is used. 177 178# Add an off-mesh route `::/0` (default route) on `r1`. 179 180r1.add_route('::/0', 's', 'med') 181r1.register_netdata() 182 183verify_within(check_num_netdata_routes, 10, 1) 184 185# Now we ping from `r2` an off-mesh address, `r2` should pick one of 186# its deprecating addresses as the source address for this message. 187# Since the SLAAC module tracks the associated Domain ID from the 188# original Prefix TLV, the route lookup for this message should 189# successfully match to `r1` off-mesh route. We validate that the 190# message is indeed delivered to `r1` and passed to host. 191 192r1_counter = r1.get_br_counter_unicast_outbound_packets() 193 194r2.ping('fd00:ee::1', verify_success=False) 195 196verify(r1_counter + 1 == r1.get_br_counter_unicast_outbound_packets()) 197 198# Repeat the same process but now ping from `c2` which is an 199# MTD child. `c2` would send the message to its parent `r2` which 200# should be able to successfully do route look up for the deprecating 201# prefix. The message should be delivered to `r1` and passed to host. 202 203r1_counter = r1.get_br_counter_unicast_outbound_packets() 204 205c2.ping('fd00:ff::1', verify_success=False) 206 207verify(r1_counter + 1 == r1.get_br_counter_unicast_outbound_packets()) 208 209# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 210# Before the address is expired, re-add ``fd00:11::/64` prefix 211# and check that the address is added back as preferred. 212 213r2.add_prefix("fd00:11:0:0::/64", "paors") 214r2.register_netdata() 215 216verify_within(check_num_netdata_prefixes, 100, 1) 217 218verify_slaac_address_for_prefix('fd00:11:0:0:', preferred=True) 219verify_slaac_address_for_prefix('fd00:22:0:0:', preferred=False) 220 221# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 222# Make sure prefix 2 is expired and removed 223 224 225def check_prefix_2_addr_expire(): 226 verify_no_slaac_address_for_prefix('fd00:22:0:0:') 227 228 229verify_within(check_prefix_2_addr_expire, 100) 230 231verify_slaac_address_for_prefix('fd00:11:0:0:', preferred=True) 232 233# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 234# Manually add an address for prefix `fd00:33::/64` on `r1` 235# and `r2` before adding a related on-mesh prefix. Validate that 236# the SLAAC module does not add addresses. 237 238r1.add_ip_addr('fd00:33::1') 239r2.add_ip_addr('fd00:33::2') 240 241r1.add_prefix("fd00:33:0:0::/64", "paos") 242r1.register_netdata() 243 244verify_within(check_num_netdata_prefixes, 100, 2) 245 246verify_slaac_address_for_prefix('fd00:11:0:0:', preferred=True) 247verify_slaac_address_for_prefix('fd00:33:0:0:', preferred=True, origin='manual') 248 249# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 250# Remove the manually added address on `r2` which should trigger 251# SLAAC module to add its own SLAAC address based on the prefix. 252 253r2.remove_ip_addr('fd00:33::2') 254time.sleep(0.1) 255 256verify_slaac_address_for_prefix('fd00:11:0:0:', preferred=True) 257verify_slaac_address_for_prefix('fd00:33:0:0:', preferred=True, nodes=[r2], origin='slaac') 258verify_slaac_address_for_prefix('fd00:33:0:0:', preferred=True, nodes=[r1], origin='manual') 259 260# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 261# Remove the prefix and make sure the manually added address 262# is not impacted, but the one added by SLAAC on `r2` should 263# be deprecating. 264 265r1.remove_prefix("fd00:33:0:0::/64") 266r1.register_netdata() 267 268verify_within(check_num_netdata_prefixes, 100, 1) 269 270verify_slaac_address_for_prefix('fd00:11:0:0:', preferred=True) 271verify_slaac_address_for_prefix('fd00:33:0:0:', preferred=False, nodes=[r2], origin='slaac') 272verify_slaac_address_for_prefix('fd00:33:0:0:', preferred=True, nodes=[r1], origin='manual') 273 274r1.remove_ip_addr('fd00:33::1') 275 276# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 277# Toranj config sets the max number of SLAAC addresses to 4 278# Check that the max limit is applied by adding 5 prefixes. 279 280r1.add_prefix("fd00:22:0:0::/64", "paos") 281r1.add_prefix("fd00:33:0:0::/64", "paos") 282r1.add_prefix("fd00:44:0:0::/64", "paos") 283r1.register_netdata() 284 285verify_within(check_num_netdata_prefixes, 100, 4) 286 287verify_slaac_address_for_prefix('fd00:11:0:0:', preferred=True) 288verify_slaac_address_for_prefix('fd00:22:0:0:', preferred=True) 289verify_slaac_address_for_prefix('fd00:33:0:0:', preferred=True) 290verify_slaac_address_for_prefix('fd00:44:0:0:', preferred=True) 291 292r1.add_prefix("fd00:55:0:0::/64", "paos") 293r1.register_netdata() 294 295verify_within(check_num_netdata_prefixes, 100, 5) 296 297verify_slaac_address_for_prefix('fd00:11:0:0:', preferred=True) 298verify_slaac_address_for_prefix('fd00:22:0:0:', preferred=True) 299verify_slaac_address_for_prefix('fd00:33:0:0:', preferred=True) 300verify_slaac_address_for_prefix('fd00:44:0:0:', preferred=True) 301verify_no_slaac_address_for_prefix('fd00:55:0:0::/64') 302 303# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 304# Remove one of the prefixes which should deprecate an 305# existing SLAAC address which should then be evicted to 306# add address for the new `fd00:55::/64` prefix. 307 308r1.remove_prefix("fd00:44:0:0::/64") 309r1.register_netdata() 310 311verify_within(check_num_netdata_prefixes, 100, 4) 312 313verify_slaac_address_for_prefix('fd00:11:0:0:', preferred=True) 314verify_slaac_address_for_prefix('fd00:22:0:0:', preferred=True) 315verify_slaac_address_for_prefix('fd00:33:0:0:', preferred=True) 316verify_slaac_address_for_prefix('fd00:55:0:0:', preferred=True) 317verify_no_slaac_address_for_prefix('fd00:44:0:0::/64') 318 319# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 320# Validate oldest entry is evicted when multiple addresses are deprecating 321# and new prefix is added. 322 323r1.remove_prefix("fd00:33:0:0::/64") 324r1.register_netdata() 325time.sleep(0.05) 326r1.remove_prefix("fd00:22:0:0::/64") 327r1.register_netdata() 328time.sleep(0.05) 329r1.remove_prefix("fd00:55:0:0::/64") 330r1.register_netdata() 331time.sleep(0.05) 332 333verify_within(check_num_netdata_prefixes, 100, 1) 334 335verify_slaac_address_for_prefix('fd00:11:0:0:', preferred=True) 336verify_slaac_address_for_prefix('fd00:22:0:0:', preferred=False) 337verify_slaac_address_for_prefix('fd00:33:0:0:', preferred=False) 338verify_slaac_address_for_prefix('fd00:55:0:0:', preferred=False) 339 340# Now add a new prefix `fd00:66::/64`, the `fd00:33::` address 341# which was removed first should be evicted to make room for 342# the SLAAC address for the new prefix. 343 344r1.add_prefix("fd00:66:0:0::/64", "paos") 345r1.register_netdata() 346 347verify_within(check_num_netdata_prefixes, 100, 2) 348 349verify_slaac_address_for_prefix('fd00:11:0:0:', preferred=True) 350verify_slaac_address_for_prefix('fd00:22:0:0:', preferred=False) 351verify_no_slaac_address_for_prefix('fd00:33:0:0:') 352verify_slaac_address_for_prefix('fd00:55:0:0:', preferred=False) 353verify_slaac_address_for_prefix('fd00:66:0:0:', preferred=True) 354 355# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 356# Validate re-adding of a prefix before its address is deprecated. 357 358r1.add_prefix("fd00:22:0:0::/64", "paos") 359r1.register_netdata() 360 361verify_within(check_num_netdata_prefixes, 100, 3) 362 363verify_slaac_address_for_prefix('fd00:11:0:0:', preferred=True) 364verify_slaac_address_for_prefix('fd00:22:0:0:', preferred=True) 365verify_no_slaac_address_for_prefix('fd00:33:0:0:') 366verify_slaac_address_for_prefix('fd00:55:0:0:', preferred=False) 367verify_slaac_address_for_prefix('fd00:66:0:0:', preferred=True) 368 369 370def check_prefix_5_addr_expire(): 371 verify_no_slaac_address_for_prefix('fd00:55:0:0:') 372 373 374verify_within(check_prefix_5_addr_expire, 100) 375 376# ----------------------------------------------------------------------------------------------------------------------- 377# Test finished 378 379cli.Node.finalize_all_nodes() 380 381print('\'{}\' passed.'.format(test_name)) 382