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: Network Data Context ID assignment and reuse delay. 36# 37# Network topology 38# 39# r1 ---- r2 ---- r3 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() 54r3 = cli.Node() 55 56# ----------------------------------------------------------------------------------------------------------------------- 57# Form topology 58 59r1.allowlist_node(r2) 60 61r2.allowlist_node(r1) 62r2.allowlist_node(r3) 63 64r3.allowlist_node(r2) 65 66r1.form('netdata') 67r2.join(r1) 68r3.join(r1) 69 70verify(r1.get_state() == 'leader') 71verify(r2.get_state() == 'router') 72verify(r3.get_state() == 'router') 73 74# ----------------------------------------------------------------------------------------------------------------------- 75# Test Implementation 76 77# Check the default reuse delay on `r1` to be 5 minutes. 78 79verify(int(r1.get_context_reuse_delay()) == 5 * 60) 80 81# Change the reuse delay on `r1` (leader) to 5 seconds. 82 83r1.set_context_reuse_delay(5) 84verify(int(r1.get_context_reuse_delay()) == 5) 85 86# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 87# Add an on-mesh prefix from each router `r1`, r2`, and `r3`. 88# Validate that netdata is updated and Context IDs are assigned. 89 90r1.add_prefix('fd00:1::/64', 'poas') 91r1.register_netdata() 92 93r2.add_prefix('fd00:2::/64', 'poas') 94r2.register_netdata() 95 96r3.add_prefix('fd00:3::/64', 'poas') 97r3.add_route('fd00:beef::/64', 's') 98r3.register_netdata() 99 100 101def check_netdata_1(): 102 global netdata 103 netdata = r1.get_netdata() 104 verify(len(netdata['prefixes']) == 3) 105 verify(len(netdata['routes']) == 1) 106 107 108verify_within(check_netdata_1, 5) 109 110contexts = netdata['contexts'] 111verify(len(contexts) == 3) 112verify(any([context.startswith('fd00:1:0:0::/64') for context in contexts])) 113verify(any([context.startswith('fd00:2:0:0::/64') for context in contexts])) 114verify(any([context.startswith('fd00:3:0:0::/64') for context in contexts])) 115for context in contexts: 116 verify(context.endswith('c')) 117 118# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 119# Remove prefix on `r3`. Validate that Context compress flag 120# is cleared for the removed prefix. 121 122r3.remove_prefix('fd00:3::/64') 123r3.register_netdata() 124 125 126def check_netdata_2(): 127 global netdata, versions 128 versions = r1.get_netdata_versions() 129 netdata = r1.get_netdata() 130 verify(len(netdata['prefixes']) == 2) 131 verify(len(netdata['routes']) == 1) 132 133 134verify_within(check_netdata_2, 5) 135 136contexts = netdata['contexts'] 137verify(len(contexts) == 3) 138verify(any([context.startswith('fd00:1:0:0::/64') for context in contexts])) 139verify(any([context.startswith('fd00:2:0:0::/64') for context in contexts])) 140verify(any([context.startswith('fd00:3:0:0::/64') for context in contexts])) 141for context in contexts: 142 if context.startswith('fd00:1:0:0::/64') or context.startswith('fd00:2:0:0::/64'): 143 verify(context.endswith('c')) 144 else: 145 verify(context.endswith('-')) 146 147# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 148# Validate that the prefix context is removed within reuse delay 149# time (which is set to 5 seconds on leader). 150 151 152def check_netdata_3(): 153 global netdata 154 netdata = r1.get_netdata() 155 verify(len(netdata['prefixes']) == 2) 156 verify(len(netdata['routes']) == 1) 157 verify(len(netdata['contexts']) == 2) 158 159 160verify_within(check_netdata_3, 5) 161old_versions = versions 162versions = r1.get_netdata_versions() 163verify(versions != old_versions) 164 165# Make sure netdata does not change afterwards 166 167time.sleep(5 * 3 / speedup) 168 169verify(versions == r1.get_netdata_versions()) 170netdata = r1.get_netdata() 171verify(len(netdata['prefixes']) == 2) 172verify(len(netdata['routes']) == 1) 173verify(len(netdata['contexts']) == 2) 174 175# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 176# Add two new on-mesh prefixes from `r3`. Validate that they get 177# assigned Context IDs. 178 179r3.add_prefix('fd00:4::/64', 'poas') 180r3.register_netdata() 181 182r3.add_prefix('fd00:3::/64', 'poas') 183r3.register_netdata() 184 185 186def check_netdata_4(): 187 global netdata 188 netdata = r1.get_netdata() 189 verify(len(netdata['prefixes']) == 4) 190 verify(len(netdata['routes']) == 1) 191 verify(len(netdata['contexts']) == 4) 192 193 194verify_within(check_netdata_4, 5) 195 196contexts = netdata['contexts'] 197verify(len(contexts) == 4) 198verify(any([context.startswith('fd00:1:0:0::/64') for context in contexts])) 199verify(any([context.startswith('fd00:2:0:0::/64') for context in contexts])) 200verify(any([context.startswith('fd00:3:0:0::/64') for context in contexts])) 201verify(any([context.startswith('fd00:4:0:0::/64') for context in contexts])) 202for context in contexts: 203 verify(context.endswith('c')) 204 205# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 206# Remove prefixes on `r1` and `r2` and re-add them back quickly both 207# from `r2`. Validate that they get the same context IDs as before. 208 209for context in contexts: 210 if context.startswith('fd00:1:0:0::/64'): 211 cid1 = int(context.split()[1]) 212 elif context.startswith('fd00:2:0:0::/64'): 213 cid2 = int(context.split()[1]) 214 215r1.remove_prefix('fd00:1:0:0::/64') 216r1.register_netdata() 217 218r2.remove_prefix('fd00:2:0:0::/64') 219r2.register_netdata() 220 221 222def check_netdata_5(): 223 global netdata 224 netdata = r1.get_netdata() 225 verify(len(netdata['prefixes']) == 2) 226 verify(len(netdata['routes']) == 1) 227 228 229verify_within(check_netdata_5, 5) 230 231contexts = netdata['contexts'] 232verify(len(contexts) == 4) 233verify(any([context.startswith('fd00:1:0:0::/64') for context in contexts])) 234verify(any([context.startswith('fd00:2:0:0::/64') for context in contexts])) 235verify(any([context.startswith('fd00:3:0:0::/64') for context in contexts])) 236verify(any([context.startswith('fd00:4:0:0::/64') for context in contexts])) 237for context in contexts: 238 if context.startswith('fd00:1:0:0::/64') or context.startswith('fd00:2:0:0::/64'): 239 verify(context.endswith('-')) 240 else: 241 verify(context.endswith('c')) 242 243# Re-add both prefixes (now from `r2`) before CID remove delay time 244# is expired. 245 246r2.add_prefix('fd00:1::/64', 'poas') 247r2.add_prefix('fd00:2::/64', 'poas') 248r2.register_netdata() 249 250 251def check_netdata_6(): 252 global netdata 253 netdata = r1.get_netdata() 254 verify(len(netdata['prefixes']) == 4) 255 verify(len(netdata['routes']) == 1) 256 257 258verify_within(check_netdata_6, 5) 259 260contexts = netdata['contexts'] 261verify(len(contexts) == 4) 262verify(any([context.startswith('fd00:1:0:0::/64') for context in contexts])) 263verify(any([context.startswith('fd00:2:0:0::/64') for context in contexts])) 264verify(any([context.startswith('fd00:3:0:0::/64') for context in contexts])) 265verify(any([context.startswith('fd00:4:0:0::/64') for context in contexts])) 266for context in contexts: 267 verify(context.endswith('c')) 268 if context.startswith('fd00:1:0:0::/64'): 269 verify(int(context.split()[1]) == cid1) 270 elif context.startswith('fd00:2:0:0::/64'): 271 verify(int(context.split()[1]) == cid2) 272 273# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 274# Remove two prefixes on `r3`. Add one back as an external route from 275# `r1`. Also add a new prefix from `r1`. Validate that the context IDs 276# for the removed on-mesh prefixes are removed after reuse delay time 277# and that the new prefix gets a different Context ID. 278 279# Remember the CID used. 280for context in contexts: 281 if context.startswith('fd00:3:0:0::/64'): 282 cid3 = int(context.split()[1]) 283 elif context.startswith('fd00:4:0:0::/64'): 284 cid4 = int(context.split()[1]) 285 286r3.remove_prefix('fd00:3:0:0::/64') 287r3.remove_prefix('fd00:4:0:0::/64') 288r3.register_netdata() 289 290 291def check_netdata_7(): 292 global netdata 293 netdata = r1.get_netdata() 294 verify(len(netdata['prefixes']) == 2) 295 verify(len(netdata['routes']) == 1) 296 297 298verify_within(check_netdata_7, 5) 299 300contexts = netdata['contexts'] 301verify(len(contexts) == 4) 302verify(any([context.startswith('fd00:1:0:0::/64') for context in contexts])) 303verify(any([context.startswith('fd00:2:0:0::/64') for context in contexts])) 304verify(any([context.startswith('fd00:3:0:0::/64') for context in contexts])) 305verify(any([context.startswith('fd00:4:0:0::/64') for context in contexts])) 306for context in contexts: 307 if context.startswith('fd00:3:0:0::/64') or context.startswith('fd00:4:0:0::/64'): 308 verify(context.endswith('-')) 309 else: 310 verify(context.endswith('c')) 311 312# Add first one removed as route and add a new prefix. 313 314r1.add_route('fd00:3:0:0::/64', 's') 315r1.add_prefix('fd00:5:0:0::/64', 'poas') 316r1.register_netdata() 317 318 319def check_netdata_8(): 320 global netdata 321 netdata = r1.get_netdata() 322 verify(len(netdata['prefixes']) == 3) 323 verify(len(netdata['routes']) == 2) 324 verify(len(netdata['contexts']) == 3) 325 326 327verify_within(check_netdata_8, 5) 328 329contexts = netdata['contexts'] 330verify(len(contexts) == 3) 331verify(any([context.startswith('fd00:1:0:0::/64') for context in contexts])) 332verify(any([context.startswith('fd00:2:0:0::/64') for context in contexts])) 333verify(any([context.startswith('fd00:5:0:0::/64') for context in contexts])) 334 335for context in contexts: 336 verify(context.endswith('c')) 337 if context.startswith('fd00:5:0:0::/64'): 338 verify(not int(context.split()[1]) in [cid3, cid4]) 339 340# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 341# Remove a prefix on `r2`, wait one second and remove a second 342# prefix. Validate the Context IDs for both are removed. 343 344r2.remove_prefix('fd00:1::/64') 345r2.register_netdata() 346 347time.sleep(1 / speedup) 348 349r2.remove_prefix('fd00:2::/64') 350r2.register_netdata() 351 352 353def check_netdata_9(): 354 global netdata 355 netdata = r1.get_netdata() 356 verify(len(netdata['prefixes']) == 1) 357 verify(len(netdata['routes']) == 2) 358 verify(len(netdata['contexts']) == 1) 359 360 361verify_within(check_netdata_9, 5) 362 363contexts = netdata['contexts'] 364verify(len(contexts) == 1) 365verify(contexts[0].startswith('fd00:5:0:0::/64')) 366verify(contexts[0].endswith('c')) 367 368# ----------------------------------------------------------------------------------------------------------------------- 369# Test finished 370 371cli.Node.finalize_all_nodes() 372 373print('\'{}\' passed.'.format(test_name)) 374