From 39e6172304c4e5bdadf2fa6dbdc4d7a7f6987ebc Mon Sep 17 00:00:00 2001 From: kangping Date: Thu, 8 Apr 2021 02:01:29 +0800 Subject: [PATCH] [border-router] accept RA initiated from the infra interface (#6348) Accept RA initiated from the infra interface so that we can detect the existence of another RA daemon which is working on the same infra interface. Also enhance the test_single_border_router.py test script to make it more readable. --- src/core/border_router/routing_manager.cpp | 33 +- .../test_single_border_router.py | 308 +++++++++--------- tests/scripts/thread-cert/config.py | 3 +- tests/scripts/thread-cert/node.py | 74 +++-- 4 files changed, 225 insertions(+), 193 deletions(-) diff --git a/src/core/border_router/routing_manager.cpp b/src/core/border_router/routing_manager.cpp index adb2dc9ee..aeae0ca10 100644 --- a/src/core/border_router/routing_manager.cpp +++ b/src/core/border_router/routing_manager.cpp @@ -249,9 +249,6 @@ void RoutingManager::RecvIcmp6Message(uint32_t aInfraIfIndex, VerifyOrExit(aInfraIfIndex == mInfraIfIndex, error = kErrorDrop); infraLinkLocalAddr = static_cast(&mInfraIfLinkLocalAddress); - // Drop any ICMPv6 messages sent from myself. - VerifyOrExit(infraLinkLocalAddr != nullptr && aSrcAddress != *infraLinkLocalAddr, error = kErrorDrop); - VerifyOrExit(aBuffer != nullptr && aBufferLength >= sizeof(*icmp6Header), error = kErrorParse); icmp6Header = reinterpret_cast(aBuffer); @@ -262,6 +259,8 @@ void RoutingManager::RecvIcmp6Message(uint32_t aInfraIfIndex, HandleRouterAdvertisement(aSrcAddress, aBuffer, aBufferLength); break; case Ip6::Icmp::Header::kTypeRouterSolicit: + // Drop Router Solicitations initiated from infra interface. + VerifyOrExit(aSrcAddress != *infraLinkLocalAddr, error = kErrorDrop); HandleRouterSolicit(aSrcAddress, aBuffer, aBufferLength); break; default: @@ -637,6 +636,8 @@ void RoutingManager::StartRoutingPolicyEvaluationDelay(void) // between 0 and kMaxRtrSolicitationDelay. void RoutingManager::StartRouterSolicitationDelay(void) { + OT_ASSERT(mAdvertisedOnLinkPrefix == nullptr); + uint32_t randomDelay; mRouterSolicitCount = 0; @@ -993,10 +994,12 @@ bool RoutingManager::UpdateDiscoveredPrefixes(const RouterAdv::PrefixInfoOption if (!IsValidOnLinkPrefix(aPio, aManagedAddrConfig)) { - otLogInfoBr("ignore invalid prefix in PIO: %s", prefix.ToString().AsCString()); + otLogInfoBr("ignore invalid on-link prefix in PIO: %s", prefix.ToString().AsCString()); ExitNow(); } + VerifyOrExit(mAdvertisedOnLinkPrefix == nullptr || prefix != *mAdvertisedOnLinkPrefix); + otLogInfoBr("discovered on-link prefix (%s, %u seconds) from interface %u", prefix.ToString().AsCString(), aPio.GetValidLifetime(), mInfraIfIndex); @@ -1020,11 +1023,20 @@ bool RoutingManager::UpdateDiscoveredPrefixes(const RouterAdv::RouteInfoOption & if (!IsValidOmrPrefix(prefix)) { - otLogInfoBr("ignore invalid prefix in RIO: %s", prefix.ToString().AsCString()); + otLogInfoBr("ignore invalid OMR prefix in RIO: %s", prefix.ToString().AsCString()); ExitNow(); } - // Ignore the OMR prefix in current Thread Network. + // Ignore OMR prefixes advertised by ourselves or in current Thread Network Data. + // The `mAdvertisedOmrPrefixes` and the OMR prefix set in Network Data should eventually + // be equal, but there is time that they are not synchronized immediately: + // 1. Network Data could contain more OMR prefixes than `mAdvertisedOmrPrefixes` because + // we added random delay before Evaluating routing policy when Network Data is changed. + // 2. `mAdvertisedOmrPrefixes` could contain more OMR prefixes than Network Data because + // it takes time to sync a new OMR prefix into Network Data (multicast loopback RA + // messages are usually faster than Thread Network Data propagation). + // They are the reasons why we need both the checks. + VerifyOrExit(!ContainsPrefix(prefix, mAdvertisedOmrPrefixes, mAdvertisedOmrPrefixNum)); VerifyOrExit(!NetworkDataContainsOmrPrefix(prefix)); otLogInfoBr("discovered OMR prefix (%s, %u seconds) from interface %u", prefix.ToString().AsCString(), @@ -1080,9 +1092,12 @@ bool RoutingManager::InvalidateDiscoveredPrefixes(const Ip6::Prefix *aPrefix, bo { mDiscoveredPrefixInvalidTimer.Stop(); - // There are no valid on-link prefixes on infra link now, start Router Solicitation - // To find out more on-link prefixes or timeout to advertise my local on-link prefix. - StartRouterSolicitationDelay(); + if (mAdvertisedOnLinkPrefix == nullptr) + { + // There are no valid on-link prefixes on infra link now, start Router Solicitation + // To find out more on-link prefixes or timeout to advertise my local on-link prefix. + StartRouterSolicitationDelay(); + } } else { diff --git a/tests/scripts/thread-cert/border_router/test_single_border_router.py b/tests/scripts/thread-cert/border_router/test_single_border_router.py index dc07be185..55a93b4c0 100755 --- a/tests/scripts/thread-cert/border_router/test_single_border_router.py +++ b/tests/scripts/thread-cert/border_router/test_single_border_router.py @@ -39,16 +39,14 @@ import thread_cert # Topology: # ----------------(eth)-------------------- # | | -# BR1 (Leader) HOST +# BR (Leader) HOST # | -# ROUTER1 +# ROUTER # -BR1 = 1 -ROUTER1 = 2 -HOST = 4 - -CHANNEL1 = 18 +BR = 1 +ROUTER = 2 +HOST = 3 # The two prefixes are set small enough that a random-generated OMR prefix is # very likely greater than them. So that the duckhorn BR will remove the random-generated one. @@ -60,20 +58,17 @@ class SingleBorderRouter(thread_cert.TestCase): USE_MESSAGE_FACTORY = False TOPOLOGY = { - BR1: { - 'name': 'BR_1', - 'allowlist': [ROUTER1], + BR: { + 'name': 'BR', + 'allowlist': [ROUTER], 'is_otbr': True, 'version': '1.2', - 'channel': CHANNEL1, - 'router_selection_jitter': 2, }, - ROUTER1: { - 'name': 'Router_1', - 'allowlist': [BR1], + ROUTER: { + 'name': 'Router', + 'allowlist': [BR], 'version': '1.2', - 'channel': CHANNEL1, - 'router_selection_jitter': 2, + 'router_selection_jitter': 1, }, HOST: { 'name': 'Host', @@ -82,16 +77,20 @@ class SingleBorderRouter(thread_cert.TestCase): } def test(self): - self.nodes[HOST].start(start_radvd=False) + br = self.nodes[BR] + router = self.nodes[ROUTER] + host = self.nodes[HOST] + + host.start(start_radvd=False) self.simulator.go(5) - self.nodes[BR1].start() + br.start() self.simulator.go(5) - self.assertEqual('leader', self.nodes[BR1].get_state()) + self.assertEqual('leader', br.get_state()) - self.nodes[ROUTER1].start() + router.start() self.simulator.go(5) - self.assertEqual('router', self.nodes[ROUTER1].get_state()) + self.assertEqual('router', router.get_state()) # # Case 1. There is no OMR prefix or on-link prefix. @@ -100,30 +99,29 @@ class SingleBorderRouter(thread_cert.TestCase): self.simulator.go(10) self.collect_ipaddrs() - logging.info("BR1 addrs: %r", self.nodes[BR1].get_addrs()) - logging.info("ROUTER1 addrs: %r", self.nodes[ROUTER1].get_addrs()) - logging.info("HOST addrs: %r", self.nodes[HOST].get_addrs()) + logging.info("BR addrs: %r", br.get_addrs()) + logging.info("ROUTER addrs: %r", router.get_addrs()) + logging.info("HOST addrs: %r", host.get_addrs()) - self.assertTrue(len(self.nodes[BR1].get_prefixes()) == 1) - self.assertTrue(len(self.nodes[ROUTER1].get_prefixes()) == 1) - self.assertTrue(len(self.nodes[BR1].get_routes()) == 1) - self.assertTrue(len(self.nodes[ROUTER1].get_routes()) == 1) + self.assertEqual(len(br.get_prefixes()), 1) + self.assertEqual(len(router.get_prefixes()), 1) + self.assertEqual(len(br.get_routes()), 1) + self.assertEqual(len(router.get_routes()), 1) - omr_prefix = self.nodes[BR1].get_prefixes()[0] - external_route = self.nodes[BR1].get_routes()[0] + omr_prefix = br.get_prefixes()[0] + external_route = br.get_routes()[0] - self.assertTrue(len(self.nodes[BR1].get_ip6_address(config.ADDRESS_TYPE.OMR)) == 1) - self.assertTrue(len(self.nodes[ROUTER1].get_ip6_address(config.ADDRESS_TYPE.OMR)) == 1) - self.assertTrue(len(self.nodes[HOST].get_ip6_address(config.ADDRESS_TYPE.ONLINK_ULA)) == 1) + self.assertEqual(len(br.get_ip6_address(config.ADDRESS_TYPE.OMR)), 1) + self.assertEqual(len(router.get_ip6_address(config.ADDRESS_TYPE.OMR)), 1) + self.assertEqual(len(host.get_ip6_address(config.ADDRESS_TYPE.ONLINK_ULA)), 1) - br1_omr_address = self.nodes[BR1].get_ip6_address(config.ADDRESS_TYPE.OMR)[0] - router1_omr_address = self.nodes[ROUTER1].get_ip6_address(config.ADDRESS_TYPE.OMR)[0] - host_ula_address = self.nodes[HOST].get_ip6_address(config.ADDRESS_TYPE.ONLINK_ULA)[0] + br1_omr_address = br.get_ip6_address(config.ADDRESS_TYPE.OMR)[0] + router1_omr_address = router.get_ip6_address(config.ADDRESS_TYPE.OMR)[0] + host_ula_address = host.get_ip6_address(config.ADDRESS_TYPE.ONLINK_ULA)[0] # Router1 can ping to/from the Host on infra link. - self.assertTrue(self.nodes[ROUTER1].ping(self.nodes[HOST].get_ip6_address(config.ADDRESS_TYPE.ONLINK_ULA)[0])) - self.assertTrue(self.nodes[HOST].ping(self.nodes[ROUTER1].get_ip6_address(config.ADDRESS_TYPE.OMR)[0], - backbone=True)) + self.assertTrue(router.ping(host.get_ip6_address(config.ADDRESS_TYPE.ONLINK_ULA)[0])) + self.assertTrue(host.ping(router.get_ip6_address(config.ADDRESS_TYPE.OMR)[0], backbone=True)) # # Case 2. User adds smaller on-mesh prefix. @@ -132,195 +130,205 @@ class SingleBorderRouter(thread_cert.TestCase): # is removed. # - self.nodes[BR1].add_prefix(ON_MESH_PREFIX1) - self.nodes[BR1].add_prefix(ON_MESH_PREFIX2) - self.nodes[BR1].register_netdata() + br.add_prefix(ON_MESH_PREFIX1) + br.add_prefix(ON_MESH_PREFIX2) + br.register_netdata() self.simulator.go(10) self.collect_ipaddrs() - logging.info("BR1 addrs: %r", self.nodes[BR1].get_addrs()) - logging.info("ROUTER1 addrs: %r", self.nodes[ROUTER1].get_addrs()) - logging.info("HOST addrs: %r", self.nodes[HOST].get_addrs()) + logging.info("BR addrs: %r", br.get_addrs()) + logging.info("ROUTER addrs: %r", router.get_addrs()) + logging.info("HOST addrs: %r", host.get_addrs()) - self.assertGreaterEqual(len(self.nodes[HOST].get_addrs()), 2) + self.assertGreaterEqual(len(host.get_addrs()), 2) - self.assertTrue(len(self.nodes[BR1].get_prefixes()) == 2) - self.assertTrue(len(self.nodes[ROUTER1].get_prefixes()) == 2) - self.assertTrue(len(self.nodes[BR1].get_routes()) == 1) - self.assertTrue(len(self.nodes[ROUTER1].get_routes()) == 1) + self.assertEqual(len(br.get_prefixes()), 2) + self.assertEqual(len(router.get_prefixes()), 2) + self.assertEqual(len(br.get_routes()), 1) + self.assertEqual(len(router.get_routes()), 1) - self.assertTrue(len(self.nodes[BR1].get_ip6_address(config.ADDRESS_TYPE.OMR)) == 2) - self.assertTrue(len(self.nodes[ROUTER1].get_ip6_address(config.ADDRESS_TYPE.OMR)) == 2) - self.assertTrue(len(self.nodes[HOST].get_ip6_address(config.ADDRESS_TYPE.ONLINK_ULA)) == 1) + self.assertEqual(len(br.get_ip6_address(config.ADDRESS_TYPE.OMR)), 2) + self.assertEqual(len(router.get_ip6_address(config.ADDRESS_TYPE.OMR)), 2) + self.assertEqual(len(host.get_ip6_address(config.ADDRESS_TYPE.ONLINK_ULA)), 1) # Router1 can ping to/from the Host on infra link. - self.assertTrue(self.nodes[ROUTER1].ping(self.nodes[HOST].get_ip6_address(config.ADDRESS_TYPE.ONLINK_ULA)[0])) - self.assertTrue(self.nodes[HOST].ping(self.nodes[ROUTER1].get_ip6_address(config.ADDRESS_TYPE.OMR)[0], - backbone=True)) - self.assertTrue(self.nodes[HOST].ping(self.nodes[ROUTER1].get_ip6_address(config.ADDRESS_TYPE.OMR)[1], - backbone=True)) + self.assertTrue(router.ping(host.get_ip6_address(config.ADDRESS_TYPE.ONLINK_ULA)[0])) + self.assertTrue(host.ping(router.get_ip6_address(config.ADDRESS_TYPE.OMR)[0], backbone=True)) + self.assertTrue(host.ping(router.get_ip6_address(config.ADDRESS_TYPE.OMR)[1], backbone=True)) # Remove user prefixes, should re-register local OMR prefix. - self.nodes[BR1].remove_prefix(ON_MESH_PREFIX1) - self.nodes[BR1].remove_prefix(ON_MESH_PREFIX2) - self.nodes[BR1].register_netdata() + br.remove_prefix(ON_MESH_PREFIX1) + br.remove_prefix(ON_MESH_PREFIX2) + br.register_netdata() self.simulator.go(10) self.collect_ipaddrs() - logging.info("BR1 addrs: %r", self.nodes[BR1].get_addrs()) - logging.info("ROUTER1 addrs: %r", self.nodes[ROUTER1].get_addrs()) - logging.info("HOST addrs: %r", self.nodes[HOST].get_addrs()) + logging.info("BR addrs: %r", br.get_addrs()) + logging.info("ROUTER addrs: %r", router.get_addrs()) + logging.info("HOST addrs: %r", host.get_addrs()) - self.assertTrue(len(self.nodes[BR1].get_prefixes()) == 1) - self.assertTrue(len(self.nodes[ROUTER1].get_prefixes()) == 1) - self.assertTrue(len(self.nodes[BR1].get_routes()) == 1) - self.assertTrue(len(self.nodes[ROUTER1].get_routes()) == 1) + self.assertEqual(len(br.get_prefixes()), 1) + self.assertEqual(len(router.get_prefixes()), 1) + self.assertEqual(len(br.get_routes()), 1) + self.assertEqual(len(router.get_routes()), 1) # The same local OMR and on-link prefix should be re-register. - self.assertEqual(omr_prefix, self.nodes[BR1].get_prefixes()[0]) - self.assertEqual(omr_prefix, self.nodes[ROUTER1].get_prefixes()[0]) - self.assertEqual(external_route, self.nodes[BR1].get_routes()[0]) - self.assertEqual(external_route, self.nodes[ROUTER1].get_routes()[0]) + self.assertEqual(br.get_prefixes(), [omr_prefix]) + self.assertEqual(router.get_prefixes(), [omr_prefix]) + self.assertEqual(br.get_routes(), [external_route]) + self.assertEqual(router.get_routes(), [external_route]) - self.assertTrue(len(self.nodes[BR1].get_ip6_address(config.ADDRESS_TYPE.OMR)) == 1) - self.assertTrue(len(self.nodes[ROUTER1].get_ip6_address(config.ADDRESS_TYPE.OMR)) == 1) - self.assertTrue(len(self.nodes[HOST].get_ip6_address(config.ADDRESS_TYPE.ONLINK_ULA)) == 1) + self.assertEqual(len(br.get_ip6_address(config.ADDRESS_TYPE.OMR)), 1) + self.assertEqual(len(router.get_ip6_address(config.ADDRESS_TYPE.OMR)), 1) + self.assertEqual(len(host.get_ip6_address(config.ADDRESS_TYPE.ONLINK_ULA)), 1) - self.assertEqual(br1_omr_address, self.nodes[BR1].get_ip6_address(config.ADDRESS_TYPE.OMR)[0]) - self.assertEqual(router1_omr_address, self.nodes[ROUTER1].get_ip6_address(config.ADDRESS_TYPE.OMR)[0]) - self.assertEqual(host_ula_address, self.nodes[HOST].get_ip6_address(config.ADDRESS_TYPE.ONLINK_ULA)[0]) + self.assertEqual(br.get_ip6_address(config.ADDRESS_TYPE.OMR), [br1_omr_address]) + self.assertEqual(router.get_ip6_address(config.ADDRESS_TYPE.OMR), [router1_omr_address]) + self.assertEqual(host.get_ip6_address(config.ADDRESS_TYPE.ONLINK_ULA), [host_ula_address]) # Router1 can ping to/from the Host on infra link. - self.assertTrue(self.nodes[ROUTER1].ping(self.nodes[HOST].get_ip6_address(config.ADDRESS_TYPE.ONLINK_ULA)[0])) - self.assertTrue(self.nodes[HOST].ping(self.nodes[ROUTER1].get_ip6_address(config.ADDRESS_TYPE.OMR)[0], - backbone=True)) + self.assertTrue(router.ping(host.get_ip6_address(config.ADDRESS_TYPE.ONLINK_ULA)[0])) + self.assertTrue(host.ping(router.get_ip6_address(config.ADDRESS_TYPE.OMR)[0], backbone=True)) # # Case 3. OMR and on-link prefixes should be removed when Border Routing is # explicitly disabled and added when Border Routing is enabled again. # - self.nodes[BR1].disable_br() + br.disable_br() self.simulator.go(10) self.collect_ipaddrs() - logging.info("BR1 addrs: %r", self.nodes[BR1].get_addrs()) - logging.info("ROUTER1 addrs: %r", self.nodes[ROUTER1].get_addrs()) - logging.info("HOST addrs: %r", self.nodes[HOST].get_addrs()) + logging.info("BR addrs: %r", br.get_addrs()) + logging.info("ROUTER addrs: %r", router.get_addrs()) + logging.info("HOST addrs: %r", host.get_addrs()) - self.assertTrue(len(self.nodes[BR1].get_prefixes()) == 0) - self.assertTrue(len(self.nodes[ROUTER1].get_prefixes()) == 0) - self.assertTrue(len(self.nodes[BR1].get_routes()) == 0) - self.assertTrue(len(self.nodes[ROUTER1].get_routes()) == 0) - self.assertTrue(len(self.nodes[BR1].get_ip6_address(config.ADDRESS_TYPE.OMR)) == 0) - self.assertTrue(len(self.nodes[ROUTER1].get_ip6_address(config.ADDRESS_TYPE.OMR)) == 0) + self.assertEqual(len(br.get_prefixes()), 0) + self.assertEqual(len(router.get_prefixes()), 0) + self.assertEqual(len(br.get_routes()), 0) + self.assertEqual(len(router.get_routes()), 0) + self.assertEqual(len(br.get_ip6_address(config.ADDRESS_TYPE.OMR)), 0) + self.assertEqual(len(router.get_ip6_address(config.ADDRESS_TYPE.OMR)), 0) # Per RFC 4862, the host will not immediately remove the ULA address, but deprecate it. - self.assertTrue(len(self.nodes[HOST].get_ip6_address(config.ADDRESS_TYPE.ONLINK_ULA)) == 1) + self.assertEqual(len(host.get_ip6_address(config.ADDRESS_TYPE.ONLINK_ULA)), 1) - self.nodes[BR1].enable_br() + br.enable_br() # It takes around 10 seconds to start sending RA messages. self.simulator.go(15) self.collect_ipaddrs() - logging.info("BR1 addrs: %r", self.nodes[BR1].get_addrs()) - logging.info("ROUTER1 addrs: %r", self.nodes[ROUTER1].get_addrs()) - logging.info("HOST addrs: %r", self.nodes[HOST].get_addrs()) + logging.info("BR addrs: %r", br.get_addrs()) + logging.info("ROUTER addrs: %r", router.get_addrs()) + logging.info("HOST addrs: %r", host.get_addrs()) - self.assertTrue(len(self.nodes[BR1].get_prefixes()) == 1) - self.assertTrue(len(self.nodes[ROUTER1].get_prefixes()) == 1) - self.assertTrue(len(self.nodes[BR1].get_routes()) == 1) - self.assertTrue(len(self.nodes[ROUTER1].get_routes()) == 1) + self.assertEqual(len(br.get_prefixes()), 1) + self.assertEqual(len(router.get_prefixes()), 1) + self.assertEqual(len(br.get_routes()), 1) + self.assertEqual(len(router.get_routes()), 1) # The same local OMR and on-link prefix should be re-registered. - self.assertEqual(omr_prefix, self.nodes[BR1].get_prefixes()[0]) - self.assertEqual(omr_prefix, self.nodes[ROUTER1].get_prefixes()[0]) - self.assertEqual(external_route, self.nodes[BR1].get_routes()[0]) - self.assertEqual(external_route, self.nodes[ROUTER1].get_routes()[0]) + self.assertEqual(br.get_prefixes(), [omr_prefix]) + self.assertEqual(router.get_prefixes(), [omr_prefix]) + self.assertEqual(br.get_routes(), [external_route]) + self.assertEqual(router.get_routes(), [external_route]) - self.assertTrue(len(self.nodes[BR1].get_ip6_address(config.ADDRESS_TYPE.OMR)) == 1) - self.assertTrue(len(self.nodes[ROUTER1].get_ip6_address(config.ADDRESS_TYPE.OMR)) == 1) - self.assertTrue(len(self.nodes[HOST].get_ip6_address(config.ADDRESS_TYPE.ONLINK_ULA)) == 1) + self.assertEqual(len(br.get_ip6_address(config.ADDRESS_TYPE.OMR)), 1) + self.assertEqual(len(router.get_ip6_address(config.ADDRESS_TYPE.OMR)), 1) + self.assertEqual(len(host.get_ip6_address(config.ADDRESS_TYPE.ONLINK_ULA)), 1) - self.assertEqual(br1_omr_address, self.nodes[BR1].get_ip6_address(config.ADDRESS_TYPE.OMR)[0]) - self.assertEqual(router1_omr_address, self.nodes[ROUTER1].get_ip6_address(config.ADDRESS_TYPE.OMR)[0]) - self.assertEqual(host_ula_address, self.nodes[HOST].get_ip6_address(config.ADDRESS_TYPE.ONLINK_ULA)[0]) + self.assertEqual(br.get_ip6_address(config.ADDRESS_TYPE.OMR), [br1_omr_address]) + self.assertEqual(router.get_ip6_address(config.ADDRESS_TYPE.OMR), [router1_omr_address]) + self.assertEqual(host.get_ip6_address(config.ADDRESS_TYPE.ONLINK_ULA), [host_ula_address]) # Router1 can ping to/from the Host on infra link. - self.assertTrue(self.nodes[ROUTER1].ping(self.nodes[HOST].get_ip6_address(config.ADDRESS_TYPE.ONLINK_ULA)[0])) - self.assertTrue(self.nodes[HOST].ping(self.nodes[ROUTER1].get_ip6_address(config.ADDRESS_TYPE.OMR)[0], - backbone=True)) + self.assertTrue(router.ping(host.get_ip6_address(config.ADDRESS_TYPE.ONLINK_ULA)[0])) + self.assertTrue(host.ping(router.get_ip6_address(config.ADDRESS_TYPE.OMR)[0], backbone=True)) # # Case 4. The Routing Manager should be stopped if the infra interface went down. # - self.nodes[BR1].disable_ether() + br.disable_ether() self.simulator.go(10) self.collect_ipaddrs() - logging.info("BR1 addrs: %r", self.nodes[BR1].get_addrs()) - logging.info("ROUTER1 addrs: %r", self.nodes[ROUTER1].get_addrs()) - logging.info("HOST addrs: %r", self.nodes[HOST].get_addrs()) + logging.info("BR addrs: %r", br.get_addrs()) + logging.info("ROUTER addrs: %r", router.get_addrs()) + logging.info("HOST addrs: %r", host.get_addrs()) - self.assertTrue(len(self.nodes[BR1].get_prefixes()) == 0) - self.assertTrue(len(self.nodes[ROUTER1].get_prefixes()) == 0) - self.assertTrue(len(self.nodes[BR1].get_routes()) == 0) - self.assertTrue(len(self.nodes[ROUTER1].get_routes()) == 0) - self.assertTrue(len(self.nodes[BR1].get_ip6_address(config.ADDRESS_TYPE.OMR)) == 0) - self.assertTrue(len(self.nodes[ROUTER1].get_ip6_address(config.ADDRESS_TYPE.OMR)) == 0) + self.assertEqual(len(br.get_prefixes()), 0) + self.assertEqual(len(router.get_prefixes()), 0) + self.assertEqual(len(br.get_routes()), 0) + self.assertEqual(len(router.get_routes()), 0) + self.assertEqual(len(br.get_ip6_address(config.ADDRESS_TYPE.OMR)), 0) + self.assertEqual(len(router.get_ip6_address(config.ADDRESS_TYPE.OMR)), 0) - self.nodes[BR1].enable_ether() + br.enable_ether() # It takes around 10 seconds to start sending RA messages. self.simulator.go(15) self.collect_ipaddrs() - logging.info("BR1 addrs: %r", self.nodes[BR1].get_addrs()) - logging.info("ROUTER1 addrs: %r", self.nodes[ROUTER1].get_addrs()) - logging.info("HOST addrs: %r", self.nodes[HOST].get_addrs()) + logging.info("BR addrs: %r", br.get_addrs()) + logging.info("ROUTER addrs: %r", router.get_addrs()) + logging.info("HOST addrs: %r", host.get_addrs()) - self.assertTrue(len(self.nodes[BR1].get_prefixes()) == 1) - self.assertTrue(len(self.nodes[ROUTER1].get_prefixes()) == 1) - self.assertTrue(len(self.nodes[BR1].get_routes()) == 1) - self.assertTrue(len(self.nodes[ROUTER1].get_routes()) == 1) + self.assertEqual(len(br.get_prefixes()), 1) + self.assertEqual(len(router.get_prefixes()), 1) + self.assertEqual(len(br.get_routes()), 1) + self.assertEqual(len(router.get_routes()), 1) # The same local OMR and on-link prefix should be re-registered. - self.assertEqual(omr_prefix, self.nodes[BR1].get_prefixes()[0]) - self.assertEqual(omr_prefix, self.nodes[ROUTER1].get_prefixes()[0]) - self.assertEqual(external_route, self.nodes[BR1].get_routes()[0]) - self.assertEqual(external_route, self.nodes[ROUTER1].get_routes()[0]) + self.assertEqual(br.get_prefixes(), [omr_prefix]) + self.assertEqual(router.get_prefixes(), [omr_prefix]) + self.assertEqual(br.get_routes(), [external_route]) + self.assertEqual(router.get_routes(), [external_route]) - self.assertTrue(len(self.nodes[BR1].get_ip6_address(config.ADDRESS_TYPE.OMR)) == 1) - self.assertTrue(len(self.nodes[ROUTER1].get_ip6_address(config.ADDRESS_TYPE.OMR)) == 1) - self.assertTrue(len(self.nodes[HOST].get_ip6_address(config.ADDRESS_TYPE.ONLINK_ULA)) == 1) + self.assertEqual(len(br.get_ip6_address(config.ADDRESS_TYPE.OMR)), 1) + self.assertEqual(len(router.get_ip6_address(config.ADDRESS_TYPE.OMR)), 1) + self.assertEqual(len(host.get_ip6_address(config.ADDRESS_TYPE.ONLINK_ULA)), 1) - self.assertEqual(br1_omr_address, self.nodes[BR1].get_ip6_address(config.ADDRESS_TYPE.OMR)[0]) - self.assertEqual(router1_omr_address, self.nodes[ROUTER1].get_ip6_address(config.ADDRESS_TYPE.OMR)[0]) - self.assertEqual(host_ula_address, self.nodes[HOST].get_ip6_address(config.ADDRESS_TYPE.ONLINK_ULA)[0]) + self.assertEqual(br.get_ip6_address(config.ADDRESS_TYPE.OMR), [br1_omr_address]) + self.assertEqual(router.get_ip6_address(config.ADDRESS_TYPE.OMR), [router1_omr_address]) + self.assertEqual(host.get_ip6_address(config.ADDRESS_TYPE.ONLINK_ULA), [host_ula_address]) # Router1 can ping to/from the Host on infra link. - self.assertTrue(self.nodes[ROUTER1].ping(self.nodes[HOST].get_ip6_address(config.ADDRESS_TYPE.ONLINK_ULA)[0])) - self.assertTrue(self.nodes[HOST].ping(self.nodes[ROUTER1].get_ip6_address(config.ADDRESS_TYPE.OMR)[0], - backbone=True)) + self.assertTrue(router.ping(host.get_ip6_address(config.ADDRESS_TYPE.ONLINK_ULA)[0])) + self.assertTrue(host.ping(router.get_ip6_address(config.ADDRESS_TYPE.OMR)[0], backbone=True)) # # Case 5. Test if the linux host is still reachable if rejoin the network. # - self.nodes[HOST].disable_ether() + host.disable_ether() self.simulator.go(10) - self.nodes[HOST].enable_ether() + host.enable_ether() self.simulator.go(10) - self.assertTrue(self.nodes[ROUTER1].ping(self.nodes[HOST].get_ip6_address(config.ADDRESS_TYPE.ONLINK_ULA)[0])) - self.assertTrue(self.nodes[HOST].ping(self.nodes[ROUTER1].get_ip6_address(config.ADDRESS_TYPE.OMR)[0], - backbone=True)) + self.assertTrue(router.ping(host.get_ip6_address(config.ADDRESS_TYPE.ONLINK_ULA)[0])) + self.assertTrue(host.ping(router.get_ip6_address(config.ADDRESS_TYPE.OMR)[0], backbone=True)) + + # + # Case 6. Test if the Border Router will remove the on-link prefix when + # another RA daemon is started on the same infra interface. + # + + br.start_radvd_service(prefix=config.ONLINK_GUA_PREFIX, slaac=True) + self.simulator.go(5) + + self.assertEqual(len(br.get_routes()), 1) + self.assertTrue(br.get_routes()[0].startswith(config.ONLINK_GUA_PREFIX.split('::/')[0])) + self.assertEqual(len(router.get_routes()), 1) + self.assertTrue(router.get_routes()[0].startswith(config.ONLINK_GUA_PREFIX.split('::/')[0])) + + self.assertTrue(router.ping(host.get_ip6_address(config.ADDRESS_TYPE.ONLINK_GUA)[0])) + self.assertTrue(host.ping(router.get_ip6_address(config.ADDRESS_TYPE.OMR)[0], backbone=True)) if __name__ == '__main__': diff --git a/tests/scripts/thread-cert/config.py b/tests/scripts/thread-cert/config.py index eaff213fe..3b50c1598 100644 --- a/tests/scripts/thread-cert/config.py +++ b/tests/scripts/thread-cert/config.py @@ -78,7 +78,7 @@ ALL_NETWORK_BBRS_ADDRESS = 'ff32:40:fd00:db8:0:0:0:3' ALL_DOMAIN_BBRS_ADDRESS = 'ff32:40:fd00:7d03:7d03:7d03:0:3' ALL_DOMAIN_BBRS_ADDRESS_ALTER = 'ff32:40:fd00:7d04:7d04:7d04:0:3' -ONLINK_PREFIX = 'fd00:dead:face::/64' +ONLINK_GUA_PREFIX = '2021::/64' # Any address starts with 'fd' are considered on-link address. ONLINK_PREFIX_REGEX_PATTERN = '^fd' @@ -113,6 +113,7 @@ class ADDRESS_TYPE(Enum): BACKBONE_GUA = 'BACKBONE_GUA' OMR = 'OMR' ONLINK_ULA = 'ONLINK_ULA' + ONLINK_GUA = 'ONLINK_GUA' RSSI = { diff --git a/tests/scripts/thread-cert/node.py b/tests/scripts/thread-cert/node.py index afe0de5a9..f60b4da4c 100755 --- a/tests/scripts/thread-cert/node.py +++ b/tests/scripts/thread-cert/node.py @@ -2670,7 +2670,7 @@ class LinuxHost(): return resp_count def _getBackboneGua(self) -> Optional[str]: - for addr in self.get_addrs(): + for addr in self.get_ether_addrs(): if re.match(config.BACKBONE_PREFIX_REGEX_PATTERN, addr, re.I): return addr @@ -2680,12 +2680,19 @@ class LinuxHost(): """ Returns the ULA addresses autoconfigured on the infra link. """ addrs = [] - for addr in self.get_addrs(): + for addr in self.get_ether_addrs(): if re.match(config.ONLINK_PREFIX_REGEX_PATTERN, addr, re.I): addrs.append(addr) return addrs + def _getInfraGua(self) -> Optional[str]: + """ Returns the GUA addresses autoconfigured on the infra link. + """ + + gua_prefix = config.ONLINK_GUA_PREFIX.split('::/')[0] + return [addr for addr in self.get_ether_addrs() if addr.startswith(gua_prefix)] + def ping(self, *args, **kwargs): backbone = kwargs.pop('backbone', False) if backbone: @@ -2768,6 +2775,31 @@ class LinuxHost(): service['addresses'] = addresses return service if service['addresses'] else None + def start_radvd_service(self, prefix, slaac): + self.bash("""cat >/etc/radvd.conf < List[str]: return self.get_ether_addrs() @@ -2829,39 +2861,15 @@ class HostNode(LinuxHost, OtbrDocker): Returns: IPv6 address string. """ - assert address_type in [config.ADDRESS_TYPE.BACKBONE_GUA, config.ADDRESS_TYPE.ONLINK_ULA] if address_type == config.ADDRESS_TYPE.BACKBONE_GUA: return self._getBackboneGua() - if address_type == config.ADDRESS_TYPE.ONLINK_ULA: + elif address_type == config.ADDRESS_TYPE.ONLINK_ULA: return self._getInfraUla() + elif address_type == config.ADDRESS_TYPE.ONLINK_GUA: + return self._getInfraGua() else: - return None - - def _service_radvd_start(self, prefix, slaac): - self.bash("""cat >/etc/radvd.conf <