1#!/usr/bin/env python3 2# 3# Copyright (c) 2021, 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# 29 30import ipaddress 31import unittest 32 33import command 34import thread_cert 35 36# Test description: 37# This test verifies network data publisher behavior with DNS/SRP service entries and on-mesh prefix and external 38# route entries. 39# 40# Topology: 41# 42# 1 leader, 5 routers and 5 end-devices all connected. 43# 44 45LEADER = 1 46ROUTER1 = 2 47ROUTER2 = 3 48ROUTER3 = 4 49ROUTER4 = 5 50ROUTER5 = 6 51END_DEV1 = 7 52END_DEV2 = 8 53END_DEV3 = 9 54END_DEV4 = 10 55END_DEV5 = 11 56 57WAIT_TIME = 55 58 59ON_MESH_PREFIX = 'fd00:1234::/64' 60ON_MESH_FLAGS = 'paso' 61 62EXTERNAL_ROUTE = 'fd00:abce::/64' 63EXTERNAL_FLAGS = 's' 64 65ANYCAST_SEQ_NUM = 4 66 67DNSSRP_ADDRESS = 'fd00::cdef' 68DNSSRP_PORT = 49152 69 70# The desired number of entries (based on related config). 71DESIRED_NUM_DNSSRP_ANYCAST = 8 72DESIRED_NUM_DNSSRP_UNCIAST = 2 73DESIRED_NUM_ON_MESH_PREFIX = 3 74DESIRED_NUM_EXTERNAL_ROUTE = 10 75 76THREAD_ENTERPRISE_NUMBER = 44970 77ANYCAST_SERVICE_NUM = 0x5c 78UNICAST_SERVICE_NUM = 0x5d 79 80 81class NetDataPublisher(thread_cert.TestCase): 82 USE_MESSAGE_FACTORY = False 83 SUPPORT_NCP = False 84 85 TOPOLOGY = { 86 LEADER: { 87 'name': 'LEADER', 88 'mode': 'rdn', 89 }, 90 ROUTER1: { 91 'name': 'ROUTER1', 92 'mode': 'rdn', 93 }, 94 ROUTER2: { 95 'name': 'ROUTER2', 96 'mode': 'rdn', 97 }, 98 ROUTER3: { 99 'name': 'ROUTER3', 100 'mode': 'rdn', 101 }, 102 ROUTER4: { 103 'name': 'ROUTER4', 104 'mode': 'rdn', 105 }, 106 ROUTER5: { 107 'name': 'ROUTER5', 108 'mode': 'rdn', 109 }, 110 END_DEV1: { 111 'name': 'END_DEV1', 112 'mode': 'rn', 113 }, 114 END_DEV2: { 115 'name': 'END_DEV2', 116 'mode': 'rn', 117 }, 118 END_DEV3: { 119 'name': 'END_DEV3', 120 'mode': 'rn', 121 }, 122 END_DEV4: { 123 'name': 'END_DEV4', 124 'mode': 'rn', 125 }, 126 END_DEV5: { 127 'name': 'END_DEV5', 128 'mode': 'rn', 129 }, 130 } 131 132 def verify_anycast_service(self, service): 133 # Verify the data in a single anycast `service` from `get_services()` 134 # Example of `service`: ['44970', '5c04', '', 's', 'bc00'] 135 self.assertEqual(int(service[0]), THREAD_ENTERPRISE_NUMBER) 136 # Check service data 137 service_data = bytes.fromhex(service[1]) 138 self.assertTrue(len(service_data) >= 2) 139 self.assertEqual(service_data[0], ANYCAST_SERVICE_NUM) 140 self.assertEqual(service_data[1], int(ANYCAST_SEQ_NUM)) 141 # Verify that it stable 142 self.assertEqual(service[3], 's') 143 144 def verify_anycast_services(self, services): 145 # Verify a list of anycast `services` from `get_services()` 146 for service in services: 147 self.verify_anycast_service(service) 148 149 def verify_unicast_service(self, service): 150 # Verify the data in a single unicast `service` from `get_services()` 151 # Example of `service`: ['44970', '5d', 'fd000db800000000c6b0e5ee81f940e8223d', 's', '7000'] 152 self.assertEqual(int(service[0]), THREAD_ENTERPRISE_NUMBER) 153 # Check service data 154 service_data = bytes.fromhex(service[1]) 155 self.assertTrue(len(service_data) >= 1) 156 self.assertEqual(service_data[0], UNICAST_SERVICE_NUM) 157 # Verify that it stable 158 self.assertEqual(service[3], 's') 159 160 def verify_unicast_services(self, services): 161 # Verify a list of unicast `services` from `get_services()` 162 for service in services: 163 self.verify_unicast_service(service) 164 165 def check_num_of_prefixes(self, prefixes, num_low, num_med, num_high): 166 # Check and validate the prefix entries in network data (from 167 # `prefixes`) based on number of published prefix entries at 168 # different preference levels given by `num_low`, `num_med`, 169 # `num_high`. Prefixes is a list of the format 170 # 'fd00:1234:0:0::/64 paos low a802'. 171 self.assertEqual(len(prefixes), min(num_high + num_med + num_low, DESIRED_NUM_ON_MESH_PREFIX)) 172 prfs = [prefix.split(' ')[2] for prefix in prefixes] 173 self.assertEqual(prfs.count('high'), min(num_high, DESIRED_NUM_ON_MESH_PREFIX)) 174 self.assertEqual(prfs.count('med'), min(num_med, max(0, DESIRED_NUM_ON_MESH_PREFIX - num_high))) 175 self.assertEqual(prfs.count('low'), min(num_low, max(0, DESIRED_NUM_ON_MESH_PREFIX - num_high - num_med))) 176 177 def check_num_of_routes(self, routes, num_low, num_med, num_high): 178 # Check and validate the prefix entries in network data (from 179 # `routes`) based on number of published prefix entries at 180 # different preference levels given by `num_low`, `num_med`, 181 # `num_high`. Prefixes is a list of the format 182 # 'fd00:abce:0:0::/64 s med 6c01'. 183 self.assertEqual(len(routes), min(num_high + num_med + num_low, DESIRED_NUM_EXTERNAL_ROUTE)) 184 prfs = [route.split(' ')[2] for route in routes] 185 self.assertEqual(prfs.count('high'), min(num_high, DESIRED_NUM_EXTERNAL_ROUTE)) 186 self.assertEqual(prfs.count('med'), min(num_med, max(0, DESIRED_NUM_EXTERNAL_ROUTE - num_high))) 187 self.assertEqual(prfs.count('low'), min(num_low, max(0, DESIRED_NUM_EXTERNAL_ROUTE - num_high - num_med))) 188 189 def test(self): 190 leader = self.nodes[LEADER] 191 router1 = self.nodes[ROUTER1] 192 router2 = self.nodes[ROUTER2] 193 router3 = self.nodes[ROUTER3] 194 router4 = self.nodes[ROUTER4] 195 router5 = self.nodes[ROUTER5] 196 end_dev1 = self.nodes[END_DEV1] 197 end_dev2 = self.nodes[END_DEV2] 198 end_dev3 = self.nodes[END_DEV3] 199 end_dev4 = self.nodes[END_DEV4] 200 end_dev5 = self.nodes[END_DEV5] 201 202 nodes = self.nodes.values() 203 routers = [router1, router2, router3, router4, router5] 204 end_devs = [end_dev1, end_dev2, end_dev3, end_dev4, end_dev5] 205 206 # Start the nodes 207 208 leader.start() 209 self.simulator.go(5) 210 self.assertEqual(leader.get_state(), 'leader') 211 212 for router in routers: 213 router.start() 214 self.simulator.go(5) 215 self.assertEqual(router.get_state(), 'router') 216 217 for end_dev in end_devs: 218 end_dev.start() 219 self.simulator.go(5) 220 self.assertEqual(end_dev.get_state(), 'child') 221 222 #--------------------------------------------------------------------------------- 223 # DNS/SRP anycast entries 224 225 # Publish DNS/SRP anycast on leader and all routers (6 nodes). 226 227 leader.netdata_publish_dnssrp_anycast(ANYCAST_SEQ_NUM) 228 for node in routers: 229 node.netdata_publish_dnssrp_anycast(ANYCAST_SEQ_NUM) 230 self.simulator.go(WAIT_TIME) 231 232 # Check all entries are present in the network data 233 234 services = leader.get_services() 235 self.assertEqual(len(services), min(1 + len(routers), DESIRED_NUM_DNSSRP_ANYCAST)) 236 self.verify_anycast_services(services) 237 238 # Publish same entry on all end-devices (5 nodes). 239 240 for node in end_devs: 241 node.netdata_publish_dnssrp_anycast(ANYCAST_SEQ_NUM) 242 print(node.name) 243 self.simulator.go(WAIT_TIME) 244 245 # Check number of entries in the network data is limited to 246 # the desired number (8 entries). 247 248 services = leader.get_services() 249 self.assertEqual(len(leader.get_services()), min(len(nodes), DESIRED_NUM_DNSSRP_ANYCAST)) 250 self.verify_anycast_services(services) 251 252 # Unpublish the entry from nodes one by one starting from leader 253 # and check that number of entries is correct in each step. 254 255 num = len(nodes) 256 for node in nodes: 257 node.netdata_unpublish_dnssrp() 258 self.simulator.go(WAIT_TIME) 259 num -= 1 260 services = leader.get_services() 261 self.assertEqual(len(services), min(num, DESIRED_NUM_DNSSRP_ANYCAST)) 262 self.verify_anycast_services(services) 263 264 #--------------------------------------------------------------------------------- 265 # DNS/SRP unicast entries 266 267 # Publish DNS/SRP unicast address on all routers, first using 268 # MLE-EID address, then change to use specific address. Verify 269 # that number of entries in network data is correct in each step 270 # and that entries are switched correctly. 271 num = 0 272 for node in routers: 273 node.netdata_publish_dnssrp_unicast_mleid(DNSSRP_PORT) 274 self.simulator.go(WAIT_TIME) 275 num += 1 276 services = leader.get_services() 277 self.assertEqual(len(services), min(num, DESIRED_NUM_DNSSRP_UNCIAST)) 278 self.verify_unicast_services(services) 279 280 for node in routers: 281 node.netdata_publish_dnssrp_unicast(DNSSRP_ADDRESS, DNSSRP_PORT) 282 self.simulator.go(WAIT_TIME) 283 services = leader.get_services() 284 self.assertEqual(len(services), min(num, DESIRED_NUM_DNSSRP_UNCIAST)) 285 self.verify_unicast_services(services) 286 287 for node in routers: 288 node.srp_server_set_enabled(True) 289 self.simulator.go(WAIT_TIME) 290 self.assertEqual(sum(node.srp_server_get_state() == 'running' for node in routers), 291 min(len(routers), DESIRED_NUM_DNSSRP_UNCIAST)) 292 self.assertEqual(sum(node.srp_server_get_state() == 'stopped' for node in routers), 293 max(len(routers) - DESIRED_NUM_DNSSRP_UNCIAST, 0)) 294 295 for node in routers: 296 node.netdata_unpublish_dnssrp() 297 self.simulator.go(WAIT_TIME) 298 num -= 1 299 services = leader.get_services() 300 self.assertEqual(len(services), min(num, DESIRED_NUM_DNSSRP_UNCIAST)) 301 self.verify_unicast_services(services) 302 for node in routers: 303 node.srp_server_set_enabled(False) 304 self.assertEqual(node.srp_server_get_state(), 'disabled') 305 306 #--------------------------------------------------------------------------------- 307 # DNS/SRP entries: Verify publisher preference when removing 308 # entries. 309 # 310 # Publish DNS/SRP anycast on 8 nodes: leader, router1, 311 # router2, and all 5 end-devices. Afterwards, manually add 312 # the same service entry in Network Data on router3, router4, 313 # and router5 and at each step check that entry from one of 314 # the end-devices is removed (publisher prefers 315 # entries from routers over the ones from end-devices). 316 317 num = 0 318 test_routers = [leader, router1, router2] 319 for node in test_routers + end_devs: 320 node.netdata_publish_dnssrp_anycast(ANYCAST_SEQ_NUM) 321 self.simulator.go(WAIT_TIME) 322 num += 1 323 services = leader.get_services() 324 self.assertEqual(len(services), num) 325 self.verify_anycast_services(services) 326 327 self.assertEqual(num, DESIRED_NUM_DNSSRP_ANYCAST) 328 329 service_data = '%02x%02x' % (ANYCAST_SERVICE_NUM, int(ANYCAST_SEQ_NUM)) 330 for node in [router3, router4, router5]: 331 node.add_service(str(THREAD_ENTERPRISE_NUMBER), service_data, '00') 332 node.register_netdata() 333 self.simulator.go(WAIT_TIME) 334 335 services = leader.get_services() 336 self.assertEqual(len(services), num) 337 self.verify_anycast_services(services) 338 339 service_rlocs = [int(service[4], 16) for service in services] 340 test_routers.append(node) 341 342 for router in test_routers: 343 self.assertIn(router.get_addr16(), service_rlocs) 344 345 #--------------------------------------------------------------------------------- 346 # On-mesh prefix 347 348 # Publish the same on-mesh prefix on different nodes (low 349 # preference on end-devices, medium preference on routers, and 350 # high on leader) one by one and then unpublish them one by one. 351 # Verify that at each step the entries in the network data are 352 # correct. Particularly verify that that higher preference 353 # entries replace lower preference ones even when there are 354 # already desired number in network data. 355 356 num_low = 0 357 num_med = 0 358 num_high = 0 359 360 for node in end_devs: 361 node.netdata_publish_prefix(ON_MESH_PREFIX, ON_MESH_FLAGS, 'low') 362 self.simulator.go(WAIT_TIME) 363 num_low += 1 364 prefixes = leader.get_prefixes() 365 self.check_num_of_prefixes(prefixes, num_low, num_med, num_high) 366 367 # Now add the entry as 'med' on routers and check that we see those in the list. 368 for node in routers: 369 node.netdata_publish_prefix(ON_MESH_PREFIX, ON_MESH_FLAGS, 'med') 370 self.simulator.go(WAIT_TIME) 371 num_med += 1 372 prefixes = leader.get_prefixes() 373 self.check_num_of_prefixes(prefixes, num_low, num_med, num_high) 374 375 leader.netdata_publish_prefix(ON_MESH_PREFIX, ON_MESH_FLAGS, 'high') 376 self.simulator.go(WAIT_TIME) 377 num_high += 1 378 prefixes = leader.get_prefixes() 379 self.check_num_of_prefixes(prefixes, num_low, num_med, num_high) 380 381 for node in routers: 382 node.netdata_unpublish_prefix(ON_MESH_PREFIX) 383 self.simulator.go(WAIT_TIME) 384 num_med -= 1 385 prefixes = leader.get_prefixes() 386 self.check_num_of_prefixes(prefixes, num_low, num_med, num_high) 387 388 leader.netdata_unpublish_prefix(ON_MESH_PREFIX) 389 self.simulator.go(WAIT_TIME) 390 num_high -= 1 391 prefixes = leader.get_prefixes() 392 self.check_num_of_prefixes(prefixes, num_low, num_med, num_high) 393 394 for node in end_devs: 395 node.netdata_unpublish_prefix(ON_MESH_PREFIX) 396 self.simulator.go(WAIT_TIME) 397 num_low -= 1 398 prefixes = leader.get_prefixes() 399 self.check_num_of_prefixes(prefixes, num_low, num_med, num_high) 400 401 #- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 402 # Verify that when removing extra entries, non-preferred entries 403 # are removed first over preferred ones. Entries from routers are 404 # preferred over similar entries from end-devices. 405 406 # Publish prefix entry on `end_dev1` and verify that it is added. 407 408 end_dev1.netdata_publish_prefix(ON_MESH_PREFIX, ON_MESH_FLAGS, 'med') 409 self.simulator.go(WAIT_TIME) 410 prefixes = leader.get_prefixes() 411 self.check_num_of_prefixes(prefixes, 0, 1, 0) 412 413 # Publish same prefix on all routers (again as `med` preference). 414 # Verify that we reach the desired number of prefix entries in network 415 # data and that the entry from `end_dev1` is present in network data. 416 417 for node in routers: 418 node.netdata_publish_prefix(ON_MESH_PREFIX, ON_MESH_FLAGS, 'med') 419 self.simulator.go(WAIT_TIME) 420 prefixes = leader.get_prefixes() 421 self.check_num_of_prefixes(prefixes, 0, 1 + len(routers), 0) 422 self.assertTrue(1 + len(routers) >= DESIRED_NUM_ON_MESH_PREFIX) 423 # `prefixes` is a list of format 'fd00:1234:0:0::/64 paos low a802' 424 rlocs = [int(prefix.split(' ')[3], 16) for prefix in prefixes] 425 self.assertTrue(rlocs.count(end_dev1.get_addr16()) == 1) 426 427 # Publish same prefix now with `high` preference on leader. 428 # Since it is `high` preference, it is added to network data 429 # which leads to total number of entries to go above the desired 430 # number temporarily and trigger other nodes to try to remove 431 # their entry. The entries from routers should be preferred over 432 # the one from `end_dev1` so that is the one we expect to be 433 # removed. We check that this is the case (i.e., the entry from 434 # `end_dev1` is no longer present in network data). 435 436 leader.netdata_publish_prefix(ON_MESH_PREFIX, ON_MESH_FLAGS, 'high') 437 self.simulator.go(WAIT_TIME) 438 prefixes = leader.get_prefixes() 439 self.check_num_of_prefixes(prefixes, 0, 1 + len(routers), 1) 440 rlocs = [int(prefix.split(' ')[3], 16) for prefix in prefixes] 441 self.assertTrue(rlocs.count(end_dev1.get_addr16()) == 0) 442 443 #--------------------------------------------------------------------------------- 444 # External route 445 446 num = 0 447 for node in nodes: 448 node.netdata_publish_route(EXTERNAL_ROUTE, EXTERNAL_FLAGS, 'low') 449 self.simulator.go(WAIT_TIME) 450 num += 1 451 routes = leader.get_routes() 452 self.check_num_of_routes(routes, num, 0, 0) 453 454 leader.netdata_unpublish_prefix(EXTERNAL_ROUTE) 455 leader.netdata_publish_route(EXTERNAL_ROUTE, EXTERNAL_FLAGS, 'high') 456 self.simulator.go(WAIT_TIME) 457 routes = leader.get_routes() 458 self.check_num_of_routes(routes, num - 1, 0, 1) 459 460 461if __name__ == '__main__': 462 unittest.main() 463