From 658491a1aa24b31611d4cca506b6b0d66f2ff423 Mon Sep 17 00:00:00 2001 From: Abtin Keshavarzian Date: Fri, 13 May 2022 12:56:16 -0700 Subject: [PATCH 01/80] [config] introduce `OT_THREAD_VERSION_1_3` (#7692) This commit adds `OT_THREAD_VERSION_1_3` constant (equal to `4`) and change=s the `OPENTHREAD_CONFIG_THREAD_VERSION` default value to be the 1.3 version number. It also updates the build switch option (Cmake and autoconf) to support 1.3 and use 1.3 as default version value (when not explicitly specified). --- CMakeLists.txt | 6 ++++-- examples/common-switches.mk | 4 +++- src/core/openthread-core-config.h | 4 +++- 3 files changed, 10 insertions(+), 4 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index f311fc921..642332f5e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -102,12 +102,14 @@ if(OT_PACKAGE_VERSION STREQUAL "") endif() message(STATUS "Package Version: ${OT_PACKAGE_VERSION}") -set(OT_THREAD_VERSION "1.2" CACHE STRING "Thread version chosen by the user at configure time") -set_property(CACHE OT_THREAD_VERSION PROPERTY STRINGS "1.1" "1.2") +set(OT_THREAD_VERSION "1.3" CACHE STRING "Thread version chosen by the user at configure time") +set_property(CACHE OT_THREAD_VERSION PROPERTY STRINGS "1.1" "1.2" "1.3") if(${OT_THREAD_VERSION} EQUAL "1.1") target_compile_definitions(ot-config INTERFACE "OPENTHREAD_CONFIG_THREAD_VERSION=OT_THREAD_VERSION_1_1") elseif(${OT_THREAD_VERSION} EQUAL "1.2") target_compile_definitions(ot-config INTERFACE "OPENTHREAD_CONFIG_THREAD_VERSION=OT_THREAD_VERSION_1_2") +elseif(${OT_THREAD_VERSION} EQUAL "1.3") + target_compile_definitions(ot-config INTERFACE "OPENTHREAD_CONFIG_THREAD_VERSION=OT_THREAD_VERSION_1_3") else() message(FATAL_ERROR "Thread version unknown: ${OT_THREAD_VERSION}") endif() diff --git a/examples/common-switches.mk b/examples/common-switches.mk index 2e947b0df..ac05ccd6c 100644 --- a/examples/common-switches.mk +++ b/examples/common-switches.mk @@ -86,7 +86,7 @@ SLAAC ?= 1 SNTP_CLIENT ?= 0 SRP_CLIENT ?= 0 SRP_SERVER ?= 0 -THREAD_VERSION ?= 1.2 +THREAD_VERSION ?= 1.3 TIME_SYNC ?= 0 TREL ?= 0 UDP_FORWARD ?= 0 @@ -333,6 +333,8 @@ ifeq ($(THREAD_VERSION),1.1) COMMONCFLAGS += -DOPENTHREAD_CONFIG_THREAD_VERSION=2 else ifeq ($(THREAD_VERSION),1.2) COMMONCFLAGS += -DOPENTHREAD_CONFIG_THREAD_VERSION=3 +else ifeq ($(THREAD_VERSION),1.3) +COMMONCFLAGS += -DOPENTHREAD_CONFIG_THREAD_VERSION=4 endif ifeq ($(TIME_SYNC),1) diff --git a/src/core/openthread-core-config.h b/src/core/openthread-core-config.h index 23d7015b7..da7e18565 100644 --- a/src/core/openthread-core-config.h +++ b/src/core/openthread-core-config.h @@ -37,8 +37,10 @@ #include #define OT_THREAD_VERSION_INVALID 0 + #define OT_THREAD_VERSION_1_1 2 #define OT_THREAD_VERSION_1_2 3 +#define OT_THREAD_VERSION_1_3 4 #define OPENTHREAD_CORE_CONFIG_H_IN @@ -51,7 +53,7 @@ #endif #ifndef OPENTHREAD_CONFIG_THREAD_VERSION -#define OPENTHREAD_CONFIG_THREAD_VERSION OT_THREAD_VERSION_1_2 +#define OPENTHREAD_CONFIG_THREAD_VERSION OT_THREAD_VERSION_1_3 #endif #include "config/announce_sender.h" From 0f8dc59f57b4e9248d882bb6d890345605d2bafa Mon Sep 17 00:00:00 2001 From: rretanubun Date: Fri, 13 May 2022 15:57:32 -0400 Subject: [PATCH 02/80] [routing-manager] use sizeof explicit type `Ip6::Icmp::Header` (#7693) While using sizeof(*icmp6Header) before it is initialized to something is not wrong, it is possible this will be flagged by some code checker tools. Lets check using direct type. --- src/core/border_router/routing_manager.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/border_router/routing_manager.cpp b/src/core/border_router/routing_manager.cpp index ef1e1ccd5..d42648192 100644 --- a/src/core/border_router/routing_manager.cpp +++ b/src/core/border_router/routing_manager.cpp @@ -340,7 +340,7 @@ void RoutingManager::RecvIcmp6Message(uint32_t aInfraIfIndex, VerifyOrExit(IsInitialized() && mIsRunning, error = kErrorDrop); VerifyOrExit(aInfraIfIndex == mInfraIfIndex, error = kErrorDrop); - VerifyOrExit(aBuffer != nullptr && aBufferLength >= sizeof(*icmp6Header), error = kErrorParse); + VerifyOrExit(aBuffer != nullptr && aBufferLength >= sizeof(Ip6::Icmp::Header), error = kErrorParse); icmp6Header = reinterpret_cast(aBuffer); From 512d0728895641061d25dbbc545f0256d3535865 Mon Sep 17 00:00:00 2001 From: Jonathan Hui Date: Fri, 13 May 2022 12:59:24 -0700 Subject: [PATCH 03/80] [tests] set `routereligible` to `False` for FED (#7694) --- .../thread-cert/v1_2_test_multicast_listener_registration.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/scripts/thread-cert/v1_2_test_multicast_listener_registration.py b/tests/scripts/thread-cert/v1_2_test_multicast_listener_registration.py index 33193b487..96cdd12f8 100755 --- a/tests/scripts/thread-cert/v1_2_test_multicast_listener_registration.py +++ b/tests/scripts/thread-cert/v1_2_test_multicast_listener_registration.py @@ -114,7 +114,7 @@ class TestMulticastListenerRegistration(thread_cert.TestCase): 'mode': 'rdn', 'version': '1.2', 'allowlist': [ROUTER_1_2], - 'router_upgrade_threshold': 0, + 'router_eligible': False, 'timeout': config.DEFAULT_CHILD_TIMEOUT, }, } From 8330e708d1c9556bc57540344e80b7e58fdcb2a9 Mon Sep 17 00:00:00 2001 From: Simon Lin Date: Tue, 17 May 2022 13:09:13 +0800 Subject: [PATCH 04/80] [routing-manager] prefer OMR prefix with higher Pref (#7688) This commit fixes the issue that the OMR prefix with higher Pref is not preferred. --- src/core/border_router/routing_manager.cpp | 19 +++++++++++-------- .../border_router/test_manual_omr_prefix.py | 12 ++++++++++-- 2 files changed, 21 insertions(+), 10 deletions(-) diff --git a/src/core/border_router/routing_manager.cpp b/src/core/border_router/routing_manager.cpp index d42648192..eabf9f4ac 100644 --- a/src/core/border_router/routing_manager.cpp +++ b/src/core/border_router/routing_manager.cpp @@ -405,8 +405,9 @@ void RoutingManager::EvaluateOmrPrefix(OmrPrefixArray &aNewOmrPrefixes) { NetworkData::Iterator iterator = NetworkData::kIteratorInit; NetworkData::OnMeshPrefixConfig onMeshPrefixConfig; - Ip6::Prefix * smallestOmrPrefix = nullptr; - Ip6::Prefix * publishedLocalOmrPrefix = nullptr; + Ip6::Prefix * electedOmrPrefix = nullptr; + signed int electedOmrPrefixPreference = 0; + Ip6::Prefix * publishedLocalOmrPrefix = nullptr; OT_ASSERT(mIsRunning); @@ -431,9 +432,11 @@ void RoutingManager::EvaluateOmrPrefix(OmrPrefixArray &aNewOmrPrefixes) continue; } - if (smallestOmrPrefix == nullptr || (prefix < *smallestOmrPrefix)) + if (electedOmrPrefix == nullptr || onMeshPrefixConfig.mPreference > electedOmrPrefixPreference || + (onMeshPrefixConfig.mPreference == electedOmrPrefixPreference && prefix < *electedOmrPrefix)) { - smallestOmrPrefix = aNewOmrPrefixes.Back(); + electedOmrPrefix = aNewOmrPrefixes.Back(); + electedOmrPrefixPreference = onMeshPrefixConfig.mPreference; } if (prefix == mLocalOmrPrefix) @@ -458,16 +461,16 @@ void RoutingManager::EvaluateOmrPrefix(OmrPrefixArray &aNewOmrPrefixes) } else { - OT_ASSERT(smallestOmrPrefix != nullptr); + OT_ASSERT(electedOmrPrefix != nullptr); - if (*smallestOmrPrefix == mLocalOmrPrefix) + if (*electedOmrPrefix == mLocalOmrPrefix) { IgnoreError(PublishLocalOmrPrefix()); } else if (IsOmrPrefixAddedToLocalNetworkData()) { - LogInfo("EvaluateOmrPrefix: There is already a smaller OMR prefix %s in the Thread network", - smallestOmrPrefix->ToString().AsCString()); + LogInfo("EvaluateOmrPrefix: There is already a preferred OMR prefix %s (pref=%d) in the Thread network", + electedOmrPrefix->ToString().AsCString(), electedOmrPrefixPreference); UnpublishLocalOmrPrefix(); diff --git a/tests/scripts/thread-cert/border_router/test_manual_omr_prefix.py b/tests/scripts/thread-cert/border_router/test_manual_omr_prefix.py index 62b1fd1a9..66f4cf2dd 100644 --- a/tests/scripts/thread-cert/border_router/test_manual_omr_prefix.py +++ b/tests/scripts/thread-cert/border_router/test_manual_omr_prefix.py @@ -79,21 +79,29 @@ class ManualOmrsPrefix(thread_cert.TestCase): # Add a smaller OMR prefix. Verify BR_1 withdraws its OMR prefix. self._test_manual_omr_prefix('2001::/64', 'paros', expect_withdraw=True) + # Add a bigger OMR prefix. Verify BR_1 does not withdraw its OMR prefix. + self._test_manual_omr_prefix('fdff:ffff:1::/64', 'paros', expect_withdraw=False, prf='med') + # Add a high preference bigger OMR prefix. Verify BR_1 withdraws its OMR prefix. + self._test_manual_omr_prefix('fdff:ffff:2::/64', 'paros', expect_withdraw=True, prf='high') # Add a smaller but invalid OMR prefix (P_on_mesh = 0). Verify BR_1 does not withdraw its OMR prefix. self._test_manual_omr_prefix('2002::/64', 'pars', expect_withdraw=False) # Add a smaller but invalid OMR prefix (P_stable = 0). Verify BR_1 does not withdraw its OMR prefix. self._test_manual_omr_prefix('2003::/64', 'paro', expect_withdraw=False) - def _test_manual_omr_prefix(self, prefix, flags, expect_withdraw): + def _test_manual_omr_prefix(self, prefix, flags, expect_withdraw, prf='med'): br1 = self.nodes[BR_1] br2 = self.nodes[BR_2] - br2.add_prefix(prefix, flags) + br2.add_prefix(prefix, flags, prf=prf) br2.register_netdata() self.simulator.go(10) + is_omr_prefix = ('a' in flags and 'o' in flags and 's' in flags and 'D' not in flags) + if expect_withdraw: self._assert_prefixes_equal(br1.get_netdata_omr_prefixes(), [prefix]) + elif is_omr_prefix: + self._assert_prefixes_equal(br1.get_netdata_omr_prefixes(), [br1.get_br_omr_prefix(), prefix]) else: self._assert_prefixes_equal(br1.get_netdata_omr_prefixes(), [br1.get_br_omr_prefix()]) From 282b85fa9cbed26bd9957e45cf0ac4eefb62c024 Mon Sep 17 00:00:00 2001 From: Jonathan Hui Date: Mon, 16 May 2022 22:12:08 -0700 Subject: [PATCH 05/80] [routing-manager] derive on-link prefix from Extended PAN ID (#7695) To align with latest Thread Specification. --- src/core/border_router/routing_manager.cpp | 58 +++++++++---------- src/core/border_router/routing_manager.hpp | 2 +- .../test_multi_border_routers.py | 13 +++-- .../test_single_border_router.py | 20 +++++++ .../test_vicarious_router_solicit.py | 4 ++ tests/scripts/thread-cert/thread_cert.py | 2 + 6 files changed, 63 insertions(+), 36 deletions(-) diff --git a/src/core/border_router/routing_manager.cpp b/src/core/border_router/routing_manager.cpp index eabf9f4ac..5f1884e38 100644 --- a/src/core/border_router/routing_manager.cpp +++ b/src/core/border_router/routing_manager.cpp @@ -47,6 +47,7 @@ #include "common/log.hpp" #include "common/random.hpp" #include "common/settings.hpp" +#include "meshcop/extended_panid.hpp" #include "net/ip6.hpp" #include "thread/network_data_leader.hpp" #include "thread/network_data_local.hpp" @@ -101,7 +102,7 @@ Error RoutingManager::Init(uint32_t aInfraIfIndex, bool aInfraIfIsRunning) #if OPENTHREAD_CONFIG_BORDER_ROUTING_NAT64_ENABLE GenerateNat64Prefix(); #endif - SuccessOrExit(error = LoadOrGenerateRandomOnLinkPrefix()); + GenerateOnLinkPrefix(); mInfraIfIndex = aInfraIfIndex; @@ -222,37 +223,18 @@ void RoutingManager::GenerateNat64Prefix(void) } #endif -Error RoutingManager::LoadOrGenerateRandomOnLinkPrefix(void) +void RoutingManager::GenerateOnLinkPrefix(void) { - Error error = kErrorNone; - bool generated = false; + MeshCoP::ExtendedPanId extPanId = Get().GetExtPanId(); - if (Get().Read(mLocalOnLinkPrefix) != kErrorNone || - !mLocalOnLinkPrefix.IsUniqueLocal()) - { - Ip6::NetworkPrefix randomOnLinkPrefix; + mLocalOnLinkPrefix.mPrefix.mFields.m8[0] = 0xfd; + // Global ID: 40 most significant bits of Extended PAN ID + memcpy(mLocalOnLinkPrefix.mPrefix.mFields.m8 + 1, extPanId.m8, 5); + // Subnet ID: 16 least significant bits of Extended PAN ID + memcpy(mLocalOnLinkPrefix.mPrefix.mFields.m8 + 6, extPanId.m8 + 6, 2); + mLocalOnLinkPrefix.SetLength(kOnLinkPrefixLength); - error = randomOnLinkPrefix.GenerateRandomUla(); - if (error != kErrorNone) - { - LogCrit("Failed to generate random on-link prefix"); - ExitNow(); - } - - mLocalOnLinkPrefix.Set(randomOnLinkPrefix); - mLocalOnLinkPrefix.SetSubnetId(0); - - IgnoreError(Get().Save(mLocalOnLinkPrefix)); - generated = true; - } - - OT_UNUSED_VARIABLE(generated); - - LogNote("Local on-link prefix: %s (%s)", mLocalOnLinkPrefix.ToString().AsCString(), - generated ? "generated" : "loaded"); - -exit: - return error; + LogNote("Local on-link prefix: %s", mLocalOnLinkPrefix.ToString().AsCString()); } void RoutingManager::EvaluateState(void) @@ -397,6 +379,24 @@ void RoutingManager::HandleNotifierEvents(Events aEvents) StartRoutingPolicyEvaluationJitter(kRoutingPolicyEvaluationJitter); } + if (aEvents.Contains(kEventThreadExtPanIdChanged)) + { + if (mIsAdvertisingLocalOnLinkPrefix) + { + RemoveExternalRoute(mLocalOnLinkPrefix); + // TODO: consider deprecating/invalidating existing + // on-link prefix + mIsAdvertisingLocalOnLinkPrefix = false; + } + + GenerateOnLinkPrefix(); + + if (mIsRunning) + { + StartRoutingPolicyEvaluationJitter(kRoutingPolicyEvaluationJitter); + } + } + exit: return; } diff --git a/src/core/border_router/routing_manager.hpp b/src/core/border_router/routing_manager.hpp index 0023e9e65..c601370ef 100644 --- a/src/core/border_router/routing_manager.hpp +++ b/src/core/border_router/routing_manager.hpp @@ -317,7 +317,7 @@ private: bool IsEnabled(void) const { return mIsEnabled; } Error LoadOrGenerateRandomBrUlaPrefix(void); void GenerateOmrPrefix(void); - Error LoadOrGenerateRandomOnLinkPrefix(void); + void GenerateOnLinkPrefix(void); const Ip6::Prefix *EvaluateOnLinkPrefix(void); diff --git a/tests/scripts/thread-cert/border_router/test_multi_border_routers.py b/tests/scripts/thread-cert/border_router/test_multi_border_routers.py index 73dc85f9c..5e4eeabd7 100755 --- a/tests/scripts/thread-cert/border_router/test_multi_border_routers.py +++ b/tests/scripts/thread-cert/border_router/test_multi_border_routers.py @@ -191,12 +191,13 @@ class MultiBorderRouters(thread_cert.TestCase): br2_omr_prefix = br2.get_br_omr_prefix() self.assertEqual(br2_omr_prefix, br2.get_netdata_omr_prefixes()[0]) - # Only BR2 will keep the route for BR1's on-link prefix - # and add route for on-link prefix of its own. - self.assertEqual(len(br1.get_netdata_non_nat64_prefixes()), 2) - self.assertEqual(len(router1.get_netdata_non_nat64_prefixes()), 2) - self.assertEqual(len(br2.get_netdata_non_nat64_prefixes()), 2) - self.assertEqual(len(router2.get_netdata_non_nat64_prefixes()), 2) + # There should be no changes to the external route for the + # on-link prefix, given that the on-link prefix is derived + # from the Extended PAN ID. + self.assertEqual(len(br1.get_netdata_non_nat64_prefixes()), 1) + self.assertEqual(len(router1.get_netdata_non_nat64_prefixes()), 1) + self.assertEqual(len(br2.get_netdata_non_nat64_prefixes()), 1) + self.assertEqual(len(router2.get_netdata_non_nat64_prefixes()), 1) br2_on_link_prefix = br2.get_br_on_link_prefix() self.assertEqual(set(map(IPv6Network, br2.get_netdata_non_nat64_prefixes())), 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 24ba05f43..4776fefbe 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 @@ -28,6 +28,7 @@ # import logging import unittest +from ipaddress import IPv6Network import config import thread_cert @@ -326,6 +327,25 @@ class SingleBorderRouter(thread_cert.TestCase): 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 7. Test if Border Router changes on-link prefix when + # Extended PAN ID changes. + # + + prefixA = br.get_br_on_link_prefix() + + router.commissioner_start() + self.simulator.go(5) + + router.send_mgmt_active_set( + active_timestamp=100, + extended_panid='0001020304050607', + ) + self.simulator.go(10) + + prefixB = br.get_br_on_link_prefix() + self.assertNotEqual(IPv6Network(prefixA), IPv6Network(prefixB)) + if __name__ == '__main__': unittest.main() diff --git a/tests/scripts/thread-cert/border_router/test_vicarious_router_solicit.py b/tests/scripts/thread-cert/border_router/test_vicarious_router_solicit.py index 7c09f9601..5c1e55847 100755 --- a/tests/scripts/thread-cert/border_router/test_vicarious_router_solicit.py +++ b/tests/scripts/thread-cert/border_router/test_vicarious_router_solicit.py @@ -67,6 +67,7 @@ class MultiThreadNetworks(thread_cert.TestCase): 'version': '1.2', 'channel': CHANNEL1, 'router_selection_jitter': 1, + 'extended_panid': '0001020304050607' }, ROUTER1: { 'name': 'Router_1', @@ -74,6 +75,7 @@ class MultiThreadNetworks(thread_cert.TestCase): 'version': '1.2', 'channel': CHANNEL1, 'router_selection_jitter': 1, + 'extended_panid': '0001020304050607' }, BR2: { 'name': 'BR_2', @@ -82,6 +84,7 @@ class MultiThreadNetworks(thread_cert.TestCase): 'version': '1.2', 'channel': CHANNEL2, 'router_selection_jitter': 1, + 'extended_panid': '08090a0b0c0d0e0f' }, ROUTER2: { 'name': 'Router_2', @@ -89,6 +92,7 @@ class MultiThreadNetworks(thread_cert.TestCase): 'version': '1.2', 'channel': CHANNEL2, 'router_selection_jitter': 1, + 'extended_panid': '08090a0b0c0d0e0f' }, HOST: { 'name': 'Host', diff --git a/tests/scripts/thread-cert/thread_cert.py b/tests/scripts/thread-cert/thread_cert.py index 0dacebd12..fdbdacec4 100755 --- a/tests/scripts/thread-cert/thread_cert.py +++ b/tests/scripts/thread-cert/thread_cert.py @@ -183,6 +183,8 @@ class TestCase(NcpSupportMixin, unittest.TestCase): self.nodes[i].set_panid(params['panid']) self.nodes[i].set_mode(params['mode']) + if 'extended_panid' in params: + self.nodes[i].set_extpanid(params['extended_panid']) if 'partition_id' in params: self.nodes[i].set_preferred_partition_id(params['partition_id']) if 'channel' in params: From 50862769b9fd6f59f5ac198cf6ff90fc4f289632 Mon Sep 17 00:00:00 2001 From: whd <7058128+superwhd@users.noreply.github.com> Date: Wed, 18 May 2022 01:24:17 +0800 Subject: [PATCH 06/80] [srp-server] add SRP server APIs for telemetry (#7680) --- include/openthread/instance.h | 2 +- include/openthread/srp_server.h | 63 +++++++++++++++++++++++++++++++++ src/core/api/srp_server_api.cpp | 25 +++++++++++++ src/core/net/srp_server.cpp | 53 +++++++++++++++++++++++++++ src/core/net/srp_server.hpp | 60 +++++++++++++++++++++++++++++++ 5 files changed, 202 insertions(+), 1 deletion(-) diff --git a/include/openthread/instance.h b/include/openthread/instance.h index 1a45a97e2..f917e7f77 100644 --- a/include/openthread/instance.h +++ b/include/openthread/instance.h @@ -53,7 +53,7 @@ extern "C" { * @note This number versions both OpenThread platform and user APIs. * */ -#define OPENTHREAD_API_VERSION (207) +#define OPENTHREAD_API_VERSION (208) /** * @addtogroup api-instance diff --git a/include/openthread/srp_server.h b/include/openthread/srp_server.h index 1eceb2b4d..c9fd220e8 100644 --- a/include/openthread/srp_server.h +++ b/include/openthread/srp_server.h @@ -167,6 +167,32 @@ typedef struct otSrpServerLeaseConfig uint32_t mMaxKeyLease; ///< The maximum KEY-LEASE interval in seconds. } otSrpServerLeaseConfig; +/** + * This structure includes SRP server lease information of a host/service. + * + */ +typedef struct otSrpServerLeaseInfo +{ + uint32_t mLease; ///< The lease time of a host/service in milliseconds. + uint32_t mKeyLease; ///< The key lease time of a host/service in milliseconds. + uint32_t mRemainingLease; ///< The remaining lease time of the host/service in milliseconds. + uint32_t mRemainingKeyLease; ///< The remaining key lease time of a host/service in milliseconds. +} otSrpServerLeaseInfo; + +/** + * This structure includes the statistics of SRP server responses. + * + */ +typedef struct otSrpServerResponseCounters +{ + uint32_t mSuccess; ///< The number of successful responses. + uint32_t mServerFailure; ///< The number of server failure responses. + uint32_t mFormatError; ///< The number of format error responses. + uint32_t mNameExists; ///< The number of 'name exists' responses. + uint32_t mRefused; ///< The number of refused responses. + uint32_t mOther; ///< The number of other responses. +} otSrpServerResponseCounters; + /** * This function returns the domain authorized to the SRP server. * @@ -207,6 +233,16 @@ otError otSrpServerSetDomain(otInstance *aInstance, const char *aDomain); */ otSrpServerState otSrpServerGetState(otInstance *aInstance); +/** + * This function returns the port the SRP server is listening to. + * + * @param[in] aInstance A pointer to an OpenThread instance. + * + * @returns The port of the SRP server. It returns 0 if the server is not running. + * + */ +uint16_t otSrpServerGetPort(otInstance *aInstance); + /** * This function returns the address mode being used by the SRP server. * @@ -365,6 +401,16 @@ void otSrpServerHandleServiceUpdateResult(otInstance *aInstance, otSrpServerServ */ const otSrpServerHost *otSrpServerGetNextHost(otInstance *aInstance, const otSrpServerHost *aHost); +/** + * This function returns the response counters of the SRP server. + * + * @param[in] aInstance A pointer to an OpenThread instance. + * + * @returns A pointer to the response counters of the SRP server. + * + */ +const otSrpServerResponseCounters *otSrpServerGetResponseCounters(otInstance *aInstance); + /** * This function tells if the SRP service host has been deleted. * @@ -399,6 +445,15 @@ const char *otSrpServerHostGetFullName(const otSrpServerHost *aHost); */ const otIp6Address *otSrpServerHostGetAddresses(const otSrpServerHost *aHost, uint8_t *aAddressesNum); +/** + * This function returns the LEASE and KEY-LEASE information of a given host. + * + * @param[in] aHost A pointer to the SRP server host. + * @param[out] aLeaseInfo A pointer to where to output the LEASE and KEY-LEASE information. + * + */ +void otSrpServerHostGetLeaseInfo(const otSrpServerHost *aHost, otSrpServerLeaseInfo *aLeaseInfo); + /** * This function returns the next service (excluding any sub-type services) of given host. * @@ -583,6 +638,14 @@ const uint8_t *otSrpServerServiceGetTxtData(const otSrpServerService *aService, */ const otSrpServerHost *otSrpServerServiceGetHost(const otSrpServerService *aService); +/** + * This function returns the LEASE and KEY-LEASE information of a given service. + * + * @param[in] aService A pointer to the SRP server service. + * @param[out] aLeaseInfo A pointer to where to output the LEASE and KEY-LEASE information. + * + */ +void otSrpServerServiceGetLeaseInfo(const otSrpServerService *aService, otSrpServerLeaseInfo *aLeaseInfo); /** * @} * diff --git a/src/core/api/srp_server_api.cpp b/src/core/api/srp_server_api.cpp index 8c59319a6..950f25e3b 100644 --- a/src/core/api/srp_server_api.cpp +++ b/src/core/api/srp_server_api.cpp @@ -57,6 +57,11 @@ otSrpServerState otSrpServerGetState(otInstance *aInstance) return MapEnum(AsCoreType(aInstance).Get().GetState()); } +uint16_t otSrpServerGetPort(otInstance *aInstance) +{ + return AsCoreType(aInstance).Get().GetPort(); +} + otSrpServerAddressMode otSrpServerGetAddressMode(otInstance *aInstance) { return MapEnum(AsCoreType(aInstance).Get().GetAddressMode()); @@ -109,6 +114,11 @@ const otSrpServerHost *otSrpServerGetNextHost(otInstance *aInstance, const otSrp return AsCoreType(aInstance).Get().GetNextHost(AsCoreTypePtr(aHost)); } +const otSrpServerResponseCounters *otSrpServerGetResponseCounters(otInstance *aInstance) +{ + return AsCoreType(aInstance).Get().GetResponseCounters(); +} + bool otSrpServerHostIsDeleted(const otSrpServerHost *aHost) { return AsCoreType(aHost).IsDeleted(); @@ -124,6 +134,16 @@ const otIp6Address *otSrpServerHostGetAddresses(const otSrpServerHost *aHost, ui return AsCoreType(aHost).GetAddresses(*aAddressesNum); } +void otSrpServerHostGetLeaseInfo(const otSrpServerHost *aHost, otSrpServerLeaseInfo *aLeaseInfo) +{ + AsCoreType(aHost).GetLeaseInfo(*aLeaseInfo); +} + +uint32_t otSrpServerHostGetKeyLease(const otSrpServerHost *aHost) +{ + return AsCoreType(aHost).GetKeyLease(); +} + const otSrpServerService *otSrpServerHostGetNextService(const otSrpServerHost * aHost, const otSrpServerService *aService) { @@ -196,4 +216,9 @@ const otSrpServerHost *otSrpServerServiceGetHost(const otSrpServerService *aServ return &AsCoreType(aService).GetHost(); } +void otSrpServerServiceGetLeaseInfo(const otSrpServerService *aService, otSrpServerLeaseInfo *aLeaseInfo) +{ + AsCoreType(aService).GetLeaseInfo(*aLeaseInfo); +} + #endif // OPENTHREAD_CONFIG_SRP_SERVER_ENABLE diff --git a/src/core/net/srp_server.cpp b/src/core/net/srp_server.cpp index b3d59f555..9621ff9e3 100644 --- a/src/core/net/srp_server.cpp +++ b/src/core/net/srp_server.cpp @@ -1236,6 +1236,8 @@ void Server::SendResponse(const Dns::UpdateHeader & aHeader, LogInfo("Send success response"); } + UpdateResponseCounters(aResponseCode); + exit: if (error != kErrorNone) { @@ -1283,6 +1285,8 @@ void Server::SendResponse(const Dns::UpdateHeader &aHeader, LogInfo("Send success response with granted lease: %u and key lease: %u", aLease, aKeyLease); + UpdateResponseCounters(Dns::UpdateHeader::kResponseSuccess); + exit: if (error != kErrorNone) { @@ -1482,6 +1486,31 @@ const char *Server::AddressModeToString(AddressMode aMode) return kAddressModeStrings[aMode]; } +void Server::UpdateResponseCounters(Dns::UpdateHeader::Response aResponseCode) +{ + switch (aResponseCode) + { + case Dns::UpdateHeader::kResponseSuccess: + ++mResponseCounters.mSuccess; + break; + case Dns::UpdateHeader::kResponseServerFailure: + ++mResponseCounters.mServerFailure; + break; + case Dns::UpdateHeader::kResponseFormatError: + ++mResponseCounters.mFormatError; + break; + case Dns::UpdateHeader::kResponseNameExists: + ++mResponseCounters.mNameExists; + break; + case Dns::UpdateHeader::kResponseRefused: + ++mResponseCounters.mRefused; + break; + default: + ++mResponseCounters.mOther; + break; + } +} + //--------------------------------------------------------------------------------------------------------------------- // Server::Service @@ -1540,6 +1569,18 @@ TimeMilli Server::Service::GetKeyExpireTime(void) const return mUpdateTime + Time::SecToMsec(mDescription->mKeyLease); } +void Server::Service::GetLeaseInfo(LeaseInfo &aLeaseInfo) const +{ + TimeMilli now = TimerMilli::GetNow(); + TimeMilli expireTime = GetExpireTime(); + TimeMilli keyExpireTime = GetKeyExpireTime(); + + aLeaseInfo.mLease = Time::SecToMsec(GetLease()); + aLeaseInfo.mKeyLease = Time::SecToMsec(GetKeyLease()); + aLeaseInfo.mRemainingLease = (now <= expireTime) ? (expireTime - now) : 0; + aLeaseInfo.mRemainingKeyLease = (now <= keyExpireTime) ? (keyExpireTime - now) : 0; +} + bool Server::Service::MatchesInstanceName(const char *aInstanceName) const { return StringMatch(mDescription->mInstanceName.AsCString(), aInstanceName, kStringCaseInsensitiveMatch); @@ -1739,6 +1780,18 @@ TimeMilli Server::Host::GetKeyExpireTime(void) const return mUpdateTime + Time::SecToMsec(mKeyLease); } +void Server::Host::GetLeaseInfo(LeaseInfo &aLeaseInfo) const +{ + TimeMilli now = TimerMilli::GetNow(); + TimeMilli expireTime = GetExpireTime(); + TimeMilli keyExpireTime = GetKeyExpireTime(); + + aLeaseInfo.mLease = Time::SecToMsec(GetLease()); + aLeaseInfo.mKeyLease = Time::SecToMsec(GetKeyLease()); + aLeaseInfo.mRemainingLease = (now <= expireTime) ? (expireTime - now) : 0; + aLeaseInfo.mRemainingKeyLease = (now <= keyExpireTime) ? (keyExpireTime - now) : 0; +} + const Server::Service *Server::Host::FindNextService(const Service *aPrevService, Service::Flags aFlags, const char * aServiceName, diff --git a/src/core/net/srp_server.hpp b/src/core/net/srp_server.hpp index c74eb07be..6289f3a2a 100644 --- a/src/core/net/srp_server.hpp +++ b/src/core/net/srp_server.hpp @@ -129,6 +129,12 @@ public: */ typedef otSrpServerServiceUpdateId ServiceUpdateId; + /** + * The SRP server lease information of a host/service. + * + */ + typedef otSrpServerLeaseInfo LeaseInfo; + /** * This enumeration represents the address mode used by the SRP server. * @@ -301,6 +307,22 @@ public: */ const Host &GetHost(void) const { return *mDescription->mHost; } + /** + * This method returns the LEASE time of the service. + * + * @returns The LEASE time in seconds. + * + */ + uint32_t GetLease(void) const { return mDescription->mLease; } + + /** + * This method returns the KEY-LEASE time of the key of the service. + * + * @returns The KEY-LEASE time in seconds. + * + */ + uint32_t GetKeyLease(void) const { return mDescription->mKeyLease; } + /** * This method returns the expire time (in milliseconds) of the service. * @@ -317,6 +339,15 @@ public: */ TimeMilli GetKeyExpireTime(void) const; + /** + * This method gets the LEASE and KEY-LEASE information of a given service. + * + * @param[out] aLeaseInfo A reference to a LeaseInfo instance. It contains the LEASE time, KEY-LEASE time, + * remaining LEASE time and the remaining KEY-LEASE time. + * + */ + void GetLeaseInfo(LeaseInfo &aLeaseInfo) const; + /** * This method indicates whether this service matches a given service instance name. * @@ -452,6 +483,15 @@ public: */ uint32_t GetKeyLease(void) const { return mKeyLease; } + /** + * This method gets the LEASE and KEY-LEASE information of a given host. + * + * @param[out] aLeaseInfo A reference to a LeaseInfo instance. It contains the LEASE time, KEY-LEASE time, + * remaining LEASE time and the remaining KEY-LEASE time. + * + */ + void GetLeaseInfo(LeaseInfo &aLeaseInfo) const; + /** * This method returns the KEY resource record of the host. * @@ -704,6 +744,14 @@ public: */ State GetState(void) const { return mState; } + /** + * This method tells the port the SRP server is listening to. + * + * @returns An integer that represents the port of the server. It returns 0 if the SRP server is not running. + * + */ + uint16_t GetPort(void) const { return IsRunning() ? mPort : 0; } + /** * This method enables/disables the SRP server. * @@ -745,6 +793,14 @@ public: */ const Host *GetNextHost(const Host *aHost); + /** + * This method returns the response counters of the SRP server. + * + * @returns A pointer to the response counters of the SRP server. + * + */ + const otSrpServerResponseCounters *GetResponseCounters(void) const { return &mResponseCounters; } + /** * This method receives the service update result from service handler set by * SetServiceHandler. @@ -886,6 +942,8 @@ private: const UpdateMetadata *FindOutstandingUpdate(const MessageMetadata &aMessageMetadata) const; static const char * AddressModeToString(AddressMode aMode); + void UpdateResponseCounters(Dns::Header::Response aResponseCode); + Ip6::Udp::Socket mSocket; otSrpServerServiceUpdateHandler mServiceUpdateHandler; void * mServiceUpdateHandlerContext; @@ -906,6 +964,8 @@ private: AddressMode mAddressMode; uint8_t mAnycastSequenceNumber; bool mHasRegisteredAnyService : 1; + + otSrpServerResponseCounters mResponseCounters; }; } // namespace Srp From 3f47516ce868aa7f26f99ae7b8d34c2ff44e5813 Mon Sep 17 00:00:00 2001 From: Sarah <91494325+sarah-iot@users.noreply.github.com> Date: Tue, 17 May 2022 10:29:16 -0700 Subject: [PATCH 07/80] [docs] add Doxygen group for new TCP Abstractions API (#7647) --- doc/ot_api_doc.h | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/doc/ot_api_doc.h b/doc/ot_api_doc.h index f0762eb0c..b231833e2 100644 --- a/doc/ot_api_doc.h +++ b/doc/ot_api_doc.h @@ -59,7 +59,16 @@ * @defgroup api-ip6 IPv6 * @defgroup api-srp SRP * @defgroup api-ping-sender Ping Sender + * + * @defgroup api-tcp-group TCP + * + * @{ + * * @defgroup api-tcp TCP + * @defgroup api-tcp-ext TCP Abstractions + * + * @} + * * @defgroup api-udp-group UDP * * @{ From cb41668c0cb0ad8138ab866cc83181c8f0c25edd Mon Sep 17 00:00:00 2001 From: Abtin Keshavarzian Date: Tue, 17 May 2022 11:25:41 -0700 Subject: [PATCH 08/80] [mle] add `Tx/RxMessage` providing `Append/Read{Tlv}` methods (#7689) This commit adds new `Mle::TxMessage` and `Mle::RxMessage` classes (as sub-classes of `Message`). `TxMessage` represents an MLE message to be sent out providing `Append{Some}Tlv()` methods and `RxMessage` represent a received MLE message providing helper methods `Read {Some}Tlv()`. --- src/core/thread/discover_scanner.cpp | 4 +- src/core/thread/mle.cpp | 352 ++++++------ src/core/thread/mle.hpp | 815 ++++++++++++++------------- src/core/thread/mle_router.cpp | 275 ++++----- src/core/thread/mle_router.hpp | 5 - 5 files changed, 737 insertions(+), 714 deletions(-) diff --git a/src/core/thread/discover_scanner.cpp b/src/core/thread/discover_scanner.cpp index e652afe58..ff0b6f899 100644 --- a/src/core/thread/discover_scanner.cpp +++ b/src/core/thread/discover_scanner.cpp @@ -67,7 +67,7 @@ Error DiscoverScanner::Discover(const Mac::ChannelMask &aScanChannels, void * aContext) { Error error = kErrorNone; - Message * message = nullptr; + Mle::TxMessage * message = nullptr; Tlv tlv; Ip6::Address destination; MeshCoP::DiscoveryRequestTlv discoveryRequest; @@ -136,7 +136,7 @@ Error DiscoverScanner::Discover(const Mac::ChannelMask &aScanChannels, destination.SetToLinkLocalAllRoutersMulticast(); - SuccessOrExit(error = Get().SendMessage(*message, destination)); + SuccessOrExit(error = message->SendTo(destination)); if ((aPanId == Mac::kPanIdBroadcast) && (Get().GetPanId() == Mac::kPanIdBroadcast)) { diff --git a/src/core/thread/mle.cpp b/src/core/thread/mle.cpp index 619171f6e..8e3a646cb 100644 --- a/src/core/thread/mle.cpp +++ b/src/core/thread/mle.cpp @@ -1031,15 +1031,15 @@ const LeaderData &Mle::GetLeaderData(void) return mLeaderData; } -Message *Mle::NewMleMessage(Command aCommand) +Mle::TxMessage *Mle::NewMleMessage(Command aCommand) { Error error = kErrorNone; - Message * message; + TxMessage * message; Message::Settings settings(Message::kNoLinkSecurity, Message::kPriorityNet); Message::SubType subType; uint8_t securitySuite; - message = mSocket.NewMessage(0, settings); + message = static_cast(mSocket.NewMessage(0, settings)); VerifyOrExit(message != nullptr, error = kErrorNoBufs); securitySuite = k154Security; @@ -1090,7 +1090,7 @@ Message *Mle::NewMleMessage(Command aCommand) SecurityHeader securityHeader; // The other fields in security header are updated in the - // message in `SendMessage()` before message is sent. + // message in `TxMessage::SendTo()` before message is sent. securityHeader.InitSecurityControl(); SuccessOrExit(error = message->Append(securityHeader)); @@ -1103,48 +1103,48 @@ exit: return message; } -Error Mle::AppendSourceAddress(Message &aMessage) const +Error Mle::TxMessage::AppendSourceAddressTlv(void) { - return Tlv::Append(aMessage, GetRloc16()); + return Tlv::Append(*this, Get().GetRloc16()); } -Error Mle::AppendStatus(Message &aMessage, StatusTlv::Status aStatus) +Error Mle::TxMessage::AppendStatusTlv(StatusTlv::Status aStatus) { - return Tlv::Append(aMessage, aStatus); + return Tlv::Append(*this, aStatus); } -Error Mle::AppendMode(Message &aMessage, DeviceMode aMode) +Error Mle::TxMessage::AppendModeTlv(DeviceMode aMode) { - return Tlv::Append(aMessage, aMode.Get()); + return Tlv::Append(*this, aMode.Get()); } -Error Mle::AppendTimeout(Message &aMessage, uint32_t aTimeout) +Error Mle::TxMessage::AppendTimeoutTlv(uint32_t aTimeout) { - return Tlv::Append(aMessage, aTimeout); + return Tlv::Append(*this, aTimeout); } -Error Mle::AppendChallenge(Message &aMessage, const Challenge &aChallenge) +Error Mle::TxMessage::AppendChallengeTlv(const Challenge &aChallenge) { - return Tlv::Append(aMessage, aChallenge.mBuffer, aChallenge.mLength); + return Tlv::Append(*this, aChallenge.mBuffer, aChallenge.mLength); } -Error Mle::AppendChallenge(Message &aMessage, const uint8_t *aChallenge, uint8_t aChallengeLength) +Error Mle::TxMessage::AppendChallengeTlv(const uint8_t *aChallenge, uint8_t aChallengeLength) { - return Tlv::Append(aMessage, aChallenge, aChallengeLength); + return Tlv::Append(*this, aChallenge, aChallengeLength); } -Error Mle::AppendResponse(Message &aMessage, const Challenge &aResponse) +Error Mle::TxMessage::AppendResponseTlv(const Challenge &aResponse) { - return Tlv::Append(aMessage, aResponse.mBuffer, aResponse.mLength); + return Tlv::Append(*this, aResponse.mBuffer, aResponse.mLength); } -Error Mle::ReadChallengeOrResponse(const Message &aMessage, uint8_t aTlvType, Challenge &aBuffer) +Error Mle::RxMessage::ReadChallengeOrResponse(uint8_t aTlvType, Challenge &aBuffer) const { Error error; uint16_t offset; uint16_t length; - SuccessOrExit(error = Tlv::FindTlvValueOffset(aMessage, aTlvType, offset, length)); + SuccessOrExit(error = Tlv::FindTlvValueOffset(*this, aTlvType, offset, length)); VerifyOrExit(length >= kMinChallengeSize, error = kErrorParse); if (length > kMaxChallengeSize) @@ -1152,24 +1152,24 @@ Error Mle::ReadChallengeOrResponse(const Message &aMessage, uint8_t aTlvType, Ch length = kMaxChallengeSize; } - aMessage.ReadBytes(offset, aBuffer.mBuffer, length); + ReadBytes(offset, aBuffer.mBuffer, length); aBuffer.mLength = static_cast(length); exit: return error; } -Error Mle::ReadChallenge(const Message &aMessage, Challenge &aChallenge) +Error Mle::RxMessage::ReadChallengeTlv(Challenge &aChallenge) const { - return ReadChallengeOrResponse(aMessage, Tlv::kChallenge, aChallenge); + return ReadChallengeOrResponse(Tlv::kChallenge, aChallenge); } -Error Mle::ReadResponse(const Message &aMessage, Challenge &aResponse) +Error Mle::RxMessage::ReadResponseTlv(Challenge &aResponse) const { - return ReadChallengeOrResponse(aMessage, Tlv::kResponse, aResponse); + return ReadChallengeOrResponse(Tlv::kResponse, aResponse); } -Error Mle::AppendLinkFrameCounter(Message &aMessage) +Error Mle::TxMessage::AppendLinkFrameCounterTlv(void) { uint32_t counter; @@ -1184,21 +1184,21 @@ Error Mle::AppendLinkFrameCounter(Message &aMessage) Get().SetAllMacFrameCounters(counter); #endif - return Tlv::Append(aMessage, counter); + return Tlv::Append(*this, counter); } -Error Mle::AppendMleFrameCounter(Message &aMessage) +Error Mle::TxMessage::AppendMleFrameCounterTlv(void) { - return Tlv::Append(aMessage, Get().GetMleFrameCounter()); + return Tlv::Append(*this, Get().GetMleFrameCounter()); } -Error Mle::ReadFrameCounters(const Message &aMessage, uint32_t &aLinkFrameCounter, uint32_t &aMleFrameCounter) const +Error Mle::RxMessage::ReadFrameCounterTlvs(uint32_t &aLinkFrameCounter, uint32_t &aMleFrameCounter) const { Error error; - SuccessOrExit(error = Tlv::Find(aMessage, aLinkFrameCounter)); + SuccessOrExit(error = Tlv::Find(*this, aLinkFrameCounter)); - switch (Tlv::Find(aMessage, aMleFrameCounter)) + switch (Tlv::Find(*this, aMleFrameCounter)) { case kErrorNone: break; @@ -1214,30 +1214,30 @@ exit: return error; } -Error Mle::AppendAddress16(Message &aMessage, uint16_t aRloc16) +Error Mle::TxMessage::AppendAddress16Tlv(uint16_t aRloc16) { - return Tlv::Append(aMessage, aRloc16); + return Tlv::Append(*this, aRloc16); } -Error Mle::AppendLeaderData(Message &aMessage) +Error Mle::TxMessage::AppendLeaderDataTlv(void) { LeaderDataTlv leaderDataTlv; - mLeaderData.SetDataVersion(Get().GetVersion(NetworkData::kFullSet)); - mLeaderData.SetStableDataVersion(Get().GetVersion(NetworkData::kStableSubset)); + Get().mLeaderData.SetDataVersion(Get().GetVersion(NetworkData::kFullSet)); + Get().mLeaderData.SetStableDataVersion(Get().GetVersion(NetworkData::kStableSubset)); leaderDataTlv.Init(); - leaderDataTlv.Set(mLeaderData); + leaderDataTlv.Set(Get().mLeaderData); - return leaderDataTlv.AppendTo(aMessage); + return leaderDataTlv.AppendTo(*this); } -Error Mle::ReadLeaderData(const Message &aMessage, LeaderData &aLeaderData) +Error Mle::RxMessage::ReadLeaderDataTlv(LeaderData &aLeaderData) const { Error error; LeaderDataTlv leaderDataTlv; - SuccessOrExit(error = Tlv::FindTlv(aMessage, leaderDataTlv)); + SuccessOrExit(error = Tlv::FindTlv(*this, leaderDataTlv)); VerifyOrExit(leaderDataTlv.IsValid(), error = kErrorParse); leaderDataTlv.Get(aLeaderData); @@ -1245,61 +1245,61 @@ exit: return error; } -Error Mle::AppendNetworkData(Message &aMessage, NetworkData::Type aType) +Error Mle::TxMessage::AppendNetworkDataTlv(NetworkData::Type aType) { Error error = kErrorNone; uint8_t networkData[NetworkData::NetworkData::kMaxSize]; uint8_t length; - VerifyOrExit(!mRetrieveNewNetworkData, error = kErrorInvalidState); + VerifyOrExit(!Get().mRetrieveNewNetworkData, error = kErrorInvalidState); length = sizeof(networkData); IgnoreError(Get().CopyNetworkData(aType, networkData, length)); - error = Tlv::Append(aMessage, networkData, length); + error = Tlv::Append(*this, networkData, length); exit: return error; } -Error Mle::AppendTlvRequest(Message &aMessage, const uint8_t *aTlvs, uint8_t aTlvsLength) +Error Mle::TxMessage::AppendTlvRequestTlv(const uint8_t *aTlvs, uint8_t aTlvsLength) { - return Tlv::Append(aMessage, aTlvs, aTlvsLength); + return Tlv::Append(*this, aTlvs, aTlvsLength); } -Error Mle::FindTlvRequest(const Message &aMessage, RequestedTlvs &aRequestedTlvs) +Error Mle::RxMessage::ReadTlvRequestTlv(RequestedTlvs &aRequestedTlvs) const { Error error; uint16_t offset; uint16_t length; - SuccessOrExit(error = Tlv::FindTlvValueOffset(aMessage, Tlv::kTlvRequest, offset, length)); + SuccessOrExit(error = Tlv::FindTlvValueOffset(*this, Tlv::kTlvRequest, offset, length)); if (length > sizeof(aRequestedTlvs.mTlvs)) { length = sizeof(aRequestedTlvs.mTlvs); } - aMessage.ReadBytes(offset, aRequestedTlvs.mTlvs, length); + ReadBytes(offset, aRequestedTlvs.mTlvs, length); aRequestedTlvs.mNumTlvs = static_cast(length); exit: return error; } -Error Mle::AppendScanMask(Message &aMessage, uint8_t aScanMask) +Error Mle::TxMessage::AppendScanMaskTlv(uint8_t aScanMask) { - return Tlv::Append(aMessage, aScanMask); + return Tlv::Append(*this, aScanMask); } -Error Mle::AppendLinkMargin(Message &aMessage, uint8_t aLinkMargin) +Error Mle::TxMessage::AppendLinkMarginTlv(uint8_t aLinkMargin) { - return Tlv::Append(aMessage, aLinkMargin); + return Tlv::Append(*this, aLinkMargin); } -Error Mle::AppendVersion(Message &aMessage) +Error Mle::TxMessage::AppendVersionTlv(void) { - return Tlv::Append(aMessage, kThreadVersion); + return Tlv::Append(*this, kThreadVersion); } bool Mle::HasUnregisteredAddress(void) @@ -1330,7 +1330,7 @@ exit: return retval; } -Error Mle::AppendAddressRegistration(Message &aMessage, AddressRegistrationMode aMode) +Error Mle::TxMessage::AppendAddressRegistrationTlv(AddressRegistrationMode aMode) { Error error = kErrorNone; Tlv tlv; @@ -1338,18 +1338,18 @@ Error Mle::AppendAddressRegistration(Message &aMessage, AddressRegistrationMode Lowpan::Context context; uint8_t length = 0; uint8_t counter = 0; - uint16_t startOffset = aMessage.GetLength(); + uint16_t startOffset = GetLength(); #if OPENTHREAD_CONFIG_DUA_ENABLE Ip6::Address domainUnicastAddress; #endif tlv.SetType(Tlv::kAddressRegistration); - SuccessOrExit(error = aMessage.Append(tlv)); + SuccessOrExit(error = Append(tlv)); // Prioritize ML-EID entry.SetContextId(kMeshLocalPrefixContextId); - entry.SetIid(GetMeshLocal64().GetIid()); - SuccessOrExit(error = aMessage.AppendBytes(&entry, entry.GetLength())); + entry.SetIid(Get().GetMeshLocal64().GetIid()); + SuccessOrExit(error = AppendBytes(&entry, entry.GetLength())); length += entry.GetLength(); // Continue to append the other addresses if not `kAppendMeshLocalOnly` mode @@ -1367,7 +1367,7 @@ Error Mle::AppendAddressRegistration(Message &aMessage, AddressRegistrationMode // Prioritize DUA, compressed entry entry.SetContextId(context.mContextId); entry.SetIid(domainUnicastAddress.GetIid()); - SuccessOrExit(error = aMessage.AppendBytes(&entry, entry.GetLength())); + SuccessOrExit(error = AppendBytes(&entry, entry.GetLength())); length += entry.GetLength(); counter++; } @@ -1375,8 +1375,8 @@ Error Mle::AppendAddressRegistration(Message &aMessage, AddressRegistrationMode for (const Ip6::Netif::UnicastAddress &addr : Get().GetUnicastAddresses()) { - if (addr.GetAddress().IsLinkLocal() || IsRoutingLocator(addr.GetAddress()) || - IsAnycastLocator(addr.GetAddress()) || addr.GetAddress() == GetMeshLocal64()) + if (addr.GetAddress().IsLinkLocal() || Get().IsRoutingLocator(addr.GetAddress()) || + Get().IsAnycastLocator(addr.GetAddress()) || addr.GetAddress() == Get().GetMeshLocal64()) { continue; } @@ -1402,7 +1402,7 @@ Error Mle::AppendAddressRegistration(Message &aMessage, AddressRegistrationMode entry.SetIp6Address(addr.GetAddress()); } - SuccessOrExit(error = aMessage.AppendBytes(&entry, entry.GetLength())); + SuccessOrExit(error = AppendBytes(&entry, entry.GetLength())); length += entry.GetLength(); counter++; // only continue to append if there is available entry. @@ -1414,9 +1414,9 @@ Error Mle::AppendAddressRegistration(Message &aMessage, AddressRegistrationMode // indirect transmission. Since Thread 1.2, non-sleepy MED should // also register external multicast addresses of scope larger than // realm with a 1.2 or higher parent. - if (!IsRxOnWhenIdle() + if (!Get().IsRxOnWhenIdle() #if (OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2) - || !GetParent().IsThreadVersion1p1() + || !Get().GetParent().IsThreadVersion1p1() #endif ) { @@ -1425,7 +1425,7 @@ Error Mle::AppendAddressRegistration(Message &aMessage, AddressRegistrationMode #if (OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2) // For Thread 1.2 MED, skip multicast address with scope not // larger than realm local when registering. - if (IsRxOnWhenIdle() && !addr.GetAddress().IsMulticastLargerThanRealmLocal()) + if (Get().IsRxOnWhenIdle() && !addr.GetAddress().IsMulticastLargerThanRealmLocal()) { continue; } @@ -1433,7 +1433,7 @@ Error Mle::AppendAddressRegistration(Message &aMessage, AddressRegistrationMode entry.SetUncompressed(); entry.SetIp6Address(addr.GetAddress()); - SuccessOrExit(error = aMessage.AppendBytes(&entry, entry.GetLength())); + SuccessOrExit(error = AppendBytes(&entry, entry.GetLength())); length += entry.GetLength(); counter++; @@ -1447,20 +1447,20 @@ exit: if (error == kErrorNone && length > 0) { tlv.SetLength(length); - aMessage.Write(startOffset, tlv); + Write(startOffset, tlv); } return error; } #if OPENTHREAD_CONFIG_TIME_SYNC_ENABLE -Error Mle::AppendTimeRequest(Message &aMessage) +Error Mle::TxMessage::AppendTimeRequestTlv(void) { // `TimeRequestTlv` has no value. - return Tlv::Append(aMessage, nullptr, 0); + return Tlv::Append(*this, nullptr, 0); } -Error Mle::AppendTimeParameter(Message &aMessage) +Error Mle::TxMessage::AppendTimeParameterTlv(void) { TimeParameterTlv tlv; @@ -1468,41 +1468,41 @@ Error Mle::AppendTimeParameter(Message &aMessage) tlv.SetTimeSyncPeriod(Get().GetTimeSyncPeriod()); tlv.SetXtalThreshold(Get().GetXtalThreshold()); - return tlv.AppendTo(aMessage); + return tlv.AppendTo(*this); } -Error Mle::AppendXtalAccuracy(Message &aMessage) +Error Mle::TxMessage::AppendXtalAccuracyTlv(void) { - return Tlv::Append(aMessage, otPlatTimeGetXtalAccuracy()); + return Tlv::Append(*this, otPlatTimeGetXtalAccuracy()); } #endif // OPENTHREAD_CONFIG_TIME_SYNC_ENABLE -Error Mle::AppendActiveTimestamp(Message &aMessage) +Error Mle::TxMessage::AppendActiveTimestampTlv(void) { Error error = kErrorNone; const MeshCoP::Timestamp *timestamp = Get().GetTimestamp(); VerifyOrExit(timestamp != nullptr); - error = Tlv::Append(aMessage, *timestamp); + error = Tlv::Append(*this, *timestamp); exit: return error; } -Error Mle::AppendPendingTimestamp(Message &aMessage) +Error Mle::TxMessage::AppendPendingTimestampTlv(void) { Error error = kErrorNone; const MeshCoP::Timestamp *timestamp = Get().GetTimestamp(); VerifyOrExit(timestamp != nullptr && timestamp->GetSeconds() != 0); - error = Tlv::Append(aMessage, *timestamp); + error = Tlv::Append(*this, *timestamp); exit: return error; } #if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE -Error Mle::AppendCslChannel(Message &aMessage) +Error Mle::TxMessage::AppendCslChannelTlv(void) { Error error = kErrorNone; CslChannelTlv cslChannel; @@ -1517,16 +1517,17 @@ Error Mle::AppendCslChannel(Message &aMessage) cslChannel.SetChannelPage(0); cslChannel.SetChannel(Get().GetCslChannel()); - SuccessOrExit(error = aMessage.Append(cslChannel)); + SuccessOrExit(error = Append(cslChannel)); exit: return error; } -Error Mle::AppendCslTimeout(Message &aMessage) +Error Mle::TxMessage::AppendCslTimeoutTlv(void) { OT_ASSERT(Get().IsCslEnabled()); - return Tlv::Append(aMessage, mCslTimeout == 0 ? mTimeout : mCslTimeout); + return Tlv::Append(*this, + Get().mCslTimeout == 0 ? Get().mTimeout : Get().mCslTimeout); } void Mle::SetCslTimeout(uint32_t aTimeout) @@ -1548,7 +1549,7 @@ exit: #endif // OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE #if OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE -Error Mle::AppendCslClockAccuracy(Message &aMessage) +Error Mle::TxMessage::AppendCslClockAccuracyTlv(void) { Error error = kErrorNone; CslClockAccuracyTlv cslClockAccuracy; @@ -1558,7 +1559,7 @@ Error Mle::AppendCslClockAccuracy(Message &aMessage) cslClockAccuracy.SetCslClockAccuracy(Get().GetCslAccuracy()); cslClockAccuracy.SetCslUncertainty(Get().GetCslUncertainty()); - SuccessOrExit(error = aMessage.Append(cslClockAccuracy)); + SuccessOrExit(error = Append(cslClockAccuracy)); exit: return error; @@ -2054,7 +2055,7 @@ void Mle::HandleDelayedResponseTimer(void) else { mDelayedResponses.Dequeue(message); - SendDelayedResponse(message, metadata); + SendDelayedResponse(static_cast(message), metadata); } } @@ -2064,7 +2065,7 @@ void Mle::HandleDelayedResponseTimer(void) } } -void Mle::SendDelayedResponse(Message &aMessage, const DelayedResponseMetadata &aMetadata) +void Mle::SendDelayedResponse(TxMessage &aMessage, const DelayedResponseMetadata &aMetadata) { Error error = kErrorNone; @@ -2072,11 +2073,11 @@ void Mle::SendDelayedResponse(Message &aMessage, const DelayedResponseMetadata & if (aMessage.GetSubType() == Message::kSubTypeMleDataRequest) { - SuccessOrExit(error = AppendActiveTimestamp(aMessage)); - SuccessOrExit(error = AppendPendingTimestamp(aMessage)); + SuccessOrExit(error = aMessage.AppendActiveTimestampTlv()); + SuccessOrExit(error = aMessage.AppendPendingTimestampTlv()); } - SuccessOrExit(error = SendMessage(aMessage, aMetadata.mDestination)); + SuccessOrExit(error = aMessage.SendTo(aMetadata.mDestination)); Log(kMessageSend, kTypeGenericDelayed, aMetadata.mDestination); @@ -2123,7 +2124,7 @@ void Mle::RemoveDelayedMessage(Message::SubType aSubType, MessageType aMessageTy void Mle::SendParentRequest(ParentRequestType aType) { Error error = kErrorNone; - Message * message; + TxMessage * message; uint8_t scanMask = 0; Ip6::Address destination; @@ -2141,16 +2142,16 @@ void Mle::SendParentRequest(ParentRequestType aType) } VerifyOrExit((message = NewMleMessage(kCommandParentRequest)) != nullptr, error = kErrorNoBufs); - SuccessOrExit(error = AppendMode(*message, mDeviceMode)); - SuccessOrExit(error = AppendChallenge(*message, mParentRequestChallenge)); - SuccessOrExit(error = AppendScanMask(*message, scanMask)); - SuccessOrExit(error = AppendVersion(*message)); + SuccessOrExit(error = message->AppendModeTlv(mDeviceMode)); + SuccessOrExit(error = message->AppendChallengeTlv(mParentRequestChallenge)); + SuccessOrExit(error = message->AppendScanMaskTlv(scanMask)); + SuccessOrExit(error = message->AppendVersionTlv()); #if OPENTHREAD_CONFIG_TIME_SYNC_ENABLE - SuccessOrExit(error = AppendTimeRequest(*message)); + SuccessOrExit(error = message->AppendTimeRequestTlv()); #endif destination.SetToLinkLocalAllRoutersMulticast(); - SuccessOrExit(error = SendMessage(*message, destination)); + SuccessOrExit(error = message->SendTo(destination)); switch (aType) { @@ -2181,7 +2182,7 @@ Error Mle::SendChildIdRequest(void) Error error = kErrorNone; uint8_t tlvs[] = {Tlv::kAddress16, Tlv::kNetworkData, Tlv::kRoute}; uint8_t tlvsLen = sizeof(tlvs); - Message * message = nullptr; + TxMessage * message = nullptr; Ip6::Address destination; if (mParent.GetExtAddress() == mParentCandidate.GetExtAddress()) @@ -2203,29 +2204,29 @@ Error Mle::SendChildIdRequest(void) } VerifyOrExit((message = NewMleMessage(kCommandChildIdRequest)) != nullptr, error = kErrorNoBufs); - SuccessOrExit(error = AppendResponse(*message, mParentCandidateChallenge)); - SuccessOrExit(error = AppendLinkFrameCounter(*message)); - SuccessOrExit(error = AppendMleFrameCounter(*message)); - SuccessOrExit(error = AppendMode(*message, mDeviceMode)); - SuccessOrExit(error = AppendTimeout(*message, mTimeout)); - SuccessOrExit(error = AppendVersion(*message)); + SuccessOrExit(error = message->AppendResponseTlv(mParentCandidateChallenge)); + SuccessOrExit(error = message->AppendLinkFrameCounterTlv()); + SuccessOrExit(error = message->AppendMleFrameCounterTlv()); + SuccessOrExit(error = message->AppendModeTlv(mDeviceMode)); + SuccessOrExit(error = message->AppendTimeoutTlv(mTimeout)); + SuccessOrExit(error = message->AppendVersionTlv()); if (!IsFullThreadDevice()) { - SuccessOrExit(error = AppendAddressRegistration(*message, mAddressRegistrationMode)); + SuccessOrExit(error = message->AppendAddressRegistrationTlv(mAddressRegistrationMode)); // no need to request the last Route64 TLV for MTD tlvsLen -= 1; } - SuccessOrExit(error = AppendTlvRequest(*message, tlvs, tlvsLen)); - SuccessOrExit(error = AppendActiveTimestamp(*message)); - SuccessOrExit(error = AppendPendingTimestamp(*message)); + SuccessOrExit(error = message->AppendTlvRequestTlv(tlvs, tlvsLen)); + SuccessOrExit(error = message->AppendActiveTimestampTlv()); + SuccessOrExit(error = message->AppendPendingTimestampTlv()); mParentCandidate.SetState(Neighbor::kStateValid); destination.SetToLinkLocalAddress(mParentCandidate.GetExtAddress()); - SuccessOrExit(error = SendMessage(*message, destination)); + SuccessOrExit(error = message->SendTo(destination)); Log(kMessageSend, (mAddressRegistrationMode == kAppendMeshLocalOnly) ? kTypeChildIdRequestShort : kTypeChildIdRequest, @@ -2249,13 +2250,13 @@ Error Mle::SendDataRequest(const Ip6::Address &aDestination, const uint8_t * aExtraTlvs, uint8_t aExtraTlvsLength) { - Error error = kErrorNone; - Message *message; + Error error = kErrorNone; + TxMessage *message; RemoveDelayedDataRequestMessage(aDestination); VerifyOrExit((message = NewMleMessage(kCommandDataRequest)) != nullptr, error = kErrorNoBufs); - SuccessOrExit(error = AppendTlvRequest(*message, aTlvs, aTlvsLength)); + SuccessOrExit(error = message->AppendTlvRequestTlv(aTlvs, aTlvsLength)); if (aExtraTlvs != nullptr && aExtraTlvsLength > 0) { @@ -2264,15 +2265,15 @@ Error Mle::SendDataRequest(const Ip6::Address &aDestination, if (aDelay) { - SuccessOrExit(error = AddDelayedResponse(*message, aDestination, aDelay)); + SuccessOrExit(error = message->SendAfterDelay(aDestination, aDelay)); Log(kMessageDelay, kTypeDataRequest, aDestination); } else { - SuccessOrExit(error = AppendActiveTimestamp(*message)); - SuccessOrExit(error = AppendPendingTimestamp(*message)); + SuccessOrExit(error = message->AppendActiveTimestampTlv()); + SuccessOrExit(error = message->AppendPendingTimestampTlv()); - SuccessOrExit(error = SendMessage(*message, aDestination)); + SuccessOrExit(error = message->SendTo(aDestination)); Log(kMessageSend, kTypeDataRequest, aDestination); if (!IsRxOnWhenIdle()) @@ -2424,7 +2425,7 @@ Error Mle::SendChildUpdateRequest(void) { Error error = kErrorNone; Ip6::Address destination; - Message * message = nullptr; + TxMessage * message = nullptr; AddressRegistrationMode mode = kAppendAllAddresses; if (!mParent.IsStateValidOrRestoring()) @@ -2438,25 +2439,25 @@ Error Mle::SendChildUpdateRequest(void) ScheduleMessageTransmissionTimer(); VerifyOrExit((message = NewMleMessage(kCommandChildUpdateRequest)) != nullptr, error = kErrorNoBufs); - SuccessOrExit(error = AppendMode(*message, mDeviceMode)); + SuccessOrExit(error = message->AppendModeTlv(mDeviceMode)); switch (mRole) { case kRoleDetached: mParentRequestChallenge.GenerateRandom(); - SuccessOrExit(error = AppendChallenge(*message, mParentRequestChallenge)); + SuccessOrExit(error = message->AppendChallengeTlv(mParentRequestChallenge)); mode = kAppendMeshLocalOnly; break; case kRoleChild: - SuccessOrExit(error = AppendSourceAddress(*message)); - SuccessOrExit(error = AppendLeaderData(*message)); - SuccessOrExit(error = AppendTimeout(*message, mTimeout)); + SuccessOrExit(error = message->AppendSourceAddressTlv()); + SuccessOrExit(error = message->AppendLeaderDataTlv()); + SuccessOrExit(error = message->AppendTimeoutTlv(mTimeout)); #if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE if (Get().IsCslEnabled()) { - SuccessOrExit(error = AppendCslChannel(*message)); - SuccessOrExit(error = AppendCslTimeout(*message)); + SuccessOrExit(error = message->AppendCslChannelTlv()); + SuccessOrExit(error = message->AppendCslTimeoutTlv()); } #endif break; @@ -2470,11 +2471,11 @@ Error Mle::SendChildUpdateRequest(void) if (!IsFullThreadDevice()) { - SuccessOrExit(error = AppendAddressRegistration(*message, mode)); + SuccessOrExit(error = message->AppendAddressRegistrationTlv(mode)); } destination.SetToLinkLocalAddress(mParent.GetExtAddress()); - SuccessOrExit(error = SendMessage(*message, destination)); + SuccessOrExit(error = message->SendTo(destination)); Log(kMessageSend, kTypeChildUpdateRequestOfParent, destination); @@ -2501,23 +2502,23 @@ Error Mle::SendChildUpdateResponse(const uint8_t *aTlvs, uint8_t aNumTlvs, const { Error error = kErrorNone; Ip6::Address destination; - Message * message; + TxMessage * message; bool checkAddress = false; VerifyOrExit((message = NewMleMessage(kCommandChildUpdateResponse)) != nullptr, error = kErrorNoBufs); - SuccessOrExit(error = AppendSourceAddress(*message)); - SuccessOrExit(error = AppendLeaderData(*message)); + SuccessOrExit(error = message->AppendSourceAddressTlv()); + SuccessOrExit(error = message->AppendLeaderDataTlv()); for (int i = 0; i < aNumTlvs; i++) { switch (aTlvs[i]) { case Tlv::kTimeout: - SuccessOrExit(error = AppendTimeout(*message, mTimeout)); + SuccessOrExit(error = message->AppendTimeoutTlv(mTimeout)); break; case Tlv::kStatus: - SuccessOrExit(error = AppendStatus(*message, StatusTlv::kError)); + SuccessOrExit(error = message->AppendStatusTlv(StatusTlv::kError)); break; case Tlv::kAddressRegistration: @@ -2528,29 +2529,29 @@ Error Mle::SendChildUpdateResponse(const uint8_t *aTlvs, uint8_t aNumTlvs, const // addresses to register we follow up with a "Child Update // Request". - SuccessOrExit(error = AppendAddressRegistration(*message, kAppendMeshLocalOnly)); + SuccessOrExit(error = message->AppendAddressRegistrationTlv(kAppendMeshLocalOnly)); checkAddress = true; } break; case Tlv::kResponse: - SuccessOrExit(error = AppendResponse(*message, aChallenge)); + SuccessOrExit(error = message->AppendResponseTlv(aChallenge)); break; case Tlv::kLinkFrameCounter: - SuccessOrExit(error = AppendLinkFrameCounter(*message)); + SuccessOrExit(error = message->AppendLinkFrameCounterTlv()); break; case Tlv::kMleFrameCounter: - SuccessOrExit(error = AppendMleFrameCounter(*message)); + SuccessOrExit(error = message->AppendMleFrameCounterTlv()); break; #if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE case Tlv::kCslTimeout: if (Get().IsCslEnabled()) { - SuccessOrExit(error = AppendCslTimeout(*message)); + SuccessOrExit(error = message->AppendCslTimeoutTlv()); } break; #endif @@ -2558,7 +2559,7 @@ Error Mle::SendChildUpdateResponse(const uint8_t *aTlvs, uint8_t aNumTlvs, const } destination.SetToLinkLocalAddress(mParent.GetExtAddress()); - SuccessOrExit(error = SendMessage(*message, destination)); + SuccessOrExit(error = message->SendTo(destination)); Log(kMessageSend, kTypeChildUpdateResponseOfParent, destination); @@ -2586,7 +2587,7 @@ void Mle::SendAnnounce(uint8_t aChannel, const Ip6::Address &aDestination, Annou Error error = kErrorNone; ChannelTlv channel; MeshCoP::Timestamp activeTimestamp; - Message * message = nullptr; + TxMessage * message = nullptr; VerifyOrExit(Get().GetSupportedChannelMask().ContainsChannel(aChannel), error = kErrorInvalidArgs); VerifyOrExit((message = NewMleMessage(kCommandAnnounce)) != nullptr, error = kErrorNoBufs); @@ -2607,13 +2608,13 @@ void Mle::SendAnnounce(uint8_t aChannel, const Ip6::Address &aDestination, Annou break; case kNormalAnnounce: - SuccessOrExit(error = AppendActiveTimestamp(*message)); + SuccessOrExit(error = message->AppendActiveTimestampTlv()); break; } SuccessOrExit(error = Tlv::Append(*message, Get().GetPanId())); - SuccessOrExit(error = SendMessage(*message, aDestination)); + SuccessOrExit(error = message->SendTo(aDestination)); LogInfo("Send Announce on channel %d", aChannel); @@ -2647,10 +2648,10 @@ bool Mle::HasMoreChannelsToAnnouce(void) const #if OPENTHREAD_CONFIG_MLE_LINK_METRICS_SUBJECT_ENABLE Error Mle::SendLinkMetricsManagementResponse(const Ip6::Address &aDestination, LinkMetrics::Status aStatus) { - Error error = kErrorNone; - Message *message; - Tlv tlv; - ot::Tlv statusSubTlv; + Error error = kErrorNone; + TxMessage *message; + Tlv tlv; + ot::Tlv statusSubTlv; VerifyOrExit((message = NewMleMessage(kCommandLinkMetricsManagementResponse)) != nullptr, error = kErrorNoBufs); @@ -2663,7 +2664,7 @@ Error Mle::SendLinkMetricsManagementResponse(const Ip6::Address &aDestination, L SuccessOrExit(error = message->Append(statusSubTlv)); SuccessOrExit(error = message->Append(aStatus)); - SuccessOrExit(error = SendMessage(*message, aDestination)); + SuccessOrExit(error = message->SendTo(aDestination)); exit: FreeMessageOnError(message, error); @@ -2674,9 +2675,9 @@ exit: #if OPENTHREAD_CONFIG_MLE_LINK_METRICS_INITIATOR_ENABLE Error Mle::SendLinkProbe(const Ip6::Address &aDestination, uint8_t aSeriesId, uint8_t *aBuf, uint8_t aLength) { - Error error = kErrorNone; - Message *message; - Tlv tlv; + Error error = kErrorNone; + TxMessage *message; + Tlv tlv; VerifyOrExit((message = NewMleMessage(kCommandLinkProbe)) != nullptr, error = kErrorNoBufs); @@ -2687,7 +2688,7 @@ Error Mle::SendLinkProbe(const Ip6::Address &aDestination, uint8_t aSeriesId, ui SuccessOrExit(error = message->Append(aSeriesId)); SuccessOrExit(error = message->AppendBytes(aBuf, aLength)); - SuccessOrExit(error = SendMessage(*message, aDestination)); + SuccessOrExit(error = message->SendTo(aDestination)); exit: FreeMessageOnError(message, error); @@ -2787,7 +2788,7 @@ exit: return error; } -Error Mle::SendMessage(Message &aMessage, const Ip6::Address &aDestination) +Error Mle::TxMessage::SendTo(const Ip6::Address &aDestination) { Error error = kErrorNone; uint16_t offset = 0; @@ -2795,11 +2796,11 @@ Error Mle::SendMessage(Message &aMessage, const Ip6::Address &aDestination) Ip6::MessageInfo messageInfo; messageInfo.SetPeerAddr(aDestination); - messageInfo.SetSockAddr(mLinkLocal64.GetAddress()); + messageInfo.SetSockAddr(Get().mLinkLocal64.GetAddress()); messageInfo.SetPeerPort(kUdpPort); messageInfo.SetHopLimit(kMleHopLimit); - IgnoreError(aMessage.Read(offset, securitySuite)); + IgnoreError(Read(offset, securitySuite)); offset += sizeof(securitySuite); if (securitySuite == k154Security) @@ -2808,24 +2809,25 @@ Error Mle::SendMessage(Message &aMessage, const Ip6::Address &aDestination) // Update the fields in the security header - IgnoreError(aMessage.Read(offset, header)); + IgnoreError(Read(offset, header)); header.SetFrameCounter(Get().GetMleFrameCounter()); header.SetKeyId(Get().GetCurrentKeySequence()); - aMessage.Write(offset, header); + Write(offset, header); offset += sizeof(SecurityHeader); - SuccessOrExit(error = ProcessMessageSecurity(Crypto::AesCcm::kEncrypt, aMessage, messageInfo, offset, header)); + SuccessOrExit( + error = Get().ProcessMessageSecurity(Crypto::AesCcm::kEncrypt, *this, messageInfo, offset, header)); Get().IncrementMleFrameCounter(); } - SuccessOrExit(error = mSocket.SendTo(aMessage, messageInfo)); + SuccessOrExit(error = Get().mSocket.SendTo(*this, messageInfo)); exit: return error; } -Error Mle::AddDelayedResponse(Message &aMessage, const Ip6::Address &aDestination, uint16_t aDelay) +Error Mle::TxMessage::SendAfterDelay(const Ip6::Address &aDestination, uint16_t aDelay) { Error error = kErrorNone; DelayedResponseMetadata metadata; @@ -2833,10 +2835,10 @@ Error Mle::AddDelayedResponse(Message &aMessage, const Ip6::Address &aDestinatio metadata.mSendTime = TimerMilli::GetNow() + aDelay; metadata.mDestination = aDestination; - SuccessOrExit(error = metadata.AppendTo(aMessage)); - mDelayedResponses.Enqueue(aMessage); + SuccessOrExit(error = metadata.AppendTo(*this)); + Get().mDelayedResponses.Enqueue(*this); - mDelayedResponseTimer.FireAtIfEarlier(metadata.mSendTime); + Get().mDelayedResponseTimer.FireAtIfEarlier(metadata.mSendTime); exit: return error; @@ -3116,7 +3118,7 @@ void Mle::HandleAdvertisement(RxInfo &aRxInfo) Log(kMessageReceive, kTypeAdvertisement, aRxInfo.mMessageInfo.GetPeerAddr(), sourceAddress); // Leader Data - SuccessOrExit(error = ReadLeaderData(aRxInfo.mMessage, leaderData)); + SuccessOrExit(error = aRxInfo.mMessage.ReadLeaderDataTlv(leaderData)); if (!IsDetached()) { @@ -3245,7 +3247,7 @@ Error Mle::HandleLeaderData(RxInfo &aRxInfo) Tlv tlv; // Leader Data - SuccessOrExit(error = ReadLeaderData(aRxInfo.mMessage, leaderData)); + SuccessOrExit(error = aRxInfo.mMessage.ReadLeaderDataTlv(leaderData)); if ((leaderData.GetPartitionId() != mLeaderData.GetPartitionId()) || (leaderData.GetWeighting() != mLeaderData.GetWeighting()) || (leaderData.GetLeaderRouterId() != GetLeaderId())) @@ -3518,7 +3520,7 @@ void Mle::HandleParentResponse(RxInfo &aRxInfo) VerifyOrExit(version >= OT_THREAD_VERSION_1_1, error = kErrorParse); // Response - SuccessOrExit(error = ReadResponse(aRxInfo.mMessage, response)); + SuccessOrExit(error = aRxInfo.mMessage.ReadResponseTlv(response)); VerifyOrExit(response == mParentRequestChallenge, error = kErrorParse); aRxInfo.mMessageInfo.GetPeerAddr().GetIid().ConvertToExtAddress(extAddress); @@ -3529,7 +3531,7 @@ void Mle::HandleParentResponse(RxInfo &aRxInfo) } // Leader Data - SuccessOrExit(error = ReadLeaderData(aRxInfo.mMessage, leaderData)); + SuccessOrExit(error = aRxInfo.mMessage.ReadLeaderDataTlv(leaderData)); // Link Margin SuccessOrExit(error = Tlv::Find(aRxInfo.mMessage, linkMarginFromTlv)); @@ -3639,7 +3641,7 @@ void Mle::HandleParentResponse(RxInfo &aRxInfo) } // Link/MLE Frame Counters - SuccessOrExit(error = ReadFrameCounters(aRxInfo.mMessage, linkFrameCounter, mleFrameCounter)); + SuccessOrExit(error = aRxInfo.mMessage.ReadFrameCounterTlvs(linkFrameCounter, mleFrameCounter)); #if OPENTHREAD_CONFIG_TIME_SYNC_ENABLE @@ -3663,7 +3665,7 @@ void Mle::HandleParentResponse(RxInfo &aRxInfo) #endif // OPENTHREAD_CONFIG_TIME_SYNC_ENABLE // Challenge - SuccessOrExit(error = ReadChallenge(aRxInfo.mMessage, mParentCandidateChallenge)); + SuccessOrExit(error = aRxInfo.mMessage.ReadChallengeTlv(mParentCandidateChallenge)); mParentCandidate.SetExtAddress(extAddress); mParentCandidate.SetRloc16(sourceAddress); @@ -3724,7 +3726,7 @@ void Mle::HandleChildIdResponse(RxInfo &aRxInfo) VerifyOrExit(RouterIdMatch(sourceAddress, shortAddress), error = kErrorRejected); // Leader Data - SuccessOrExit(error = ReadLeaderData(aRxInfo.mMessage, leaderData)); + SuccessOrExit(error = aRxInfo.mMessage.ReadLeaderDataTlv(leaderData)); // Network Data error = Tlv::FindTlvOffset(aRxInfo.mMessage, Tlv::kNetworkData, networkDataOffset); @@ -3852,7 +3854,7 @@ void Mle::HandleChildUpdateRequest(RxInfo &aRxInfo) Log(kMessageReceive, kTypeChildUpdateRequestOfParent, aRxInfo.mMessageInfo.GetPeerAddr(), sourceAddress); // Challenge - switch (ReadChallenge(aRxInfo.mMessage, challenge)) + switch (aRxInfo.mMessage.ReadChallengeTlv(challenge)) { case kErrorNone: tlvs[numTlvs++] = Tlv::kResponse; @@ -3906,7 +3908,7 @@ void Mle::HandleChildUpdateRequest(RxInfo &aRxInfo) } // TLV Request - switch (FindTlvRequest(aRxInfo.mMessage, requestedTlvs)) + switch (aRxInfo.mMessage.ReadTlvRequestTlv(requestedTlvs)) { case kErrorNone: for (uint8_t i = 0; i < requestedTlvs.mNumTlvs; i++) @@ -3958,7 +3960,7 @@ void Mle::HandleChildUpdateResponse(RxInfo &aRxInfo) switch (mRole) { case kRoleDetached: - SuccessOrExit(error = ReadResponse(aRxInfo.mMessage, response)); + SuccessOrExit(error = aRxInfo.mMessage.ReadResponseTlv(response)); VerifyOrExit(response == mParentRequestChallenge, error = kErrorSecurity); break; @@ -3985,7 +3987,7 @@ void Mle::HandleChildUpdateResponse(RxInfo &aRxInfo) switch (mRole) { case kRoleDetached: - SuccessOrExit(error = ReadFrameCounters(aRxInfo.mMessage, linkFrameCounter, mleFrameCounter)); + SuccessOrExit(error = aRxInfo.mMessage.ReadFrameCounterTlvs(linkFrameCounter, mleFrameCounter)); mParent.GetLinkFrameCounters().SetAll(linkFrameCounter); mParent.SetLinkAckFrameCounter(linkFrameCounter); @@ -4674,9 +4676,9 @@ const char *Mle::ReattachStateToString(ReattachState aState) #if OPENTHREAD_CONFIG_MLE_LINK_METRICS_INITIATOR_ENABLE Error Mle::SendLinkMetricsManagementRequest(const Ip6::Address &aDestination, const uint8_t *aSubTlvs, uint8_t aLength) { - Error error = kErrorNone; - Message *message; - Tlv tlv; + Error error = kErrorNone; + TxMessage *message; + Tlv tlv; VerifyOrExit((message = NewMleMessage(kCommandLinkMetricsManagementRequest)) != nullptr, error = kErrorNoBufs); @@ -4687,7 +4689,7 @@ Error Mle::SendLinkMetricsManagementRequest(const Ip6::Address &aDestination, co SuccessOrExit(error = message->AppendBytes(&tlv, sizeof(tlv))); SuccessOrExit(error = message->AppendBytes(aSubTlvs, aLength)); - SuccessOrExit(error = SendMessage(*message, aDestination)); + SuccessOrExit(error = message->SendTo(aDestination)); exit: FreeMessageOnError(message, error); diff --git a/src/core/thread/mle.hpp b/src/core/thread/mle.hpp index 6c870c326..ff3c173e8 100644 --- a/src/core/thread/mle.hpp +++ b/src/core/thread/mle.hpp @@ -822,7 +822,7 @@ protected: static constexpr uint16_t kMleMaxResponseDelay = 1000u; ///< Max delay before responding to a multicast request. /** - * This enumeration type is used in `AppendAddressRegistration()` to determine which addresses to include in the + * This enumeration type is used in `AppendAddressRegistrationTlv()` to determine which addresses to include in the * appended Address Registration TLV. * */ @@ -941,6 +941,421 @@ protected: uint8_t mNumTlvs; ///< Number of TLVs in the array. }; + /** + * This class represents an MLE Tx message. + * + */ + class TxMessage : public Message + { + public: + /** + * This method appends a Source Address TLV to the message. + * + * @retval kErrorNone Successfully appended the Source Address TLV. + * @retval kErrorNoBufs Insufficient buffers available to append the Source Address TLV. + * + */ + Error AppendSourceAddressTlv(void); + + /** + * This method appends a Mode TLV to the message. + * + * @param[in] aMode The Device Mode. + * + * @retval kErrorNone Successfully appended the Mode TLV. + * @retval kErrorNoBufs Insufficient buffers available to append the Mode TLV. + * + */ + Error AppendModeTlv(DeviceMode aMode); + + /** + * This method appends a Timeout TLV to the message. + * + * @param[in] aTimeout The Timeout value. + * + * @retval kErrorNone Successfully appended the Timeout TLV. + * @retval kErrorNoBufs Insufficient buffers available to append the Timeout TLV. + * + */ + Error AppendTimeoutTlv(uint32_t aTimeout); + + /** + * This method appends a Challenge TLV to the message. + * + * @param[in] aChallenge A pointer to the Challenge value. + * @param[in] aChallengeLength The length of the Challenge value in bytes. + * + * @retval kErrorNone Successfully appended the Challenge TLV. + * @retval kErrorNoBufs Insufficient buffers available to append the Challenge TLV. + * + */ + Error AppendChallengeTlv(const uint8_t *aChallenge, uint8_t aChallengeLength); + + /** + * This method appends a Challenge TLV to the message. + * + * @param[in] aChallenge A reference to the Challenge data. + * + * @retval kErrorNone Successfully appended the Challenge TLV. + * @retval kErrorNoBufs Insufficient buffers available to append the Challenge TLV. + * + */ + Error AppendChallengeTlv(const Challenge &aChallenge); + + /** + * This method appends a Response TLV to the message. + * + * @param[in] aResponse A reference to the Response data. + * + * @retval kErrorNone Successfully appended the Response TLV. + * @retval kErrorNoBufs Insufficient buffers available to append the Response TLV. + * + */ + Error AppendResponseTlv(const Challenge &aResponse); + + /** + * This method appends a Link Frame Counter TLV to the message. + * + * @retval kErrorNone Successfully appended the Link Frame Counter TLV. + * @retval kErrorNoBufs Insufficient buffers available to append the Link Frame Counter TLV. + * + */ + Error AppendLinkFrameCounterTlv(void); + + /** + * This method appends an MLE Frame Counter TLV to the message. + * + * @retval kErrorNone Successfully appended the Frame Counter TLV. + * @retval kErrorNoBufs Insufficient buffers available to append the MLE Frame Counter TLV. + * + */ + Error AppendMleFrameCounterTlv(void); + + /** + * This method appends an Address16 TLV to the message. + * + * @param[in] aRloc16 The RLOC16 value. + * + * @retval kErrorNone Successfully appended the Address16 TLV. + * @retval kErrorNoBufs Insufficient buffers available to append the Address16 TLV. + * + */ + Error AppendAddress16Tlv(uint16_t aRloc16); + + /** + * This method appends a Network Data TLV to the message. + * + * @param[in] aType The Network Data type to append, full set or stable subset. + * + * @retval kErrorNone Successfully appended the Network Data TLV. + * @retval kErrorNoBufs Insufficient buffers available to append the Network Data TLV. + * + */ + Error AppendNetworkDataTlv(NetworkData::Type aType); + + /** + * This method appends a TLV Request TLV to the message. + * + * @param[in] aTlvs A pointer to the list of TLV types. + * @param[in] aTlvsLength The number of TLV types in @p aTlvs + * + * @retval kErrorNone Successfully appended the TLV Request TLV. + * @retval kErrorNoBufs Insufficient buffers available to append the TLV Request TLV. + * + */ + Error AppendTlvRequestTlv(const uint8_t *aTlvs, uint8_t aTlvsLength); + + /** + * This method appends a Leader Data TLV to the message. + * + * @retval kErrorNone Successfully appended the Leader Data TLV. + * @retval kErrorNoBufs Insufficient buffers available to append the Leader Data TLV. + * + */ + Error AppendLeaderDataTlv(void); + + /** + * This method appends a Scan Mask TLV to th message. + * + * @param[in] aScanMask The Scan Mask value. + * + * @retval kErrorNone Successfully appended the Scan Mask TLV. + * @retval kErrorNoBufs Insufficient buffers available to append the Scan Mask TLV. + * + */ + Error AppendScanMaskTlv(uint8_t aScanMask); + + /** + * This method appends a Status TLV to the message. + * + * @param[in] aStatus The Status value. + * + * @retval kErrorNone Successfully appended the Status TLV. + * @retval kErrorNoBufs Insufficient buffers available to append the Status TLV. + * + */ + Error AppendStatusTlv(StatusTlv::Status aStatus); + + /** + * This method appends a Link Margin TLV to the message. + * + * @param[in] aLinkMargin The Link Margin value. + * + * @retval kErrorNone Successfully appended the Link Margin TLV. + * @retval kErrorNoBufs Insufficient buffers available to append the Link Margin TLV. + * + */ + Error AppendLinkMarginTlv(uint8_t aLinkMargin); + + /** + * This method appends a Version TLV to the message. + * + * @retval kErrorNone Successfully appended the Version TLV. + * @retval kErrorNoBufs Insufficient buffers available to append the Version TLV. + * + */ + Error AppendVersionTlv(void); + + /** + * This method appends an Address Registration TLV to the message. + * + * @param[in] aMode Determines which addresses to include in the TLV (see `AddressRegistrationMode`). + * + * @retval kErrorNone Successfully appended the Address Registration TLV. + * @retval kErrorNoBufs Insufficient buffers available to append the Address Registration TLV. + * + */ + Error AppendAddressRegistrationTlv(AddressRegistrationMode aMode = kAppendAllAddresses); + +#if OPENTHREAD_CONFIG_TIME_SYNC_ENABLE + /** + * This method appends a Time Request TLV to the message. + * + * @retval kErrorNone Successfully appended the Time Request TLV. + * @retval kErrorNoBufs Insufficient buffers available to append the Time Request TLV. + * + */ + Error AppendTimeRequestTlv(void); + + /** + * This method appends a Time Parameter TLV to the message. + * + * @retval kErrorNone Successfully appended the Time Parameter TLV. + * @retval kErrorNoBufs Insufficient buffers available to append the Time Parameter TLV. + * + */ + Error AppendTimeParameterTlv(void); +#endif + /** + * This method appends a XTAL Accuracy TLV to the message. + * + * @retval kErrorNone Successfully appended the XTAL Accuracy TLV. + * @retval kErrorNoBufs Insufficient buffers available to append the XTAl Accuracy TLV. + * + */ + Error AppendXtalAccuracyTlv(void); + +#if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE + /** + * This method appends a CSL Channel TLV to the message. + * + * @retval kErrorNone Successfully appended the CSL Channel TLV. + * @retval kErrorNoBufs Insufficient buffers available to append the CSL Channel TLV. + * + */ + Error AppendCslChannelTlv(void); + + /** + * This method appends a CSL Sync Timeout TLV to the message. + * + * @retval kErrorNone Successfully appended the CSL Timeout TLV. + * @retval kErrorNoBufs Insufficient buffers available to append the CSL Timeout TLV. + * + */ + Error AppendCslTimeoutTlv(void); +#endif + +#if OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE + /** + * This method appends a CSL Clock Accuracy TLV to the message. + * + * @retval kErrorNone Successfully appended the CSL Accuracy TLV. + * @retval kErrorNoBufs Insufficient buffers available to append the CSL Accuracy TLV. + * + */ + Error AppendCslClockAccuracyTlv(void); +#endif + + /** + * This method appends a Active Timestamp TLV to the message. + * + * @retval kErrorNone Successfully appended the Active Timestamp TLV. + * @retval kErrorNoBufs Insufficient buffers available to append the Active Timestamp TLV. + * + */ + Error AppendActiveTimestampTlv(void); + + /** + * This method appends a Pending Timestamp TLV to the message. + * + * @retval kErrorNone Successfully appended the Pending Timestamp TLV. + * @retval kErrorNoBufs Insufficient buffers available to append the Pending Timestamp TLV. + * + */ + Error AppendPendingTimestampTlv(void); + +#if OPENTHREAD_FTD + /** + * This method appends a Route TLV to the message. + * + * @param[in] aNeighbor A pointer to the intended destination (can be `nullptr`). + * + * @retval kErrorNone Successfully appended the Route TLV. + * @retval kErrorNoBufs Insufficient buffers available to append the Route TLV. + * + */ + Error AppendRouteTlv(Neighbor *aNeighbor = nullptr); + + /** + * This method appends a Active Dataset TLV to the message. + * + * @retval kErrorNone Successfully appended the Active Dataset TLV. + * @retval kErrorNoBufs Insufficient buffers available to append the Active Dataset TLV. + * + */ + Error AppendActiveDatasetTlv(void); + + /** + * This method appends a Pending Dataset TLV to the message. + * + * @retval kErrorNone Successfully appended the Pending Dataset TLV. + * @retval kErrorNoBufs Insufficient buffers available to append the Pending Dataset TLV. + * + */ + Error AppendPendingDatasetTlv(void); + + /** + * This method appends a Connectivity TLV to the message. + * + * @retval kErrorNone Successfully appended the Connectivity TLV. + * @retval kErrorNoBufs Insufficient buffers available to append the Connectivity TLV. + * + */ + Error AppendConnectivityTlv(void); + + /** + * This method appends a Address Registration TLV to the message with addresses from a given child. + * + * @param[in] aChild The child to include its list of addresses in the Address Registration TLV. + * + * @retval kErrorNone Successfully appended the Connectivity TLV. + * @retval kErrorNoBufs Insufficient buffers available to append the Connectivity TLV. + * + */ + Error AppendAddresseRegisterationTlv(Child &aChild); +#endif // OPENTHREAD_FTD + + /** + * This method submits the MLE message to the UDP socket to be sent. + * + * @param[in] aDestination A reference to the IPv6 address of the destination. + * + * @retval kErrorNone Successfully submitted the MLE message. + * @retval kErrorNoBufs Insufficient buffers to form the rest of the MLE message. + * + */ + Error SendTo(const Ip6::Address &aDestination); + + /** + * This method enqueues the message to be sent after a given delay. + * + * @param[in] aDestination The IPv6 address of the recipient of the message. + * @param[in] aDelay The delay in milliseconds before transmission of the message. + * + * @retval kErrorNone Successfully queued the message to transmit after the delay. + * @retval kErrorNoBufs Insufficient buffers to queue the message. + * + */ + Error SendAfterDelay(const Ip6::Address &aDestination, uint16_t aDelay); + }; + + /** + * This class represents an MLE Rx message. + * + */ + class RxMessage : public Message + { + public: + /** + * This method reads Challenge TLV from the message. + * + * @param[out] aChallenge A reference to the Challenge data where to output the read value. + * + * @retval kErrorNone Successfully read the Challenge TLV. + * @retval kErrorNotFound Challenge TLV was not found in the message. + * @retval kErrorParse Challenge TLV was found but could not be parsed. + * + */ + Error ReadChallengeTlv(Challenge &aChallenge) const; + + /** + * This method reads Response TLV from the message. + * + * @param[out] aResponse A reference to the Response data where to output the read value. + * + * @retval kErrorNone Successfully read the Response TLV. + * @retval kErrorNotFound Response TLV was not found in the message. + * @retval kErrorParse Response TLV was found but could not be parsed. + * + */ + Error ReadResponseTlv(Challenge &aResponse) const; + + /** + * This method reads Link and MLE Frame Counters from the message. + * + * Link Frame Counter TLV must be present in the message and its value is read into @p aLinkFrameCounter. If MLE + * Frame Counter TLV is present in the message, its value is read into @p aMleFrameCounter. If the MLE Frame + * Counter TLV is not present in the message, then @p aMleFrameCounter is set to the same value as + * @p aLinkFrameCounter. + * + * @param[out] aLinkFrameCounter A reference to an `uint32_t` to output the Link Frame Counter. + * @param[out] aMleFrameCounter A reference to an `uint32_t` to output the MLE Frame Counter. + * + * @retval kErrorNone Successfully read the counters. + * @retval kErrorNotFound Link Frame Counter TLV was not found in the message. + * @retval kErrorParse TLVs are not well-formed. + * + */ + Error ReadFrameCounterTlvs(uint32_t &aLinkFrameCounter, uint32_t &aMleFrameCounter) const; + + /** + * This method reads TLV Request TLV from the message. + * + * @param[out] aRequestedTlvs A reference to output the read list of requested TLVs. + * + * @retval kErrorNone Successfully read the TLV. + * @retval kErrorNotFound TLV was not found in the message. + * @retval kErrorParse TLV was found but could not be parsed. + * + */ + Error ReadTlvRequestTlv(RequestedTlvs &aRequestedTlvs) const; + + /** + * This method reads Leader Data TLV from a message. + * + * @param[out] aLeaderData A reference to output the Leader Data. + * + * @retval kErrorNone Successfully read the TLV. + * @retval kErrorNotFound TLV was not found in the message. + * @retval kErrorParse TLV was found but could not be parsed. + * + */ + Error ReadLeaderDataTlv(LeaderData &aLeaderData) const; + + private: + Error ReadChallengeOrResponse(uint8_t aTlvType, Challenge &aBuffer) const; + }; + /** * This structure represents a received MLE message containing additional information about the message (e.g. * key sequence, neighbor from which it was received). @@ -956,7 +1371,7 @@ protected: * */ RxInfo(Message &aMessage, const Ip6::MessageInfo &aMessageInfo) - : mMessage(aMessage) + : mMessage(static_cast(aMessage)) , mMessageInfo(aMessageInfo) , mFrameCounter(0) , mKeySequence(0) @@ -964,7 +1379,7 @@ protected: { } - Message & mMessage; ///< The MLE message. + RxMessage & mMessage; ///< The MLE message. const Ip6::MessageInfo &mMessageInfo; ///< The `MessageInfo` associated with the message. uint32_t mFrameCounter; ///< The frame counter from aux security header. uint32_t mKeySequence; ///< The key sequence from the aux security header. @@ -979,7 +1394,7 @@ protected: * @returns A pointer to the message or `nullptr` if insufficient message buffers are available. * */ - Message *NewMleMessage(Command aCommand); + TxMessage *NewMleMessage(Command aCommand); /** * This method sets the device role. @@ -1005,370 +1420,6 @@ protected: */ void SetAttachState(AttachState aState); - /** - * This method appends a Source Address TLV to a message. - * - * @param[in] aMessage A reference to the message. - * - * @retval kErrorNone Successfully appended the Source Address TLV. - * @retval kErrorNoBufs Insufficient buffers available to append the Source Address TLV. - * - */ - Error AppendSourceAddress(Message &aMessage) const; - - /** - * This method appends a Mode TLV to a message. - * - * @param[in] aMessage A reference to the message. - * @param[in] aMode The Device Mode. - * - * @retval kErrorNone Successfully appended the Mode TLV. - * @retval kErrorNoBufs Insufficient buffers available to append the Mode TLV. - * - */ - Error AppendMode(Message &aMessage, DeviceMode aMode); - - /** - * This method appends a Timeout TLV to a message. - * - * @param[in] aMessage A reference to the message. - * @param[in] aTimeout The Timeout value. - * - * @retval kErrorNone Successfully appended the Timeout TLV. - * @retval kErrorNoBufs Insufficient buffers available to append the Timeout TLV. - * - */ - Error AppendTimeout(Message &aMessage, uint32_t aTimeout); - - /** - * This method appends a Challenge TLV to a message. - * - * @param[in] aMessage A reference to the message. - * @param[in] aChallenge A pointer to the Challenge value. - * @param[in] aChallengeLength The length of the Challenge value in bytes. - * - * @retval kErrorNone Successfully appended the Challenge TLV. - * @retval kErrorNoBufs Insufficient buffers available to append the Challenge TLV. - * - */ - Error AppendChallenge(Message &aMessage, const uint8_t *aChallenge, uint8_t aChallengeLength); - - /** - * This method appends a Challenge TLV to a message. - * - * @param[in] aMessage A reference to the message. - * @param[in] aChallenge A reference to the Challenge data. - * - * @retval kErrorNone Successfully appended the Challenge TLV. - * @retval kErrorNoBufs Insufficient buffers available to append the Challenge TLV. - * - */ - Error AppendChallenge(Message &aMessage, const Challenge &aChallenge); - - /** - * This method reads Challenge TLV from a message. - * - * @param[in] aMessage A reference to the message. - * @param[out] aChallenge A reference to the Challenge data where to output the read value. - * - * @retval kErrorNone Successfully read the Challenge TLV. - * @retval kErrorNotFound Challenge TLV was not found in the message. - * @retval kErrorParse Challenge TLV was found but could not be parsed. - * - */ - Error ReadChallenge(const Message &aMessage, Challenge &aChallenge); - - /** - * This method appends a Response TLV to a message. - * - * @param[in] aMessage A reference to the message. - * @param[in] aResponse A reference to the Response data. - * - * @retval kErrorNone Successfully appended the Response TLV. - * @retval kErrorNoBufs Insufficient buffers available to append the Response TLV. - * - */ - Error AppendResponse(Message &aMessage, const Challenge &aResponse); - - /** - * This method reads Response TLV from a message. - * - * @param[in] aMessage A reference to the message. - * @param[out] aResponse A reference to the Response data where to output the read value. - * - * @retval kErrorNone Successfully read the Response TLV. - * @retval kErrorNotFound Response TLV was not found in the message. - * @retval kErrorParse Response TLV was found but could not be parsed. - * - */ - Error ReadResponse(const Message &aMessage, Challenge &aResponse); - - /** - * This method appends a Link Frame Counter TLV to a message. - * - * @param[in] aMessage A reference to the message. - * - * @retval kErrorNone Successfully appended the Link Frame Counter TLV. - * @retval kErrorNoBufs Insufficient buffers available to append the Link Frame Counter TLV. - * - */ - Error AppendLinkFrameCounter(Message &aMessage); - - /** - * This method reads Link and MLE Frame Counters from a message. - * - * Link Frame Counter TLV must be present in the message and its value is read into @p aLinkFrameCounter. If MLE - * Frame Counter TLV is present in the message, its value is read into @p aMleFrameCounter. If the MLE Frame - * Counter TLV is not present in the message, then @p aMleFrameCounter is set to same value as @p aLinkFrameCounter. - * - * @param[in] aMessage A reference to the message to read from. - * @param[out] aLinkFrameCounter A reference to an `uint32_t` to output the Link Frame Counter. - * @param[out] aMleFrameCounter A reference to an `uint32_t` to output the MLE Frame Counter. - * - * @retval kErrorNone Successfully read the counters. - * @retval kErrorNotFound Link Frame Counter TLV was not found in the message. - * @retval kErrorParse TLVs are not well-formed. - * - */ - Error ReadFrameCounters(const Message &aMessage, uint32_t &aLinkFrameCounter, uint32_t &aMleFrameCounter) const; - - /** - * This method appends an MLE Frame Counter TLV to a message. - * - * @param[in] aMessage A reference to the message. - * - * @retval kErrorNone Successfully appended the Frame Counter TLV. - * @retval kErrorNoBufs Insufficient buffers available to append the MLE Frame Counter TLV. - * - */ - Error AppendMleFrameCounter(Message &aMessage); - - /** - * This method appends an Address16 TLV to a message. - * - * @param[in] aMessage A reference to the message. - * @param[in] aRloc16 The RLOC16 value. - * - * @retval kErrorNone Successfully appended the Address16 TLV. - * @retval kErrorNoBufs Insufficient buffers available to append the Address16 TLV. - * - */ - Error AppendAddress16(Message &aMessage, uint16_t aRloc16); - - /** - * This method appends a Network Data TLV to the message. - * - * @param[in] aMessage A reference to the message. - * @param[in] aType The Network Data type to append, full set or stable subset. - * - * @retval kErrorNone Successfully appended the Network Data TLV. - * @retval kErrorNoBufs Insufficient buffers available to append the Network Data TLV. - * - */ - Error AppendNetworkData(Message &aMessage, NetworkData::Type aType); - - /** - * This method appends a TLV Request TLV to a message. - * - * @param[in] aMessage A reference to the message. - * @param[in] aTlvs A pointer to the list of TLV types. - * @param[in] aTlvsLength The number of TLV types in @p aTlvs - * - * @retval kErrorNone Successfully appended the TLV Request TLV. - * @retval kErrorNoBufs Insufficient buffers available to append the TLV Request TLV. - * - */ - Error AppendTlvRequest(Message &aMessage, const uint8_t *aTlvs, uint8_t aTlvsLength); - - /** - * This method reads TLV Request TLV from a message. - * - * @param[in] aMessage A reference to the message. - * @param[out] aRequestedTlvs A reference to output the read list of requested TLVs. - * - * @retval kErrorNone Successfully read the TLV. - * @retval kErrorNotFound TLV was not found in the message. - * @retval kErrorParse TLV was found but could not be parsed. - * - */ - Error FindTlvRequest(const Message &aMessage, RequestedTlvs &aRequestedTlvs); - - /** - * This method appends a Leader Data TLV to a message. - * - * @param[in] aMessage A reference to the message. - * - * @retval kErrorNone Successfully appended the Leader Data TLV. - * @retval kErrorNoBufs Insufficient buffers available to append the Leader Data TLV. - * - */ - Error AppendLeaderData(Message &aMessage); - - /** - * This method reads Leader Data TLV from a message. - * - * @param[in] aMessage A reference to the message. - * @param[out] aLeaderData A reference to output the Leader Data. - * - * @retval kErrorNone Successfully read the TLV. - * @retval kErrorNotFound TLV was not found in the message. - * @retval kErrorParse TLV was found but could not be parsed. - * - */ - Error ReadLeaderData(const Message &aMessage, LeaderData &aLeaderData); - - /** - * This method appends a Scan Mask TLV to a message. - * - * @param[in] aMessage A reference to the message. - * @param[in] aScanMask The Scan Mask value. - * - * @retval kErrorNone Successfully appended the Scan Mask TLV. - * @retval kErrorNoBufs Insufficient buffers available to append the Scan Mask TLV. - * - */ - Error AppendScanMask(Message &aMessage, uint8_t aScanMask); - - /** - * This method appends a Status TLV to a message. - * - * @param[in] aMessage A reference to the message. - * @param[in] aStatus The Status value. - * - * @retval kErrorNone Successfully appended the Status TLV. - * @retval kErrorNoBufs Insufficient buffers available to append the Status TLV. - * - */ - Error AppendStatus(Message &aMessage, StatusTlv::Status aStatus); - - /** - * This method appends a Link Margin TLV to a message. - * - * @param[in] aMessage A reference to the message. - * @param[in] aLinkMargin The Link Margin value. - * - * @retval kErrorNone Successfully appended the Link Margin TLV. - * @retval kErrorNoBufs Insufficient buffers available to append the Link Margin TLV. - * - */ - Error AppendLinkMargin(Message &aMessage, uint8_t aLinkMargin); - - /** - * This method appends a Version TLV to a message. - * - * @param[in] aMessage A reference to the message. - * - * @retval kErrorNone Successfully appended the Version TLV. - * @retval kErrorNoBufs Insufficient buffers available to append the Version TLV. - * - */ - Error AppendVersion(Message &aMessage); - - /** - * This method appends an Address Registration TLV to a message. - * - * @param[in] aMessage A reference to the message. - * @param[in] aMode Determines which addresses to include in the TLV (see `AddressRegistrationMode`). - * - * @retval kErrorNone Successfully appended the Address Registration TLV. - * @retval kErrorNoBufs Insufficient buffers available to append the Address Registration TLV. - * - */ - Error AppendAddressRegistration(Message &aMessage, AddressRegistrationMode aMode = kAppendAllAddresses); - -#if OPENTHREAD_CONFIG_TIME_SYNC_ENABLE - /** - * This method appends a Time Request TLV to a message. - * - * @param[in] aMessage A reference to the message. - * - * @retval kErrorNone Successfully appended the Time Request TLV. - * @retval kErrorNoBufs Insufficient buffers available to append the Time Request TLV. - * - */ - Error AppendTimeRequest(Message &aMessage); - - /** - * This method appends a Time Parameter TLV to a message. - * - * @param[in] aMessage A reference to the message. - * - * @retval kErrorNone Successfully appended the Time Parameter TLV. - * @retval kErrorNoBufs Insufficient buffers available to append the Time Parameter TLV. - * - */ - Error AppendTimeParameter(Message &aMessage); - - /** - * This method appends a XTAL Accuracy TLV to a message. - * - * @param[in] aMessage A reference to the message. - * - * @retval kErrorNone Successfully appended the XTAL Accuracy TLV. - * @retval kErrorNoBufs Insufficient buffers available to append the XTAl Accuracy TLV. - * - */ - Error AppendXtalAccuracy(Message &aMessage); -#endif // OPENTHREAD_CONFIG_TIME_SYNC_ENABLE - -#if (OPENTHREAD_FTD && OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE) || OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE - /** - * This method appends a CSL Channel TLV to a message. - * - * @param[in] aMessage A reference to the message. - * - * @retval kErrorNone Successfully appended the CSL Channel TLV. - * @retval kErrorNoBufs Insufficient buffers available to append the CSL Channel TLV. - * - */ - Error AppendCslChannel(Message &aMessage); - - /** - * This method appends a CSL Sync Timeout TLV to a message. - * - * @param[in] aMessage A reference to the message. - * - * @retval kErrorNone Successfully appended the CSL Timeout TLV. - * @retval kErrorNoBufs Insufficient buffers available to append the CSL Timeout TLV. - * - */ - Error AppendCslTimeout(Message &aMessage); -#endif // (OPENTHREAD_FTD && OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE) || OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE - -#if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE || OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE - /** - * This method appends a CSL Clock Accuracy TLV to a message. - * - * @param[in] aMessage A reference to the message. - * - * @retval kErrorNone Successfully appended the CSL Accuracy TLV. - * @retval kErrorNoBufs Insufficient buffers available to append the CSL Accuracy TLV. - */ - Error AppendCslClockAccuracy(Message &aMessage); -#endif - - /** - * This method appends a Active Timestamp TLV to a message. - * - * @param[in] aMessage A reference to the message. - * - * @retval kErrorNone Successfully appended the Active Timestamp TLV. - * @retval kErrorNoBufs Insufficient buffers available to append the Active Timestamp TLV. - * - */ - Error AppendActiveTimestamp(Message &aMessage); - - /** - * This method appends a Pending Timestamp TLV to a message. - * - * @param[in] aMessage A reference to the message. - * - * @retval kErrorNone Successfully appended the Pending Timestamp TLV. - * @retval kErrorNoBufs Insufficient buffers available to append the Pending Timestamp TLV. - * - */ - Error AppendPendingTimestamp(Message &aMessage); - /** * This method checks if the destination is reachable. * @@ -1434,18 +1485,6 @@ protected: */ Error SendChildUpdateResponse(const uint8_t *aTlvs, uint8_t aNumTlvs, const Challenge &aChallenge); - /** - * This method submits an MLE message to the UDP socket. - * - * @param[in] aMessage A reference to the message. - * @param[in] aDestination A reference to the IPv6 address of the destination. - * - * @retval kErrorNone Successfully submitted the MLE message. - * @retval kErrorNoBufs Insufficient buffers to form the rest of the MLE message. - * - */ - Error SendMessage(Message &aMessage, const Ip6::Address &aDestination); - /** * This method sets the RLOC16 assigned to the Thread interface. * @@ -1476,19 +1515,6 @@ protected: */ void SetLeaderData(uint32_t aPartitionId, uint8_t aWeighting, uint8_t aLeaderRouterId); - /** - * This method adds a message to the message queue. The queued message will be transmitted after given delay. - * - * @param[in] aMessage The message to transmit after given delay. - * @param[in] aDestination The IPv6 address of the recipient of the message. - * @param[in] aDelay The delay in milliseconds before transmission of the message. - * - * @retval kErrorNone Successfully queued the message to transmit after the delay. - * @retval kErrorNoBufs Insufficient buffers to queue the message. - * - */ - Error AddDelayedResponse(Message &aMessage, const Ip6::Address &aDestination, uint16_t aDelay); - #if OT_SHOULD_LOG_AT(OT_LOG_LEVEL_INFO) /** * This static method emits a log message with an IPv6 address. @@ -1763,13 +1789,12 @@ private: void HandleAttachTimer(void); static void HandleDelayedResponseTimer(Timer &aTimer); void HandleDelayedResponseTimer(void); - void SendDelayedResponse(Message &aMessage, const DelayedResponseMetadata &aMetadata); + void SendDelayedResponse(TxMessage &aMessage, const DelayedResponseMetadata &aMetadata); static void HandleMessageTransmissionTimer(Timer &aTimer); void HandleMessageTransmissionTimer(void); static void HandleUdpReceive(void *aContext, otMessage *aMessage, const otMessageInfo *aMessageInfo); void HandleUdpReceive(Message &aMessage, const Ip6::MessageInfo &aMessageInfo); void ScheduleMessageTransmissionTimer(void); - Error ReadChallengeOrResponse(const Message &aMessage, uint8_t aTlvType, Challenge &aBuffer); void HandleAdvertisement(RxInfo &aRxInfo); void HandleChildIdResponse(RxInfo &aRxInfo); diff --git a/src/core/thread/mle_router.cpp b/src/core/thread/mle_router.cpp index bde40428c..86335e05f 100644 --- a/src/core/thread/mle_router.cpp +++ b/src/core/thread/mle_router.cpp @@ -481,7 +481,7 @@ void MleRouter::SendAdvertisement(void) { Error error = kErrorNone; Ip6::Address destination; - Message * message = nullptr; + TxMessage * message = nullptr; // Suppress MLE Advertisements when trying to attach to a better partition. // @@ -497,8 +497,8 @@ void MleRouter::SendAdvertisement(void) VerifyOrExit(!mAddressSolicitPending); VerifyOrExit((message = NewMleMessage(kCommandAdvertisement)) != nullptr, error = kErrorNoBufs); - SuccessOrExit(error = AppendSourceAddress(*message)); - SuccessOrExit(error = AppendLeaderData(*message)); + SuccessOrExit(error = message->AppendSourceAddressTlv()); + SuccessOrExit(error = message->AppendLeaderDataTlv()); switch (mRole) { @@ -512,12 +512,12 @@ void MleRouter::SendAdvertisement(void) case kRoleRouter: case kRoleLeader: - SuccessOrExit(error = AppendRoute(*message)); + SuccessOrExit(error = message->AppendRouteTlv()); break; } destination.SetToLinkLocalAllNodesMulticast(); - SuccessOrExit(error = SendMessage(*message, destination)); + SuccessOrExit(error = message->SendTo(destination)); Log(kMessageSend, kTypeAdvertisement, destination); @@ -532,13 +532,13 @@ Error MleRouter::SendLinkRequest(Neighbor *aNeighbor) static const uint8_t routerTlvs[] = {Tlv::kLinkMargin}; static const uint8_t validNeighborTlvs[] = {Tlv::kLinkMargin, Tlv::kRoute}; Error error = kErrorNone; - Message * message; + TxMessage * message; Ip6::Address destination; destination.Clear(); VerifyOrExit((message = NewMleMessage(kCommandLinkRequest)) != nullptr, error = kErrorNoBufs); - SuccessOrExit(error = AppendVersion(*message)); + SuccessOrExit(error = message->AppendVersionTlv()); switch (mRole) { @@ -547,32 +547,32 @@ Error MleRouter::SendLinkRequest(Neighbor *aNeighbor) OT_UNREACHABLE_CODE(break); case kRoleDetached: - SuccessOrExit(error = AppendTlvRequest(*message, detachedTlvs, sizeof(detachedTlvs))); + SuccessOrExit(error = message->AppendTlvRequestTlv(detachedTlvs, sizeof(detachedTlvs))); break; case kRoleChild: - SuccessOrExit(error = AppendSourceAddress(*message)); - SuccessOrExit(error = AppendLeaderData(*message)); + SuccessOrExit(error = message->AppendSourceAddressTlv()); + SuccessOrExit(error = message->AppendLeaderDataTlv()); break; case kRoleRouter: case kRoleLeader: if (aNeighbor == nullptr || !aNeighbor->IsStateValid()) { - SuccessOrExit(error = AppendTlvRequest(*message, routerTlvs, sizeof(routerTlvs))); + SuccessOrExit(error = message->AppendTlvRequestTlv(routerTlvs, sizeof(routerTlvs))); } else { - SuccessOrExit(error = AppendTlvRequest(*message, validNeighborTlvs, sizeof(validNeighborTlvs))); + SuccessOrExit(error = message->AppendTlvRequestTlv(validNeighborTlvs, sizeof(validNeighborTlvs))); } - SuccessOrExit(error = AppendSourceAddress(*message)); - SuccessOrExit(error = AppendLeaderData(*message)); + SuccessOrExit(error = message->AppendSourceAddressTlv()); + SuccessOrExit(error = message->AppendLeaderDataTlv()); break; } #if OPENTHREAD_CONFIG_TIME_SYNC_ENABLE - SuccessOrExit(error = AppendTimeRequest(*message)); + SuccessOrExit(error = message->AppendTimeRequestTlv()); #endif if (aNeighbor == nullptr) @@ -580,7 +580,7 @@ Error MleRouter::SendLinkRequest(Neighbor *aNeighbor) mChallenge.GenerateRandom(); mChallengeTimeout = (((2 * kMaxResponseDelay) + kStateUpdatePeriod - 1) / kStateUpdatePeriod); - SuccessOrExit(error = AppendChallenge(*message, mChallenge)); + SuccessOrExit(error = message->AppendChallengeTlv(mChallenge)); destination.SetToLinkLocalAllRoutersMulticast(); } else @@ -588,20 +588,21 @@ Error MleRouter::SendLinkRequest(Neighbor *aNeighbor) if (!aNeighbor->IsStateValid()) { aNeighbor->GenerateChallenge(); - SuccessOrExit(error = AppendChallenge(*message, aNeighbor->GetChallenge(), aNeighbor->GetChallengeSize())); + SuccessOrExit(error = + message->AppendChallengeTlv(aNeighbor->GetChallenge(), aNeighbor->GetChallengeSize())); } else { Challenge challenge; challenge.GenerateRandom(); - SuccessOrExit(error = AppendChallenge(*message, challenge)); + SuccessOrExit(error = message->AppendChallengeTlv(challenge)); } destination.SetToLinkLocalAddress(aNeighbor->GetExtAddress()); } - SuccessOrExit(error = SendMessage(*message, destination)); + SuccessOrExit(error = message->SendTo(destination)); Log(kMessageSend, kTypeLinkRequest, destination); @@ -627,14 +628,14 @@ void MleRouter::HandleLinkRequest(RxInfo &aRxInfo) VerifyOrExit(!IsAttaching(), error = kErrorInvalidState); // Challenge - SuccessOrExit(error = ReadChallenge(aRxInfo.mMessage, challenge)); + SuccessOrExit(error = aRxInfo.mMessage.ReadChallengeTlv(challenge)); // Version SuccessOrExit(error = Tlv::Find(aRxInfo.mMessage, version)); VerifyOrExit(version >= OT_THREAD_VERSION_1_1, error = kErrorParse); // Leader Data - switch (ReadLeaderData(aRxInfo.mMessage, leaderData)) + switch (aRxInfo.mMessage.ReadLeaderDataTlv(leaderData)) { case kErrorNone: VerifyOrExit(leaderData.GetPartitionId() == mLeaderData.GetPartitionId(), error = kErrorInvalidState); @@ -689,7 +690,7 @@ void MleRouter::HandleLinkRequest(RxInfo &aRxInfo) } // TLV Request - switch (FindTlvRequest(aRxInfo.mMessage, requestedTlvs)) + switch (aRxInfo.mMessage.ReadTlvRequestTlv(requestedTlvs)) { case kErrorNone: break; @@ -727,28 +728,28 @@ Error MleRouter::SendLinkAccept(const Ip6::MessageInfo &aMessageInfo, { Error error = kErrorNone; static const uint8_t routerTlvs[] = {Tlv::kLinkMargin}; - Message * message; + TxMessage * message; Command command; uint8_t linkMargin; command = (aNeighbor == nullptr || aNeighbor->IsStateValid()) ? kCommandLinkAccept : kCommandLinkAcceptAndRequest; VerifyOrExit((message = NewMleMessage(command)) != nullptr, error = kErrorNoBufs); - SuccessOrExit(error = AppendVersion(*message)); - SuccessOrExit(error = AppendSourceAddress(*message)); - SuccessOrExit(error = AppendResponse(*message, aChallenge)); - SuccessOrExit(error = AppendLinkFrameCounter(*message)); - SuccessOrExit(error = AppendMleFrameCounter(*message)); + SuccessOrExit(error = message->AppendVersionTlv()); + SuccessOrExit(error = message->AppendSourceAddressTlv()); + SuccessOrExit(error = message->AppendResponseTlv(aChallenge)); + SuccessOrExit(error = message->AppendLinkFrameCounterTlv()); + SuccessOrExit(error = message->AppendMleFrameCounterTlv()); // always append a link margin, regardless of whether or not it was requested linkMargin = LinkQualityInfo::ConvertRssToLinkMargin(Get().GetNoiseFloor(), aMessageInfo.GetThreadLinkInfo()->GetRss()); - SuccessOrExit(error = AppendLinkMargin(*message, linkMargin)); + SuccessOrExit(error = message->AppendLinkMarginTlv(linkMargin)); if (aNeighbor != nullptr && IsActiveRouter(aNeighbor->GetRloc16())) { - SuccessOrExit(error = AppendLeaderData(*message)); + SuccessOrExit(error = message->AppendLeaderDataTlv()); } for (uint8_t i = 0; i < aRequestedTlvs.mNumTlvs; i++) @@ -756,12 +757,12 @@ Error MleRouter::SendLinkAccept(const Ip6::MessageInfo &aMessageInfo, switch (aRequestedTlvs.mTlvs[i]) { case Tlv::kRoute: - SuccessOrExit(error = AppendRoute(*message, aNeighbor)); + SuccessOrExit(error = message->AppendRouteTlv(aNeighbor)); break; case Tlv::kAddress16: VerifyOrExit(aNeighbor != nullptr, error = kErrorDrop); - SuccessOrExit(error = AppendAddress16(*message, aNeighbor->GetRloc16())); + SuccessOrExit(error = message->AppendAddress16Tlv(aNeighbor->GetRloc16())); break; case Tlv::kLinkMargin: @@ -776,8 +777,8 @@ Error MleRouter::SendLinkAccept(const Ip6::MessageInfo &aMessageInfo, { aNeighbor->GenerateChallenge(); - SuccessOrExit(error = AppendChallenge(*message, aNeighbor->GetChallenge(), aNeighbor->GetChallengeSize())); - SuccessOrExit(error = AppendTlvRequest(*message, routerTlvs, sizeof(routerTlvs))); + SuccessOrExit(error = message->AppendChallengeTlv(aNeighbor->GetChallenge(), aNeighbor->GetChallengeSize())); + SuccessOrExit(error = message->AppendTlvRequestTlv(routerTlvs, sizeof(routerTlvs))); aNeighbor->SetLastHeard(TimerMilli::GetNow()); aNeighbor->SetState(Neighbor::kStateLinkRequest); } @@ -791,14 +792,14 @@ Error MleRouter::SendLinkAccept(const Ip6::MessageInfo &aMessageInfo, if (aMessageInfo.GetSockAddr().IsMulticast()) { - SuccessOrExit(error = AddDelayedResponse(*message, aMessageInfo.GetPeerAddr(), - 1 + Random::NonCrypto::GetUint16InRange(0, kMaxResponseDelay))); + SuccessOrExit(error = message->SendAfterDelay(aMessageInfo.GetPeerAddr(), + 1 + Random::NonCrypto::GetUint16InRange(0, kMaxResponseDelay))); Log(kMessageDelay, kTypeLinkAccept, aMessageInfo.GetPeerAddr()); } else { - SuccessOrExit(error = SendMessage(*message, aMessageInfo.GetPeerAddr())); + SuccessOrExit(error = message->SendTo(aMessageInfo.GetPeerAddr())); Log(kMessageSend, kTypeLinkAccept, aMessageInfo.GetPeerAddr()); } @@ -854,7 +855,7 @@ Error MleRouter::HandleLinkAccept(RxInfo &aRxInfo, bool aRequest) neighborState = (router != nullptr) ? router->GetState() : Neighbor::kStateInvalid; // Response - SuccessOrExit(error = ReadResponse(aRxInfo.mMessage, response)); + SuccessOrExit(error = aRxInfo.mMessage.ReadResponseTlv(response)); // verify response switch (neighborState) @@ -886,7 +887,7 @@ Error MleRouter::HandleLinkAccept(RxInfo &aRxInfo, bool aRequest) VerifyOrExit(version >= OT_THREAD_VERSION_1_1, error = kErrorParse); // Link and MLE Frame Counters - SuccessOrExit(error = ReadFrameCounters(aRxInfo.mMessage, linkFrameCounter, mleFrameCounter)); + SuccessOrExit(error = aRxInfo.mMessage.ReadFrameCounterTlvs(linkFrameCounter, mleFrameCounter)); // Link Margin switch (Tlv::Find(aRxInfo.mMessage, linkMargin)) @@ -915,7 +916,7 @@ Error MleRouter::HandleLinkAccept(RxInfo &aRxInfo, bool aRequest) VerifyOrExit(GetRloc16() == address16, error = kErrorDrop); // Leader Data - SuccessOrExit(error = ReadLeaderData(aRxInfo.mMessage, leaderData)); + SuccessOrExit(error = aRxInfo.mMessage.ReadLeaderDataTlv(leaderData)); SetLeaderData(leaderData.GetPartitionId(), leaderData.GetWeighting(), leaderData.GetLeaderRouterId()); // Route @@ -950,7 +951,7 @@ Error MleRouter::HandleLinkAccept(RxInfo &aRxInfo, bool aRequest) VerifyOrExit(router != nullptr); // Leader Data - SuccessOrExit(error = ReadLeaderData(aRxInfo.mMessage, leaderData)); + SuccessOrExit(error = aRxInfo.mMessage.ReadLeaderDataTlv(leaderData)); VerifyOrExit(leaderData.GetPartitionId() == mLeaderData.GetPartitionId()); if (mRetrieveNewNetworkData || @@ -1014,10 +1015,10 @@ Error MleRouter::HandleLinkAccept(RxInfo &aRxInfo, bool aRequest) RequestedTlvs requestedTlvs; // Challenge - SuccessOrExit(error = ReadChallenge(aRxInfo.mMessage, challenge)); + SuccessOrExit(error = aRxInfo.mMessage.ReadChallengeTlv(challenge)); // TLV Request - switch (FindTlvRequest(aRxInfo.mMessage, requestedTlvs)) + switch (aRxInfo.mMessage.ReadTlvRequestTlv(requestedTlvs)) { case kErrorNone: break; @@ -1227,7 +1228,7 @@ Error MleRouter::HandleAdvertisement(RxInfo &aRxInfo) SuccessOrExit(error = Tlv::Find(aRxInfo.mMessage, sourceAddress)); // Leader Data - SuccessOrExit(error = ReadLeaderData(aRxInfo.mMessage, leaderData)); + SuccessOrExit(error = aRxInfo.mMessage.ReadLeaderDataTlv(leaderData)); // Route Data (optional) if (Tlv::FindTlv(aRxInfo.mMessage, route) == kErrorNone) @@ -1707,7 +1708,7 @@ void MleRouter::HandleParentRequest(RxInfo &aRxInfo) } // Challenge - SuccessOrExit(error = ReadChallenge(aRxInfo.mMessage, challenge)); + SuccessOrExit(error = aRxInfo.mMessage.ReadChallengeTlv(challenge)); child = mChildTable.FindChild(extAddr, Child::kInStateAnyExceptInvalid); @@ -1985,38 +1986,38 @@ void MleRouter::SendParentResponse(Child *aChild, const Challenge &aChallenge, b { Error error = kErrorNone; Ip6::Address destination; - Message * message; + TxMessage * message; uint16_t delay; VerifyOrExit((message = NewMleMessage(kCommandParentResponse)) != nullptr, error = kErrorNoBufs); message->SetDirectTransmission(); - SuccessOrExit(error = AppendSourceAddress(*message)); - SuccessOrExit(error = AppendLeaderData(*message)); - SuccessOrExit(error = AppendLinkFrameCounter(*message)); - SuccessOrExit(error = AppendMleFrameCounter(*message)); - SuccessOrExit(error = AppendResponse(*message, aChallenge)); + SuccessOrExit(error = message->AppendSourceAddressTlv()); + SuccessOrExit(error = message->AppendLeaderDataTlv()); + SuccessOrExit(error = message->AppendLinkFrameCounterTlv()); + SuccessOrExit(error = message->AppendMleFrameCounterTlv()); + SuccessOrExit(error = message->AppendResponseTlv(aChallenge)); #if OPENTHREAD_CONFIG_TIME_SYNC_ENABLE if (aChild->IsTimeSyncEnabled()) { - SuccessOrExit(error = AppendTimeParameter(*message)); + SuccessOrExit(error = message->AppendTimeParameterTlv()); } #endif #if OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE if (aChild->IsThreadVersionCslCapable()) { - SuccessOrExit(error = AppendCslClockAccuracy(*message)); + SuccessOrExit(error = message->AppendCslClockAccuracyTlv()); } #endif aChild->GenerateChallenge(); - SuccessOrExit(error = AppendChallenge(*message, aChild->GetChallenge(), aChild->GetChallengeSize())); - error = AppendLinkMargin(*message, aChild->GetLinkInfo().GetLinkMargin()); + SuccessOrExit(error = message->AppendChallengeTlv(aChild->GetChallenge(), aChild->GetChallengeSize())); + error = message->AppendLinkMarginTlv(aChild->GetLinkInfo().GetLinkMargin()); SuccessOrExit(error); - SuccessOrExit(error = AppendConnectivity(*message)); - SuccessOrExit(error = AppendVersion(*message)); + SuccessOrExit(error = message->AppendConnectivityTlv()); + SuccessOrExit(error = message->AppendVersionTlv()); destination.SetToLinkLocalAddress(aChild->GetExtAddress()); @@ -2029,7 +2030,7 @@ void MleRouter::SendParentResponse(Child *aChild, const Challenge &aChallenge, b delay = 1 + Random::NonCrypto::GetUint16InRange(0, kParentResponseMaxDelayAll); } - SuccessOrExit(error = AddDelayedResponse(*message, destination, delay)); + SuccessOrExit(error = message->SendAfterDelay(destination, delay)); Log(kMessageDelay, kTypeParentResponse, destination); @@ -2299,7 +2300,7 @@ void MleRouter::HandleChildIdRequest(RxInfo &aRxInfo) VerifyOrExit(version >= OT_THREAD_VERSION_1_1, error = kErrorParse); // Response - SuccessOrExit(error = ReadResponse(aRxInfo.mMessage, response)); + SuccessOrExit(error = aRxInfo.mMessage.ReadResponseTlv(response)); VerifyOrExit(response.Matches(child->GetChallenge(), child->GetChallengeSize()), error = kErrorSecurity); // Remove existing MLE messages @@ -2309,7 +2310,7 @@ void MleRouter::HandleChildIdRequest(RxInfo &aRxInfo) Get().RemoveMessages(*child, Message::kSubTypeMleDataResponse); // Link-Layer and MLE Frame Counters - SuccessOrExit(error = ReadFrameCounters(aRxInfo.mMessage, linkFrameCounter, mleFrameCounter)); + SuccessOrExit(error = aRxInfo.mMessage.ReadFrameCounterTlvs(linkFrameCounter, mleFrameCounter)); // Mode SuccessOrExit(error = Tlv::Find(aRxInfo.mMessage, modeBitmask)); @@ -2319,7 +2320,7 @@ void MleRouter::HandleChildIdRequest(RxInfo &aRxInfo) SuccessOrExit(error = Tlv::Find(aRxInfo.mMessage, timeout)); // TLV Request - SuccessOrExit(error = FindTlvRequest(aRxInfo.mMessage, requestedTlvs)); + SuccessOrExit(error = aRxInfo.mMessage.ReadTlvRequestTlv(requestedTlvs)); VerifyOrExit(requestedTlvs.mNumTlvs <= Child::kMaxRequestTlvs, error = kErrorParse); // Active Timestamp @@ -2453,7 +2454,7 @@ void MleRouter::HandleChildUpdateRequest(RxInfo &aRxInfo) mode.Set(modeBitmask); // Challenge - switch (ReadChallenge(aRxInfo.mMessage, challenge)) + switch (aRxInfo.mMessage.ReadChallengeTlv(challenge)) { case kErrorNone: tlvs[tlvslength++] = Tlv::kResponse; @@ -2513,7 +2514,7 @@ void MleRouter::HandleChildUpdateRequest(RxInfo &aRxInfo) } // Leader Data - switch (ReadLeaderData(aRxInfo.mMessage, leaderData)) + switch (aRxInfo.mMessage.ReadLeaderDataTlv(leaderData)) { case kErrorNone: child->SetNetworkDataVersion(leaderData.GetDataVersion(child->GetNetworkDataType())); @@ -2545,7 +2546,7 @@ void MleRouter::HandleChildUpdateRequest(RxInfo &aRxInfo) } // TLV Request - switch (FindTlvRequest(aRxInfo.mMessage, requestedTlvs)) + switch (aRxInfo.mMessage.ReadTlvRequestTlv(requestedTlvs)) { case kErrorNone: VerifyOrExit(requestedTlvs.mNumTlvs <= (kMaxResponseTlvs - tlvslength), error = kErrorParse); @@ -2658,7 +2659,7 @@ void MleRouter::HandleChildUpdateResponse(RxInfo &aRxInfo) child = static_cast(aRxInfo.mNeighbor); // Response - switch (ReadResponse(aRxInfo.mMessage, response)) + switch (aRxInfo.mMessage.ReadResponseTlv(response)) { case kErrorNone: VerifyOrExit(response.Matches(child->GetChallenge(), child->GetChallengeSize()), error = kErrorSecurity); @@ -2748,7 +2749,7 @@ void MleRouter::HandleChildUpdateResponse(RxInfo &aRxInfo) } // Leader Data - switch (ReadLeaderData(aRxInfo.mMessage, leaderData)) + switch (aRxInfo.mMessage.ReadLeaderDataTlv(leaderData)) { case kErrorNone: child->SetNetworkDataVersion(leaderData.GetDataVersion(child->GetNetworkDataType())); @@ -2781,7 +2782,7 @@ void MleRouter::HandleDataRequest(RxInfo &aRxInfo) VerifyOrExit(aRxInfo.mNeighbor && aRxInfo.mNeighbor->IsStateValid(), error = kErrorSecurity); // TLV Request - SuccessOrExit(error = FindTlvRequest(aRxInfo.mMessage, requestedTlvs)); + SuccessOrExit(error = aRxInfo.mMessage.ReadTlvRequestTlv(requestedTlvs)); VerifyOrExit(requestedTlvs.mNumTlvs <= sizeof(tlvs), error = kErrorParse); memset(tlvs, Tlv::kInvalid, sizeof(tlvs)); @@ -2987,7 +2988,7 @@ exit: Error MleRouter::SendDiscoveryResponse(const Ip6::Address &aDestination, const Message &aDiscoverRequestMessage) { Error error = kErrorNone; - Message * message; + TxMessage * message; uint16_t startOffset; Tlv tlv; MeshCoP::DiscoveryResponseTlv discoveryResponse; @@ -3071,7 +3072,7 @@ Error MleRouter::SendDiscoveryResponse(const Ip6::Address &aDestination, const M delay = Random::NonCrypto::GetUint16InRange(0, kDiscoveryMaxJitter + 1); - SuccessOrExit(error = AddDelayedResponse(*message, aDestination, delay)); + SuccessOrExit(error = message->SendAfterDelay(aDestination, delay)); Log(kMessageDelay, kTypeDiscoveryResponse, aDestination); @@ -3085,13 +3086,13 @@ Error MleRouter::SendChildIdResponse(Child &aChild) { Error error = kErrorNone; Ip6::Address destination; - Message * message; + TxMessage * message; VerifyOrExit((message = NewMleMessage(kCommandChildIdResponse)) != nullptr, error = kErrorNoBufs); - SuccessOrExit(error = AppendSourceAddress(*message)); - SuccessOrExit(error = AppendLeaderData(*message)); - SuccessOrExit(error = AppendActiveTimestamp(*message)); - SuccessOrExit(error = AppendPendingTimestamp(*message)); + SuccessOrExit(error = message->AppendSourceAddressTlv()); + SuccessOrExit(error = message->AppendLeaderDataTlv()); + SuccessOrExit(error = message->AppendActiveTimestampTlv()); + SuccessOrExit(error = message->AppendPendingTimestampTlv()); if ((aChild.GetRloc16() == 0) || !RouterIdMatch(aChild.GetRloc16(), GetRloc16())) { @@ -3115,26 +3116,26 @@ Error MleRouter::SendChildIdResponse(Child &aChild) aChild.SetRloc16(rloc16); } - SuccessOrExit(error = AppendAddress16(*message, aChild.GetRloc16())); + SuccessOrExit(error = message->AppendAddress16Tlv(aChild.GetRloc16())); for (uint8_t i = 0; i < Child::kMaxRequestTlvs; i++) { switch (aChild.GetRequestTlv(i)) { case Tlv::kNetworkData: - SuccessOrExit(error = AppendNetworkData(*message, aChild.GetNetworkDataType())); + SuccessOrExit(error = message->AppendNetworkDataTlv(aChild.GetNetworkDataType())); break; case Tlv::kRoute: - SuccessOrExit(error = AppendRoute(*message)); + SuccessOrExit(error = message->AppendRouteTlv()); break; case Tlv::kActiveDataset: - SuccessOrExit(error = AppendActiveDataset(*message)); + SuccessOrExit(error = message->AppendActiveDatasetTlv()); break; case Tlv::kPendingDataset: - SuccessOrExit(error = AppendPendingDataset(*message)); + SuccessOrExit(error = message->AppendPendingDatasetTlv()); break; default: @@ -3144,7 +3145,7 @@ Error MleRouter::SendChildIdResponse(Child &aChild) if (!aChild.IsFullThreadDevice()) { - SuccessOrExit(error = AppendChildAddresses(*message, aChild)); + SuccessOrExit(error = message->AppendAddresseRegisterationTlv(aChild)); } SetChildStateToValid(aChild); @@ -3162,7 +3163,7 @@ Error MleRouter::SendChildIdResponse(Child &aChild) #endif destination.SetToLinkLocalAddress(aChild.GetExtAddress()); - SuccessOrExit(error = SendMessage(*message, destination)); + SuccessOrExit(error = message->SendTo(destination)); Log(kMessageSend, kTypeChildIdResponse, destination, aChild.GetRloc16()); @@ -3176,7 +3177,7 @@ Error MleRouter::SendChildUpdateRequest(Child &aChild) static const uint8_t tlvs[] = {Tlv::kTimeout, Tlv::kAddressRegistration}; Error error = kErrorNone; Ip6::Address destination; - Message * message = nullptr; + TxMessage * message = nullptr; if (!aChild.IsRxOnWhenIdle()) { @@ -3201,21 +3202,21 @@ Error MleRouter::SendChildUpdateRequest(Child &aChild) } VerifyOrExit((message = NewMleMessage(kCommandChildUpdateRequest)) != nullptr, error = kErrorNoBufs); - SuccessOrExit(error = AppendSourceAddress(*message)); - SuccessOrExit(error = AppendLeaderData(*message)); - SuccessOrExit(error = AppendNetworkData(*message, aChild.GetNetworkDataType())); - SuccessOrExit(error = AppendActiveTimestamp(*message)); - SuccessOrExit(error = AppendPendingTimestamp(*message)); + SuccessOrExit(error = message->AppendSourceAddressTlv()); + SuccessOrExit(error = message->AppendLeaderDataTlv()); + SuccessOrExit(error = message->AppendNetworkDataTlv(aChild.GetNetworkDataType())); + SuccessOrExit(error = message->AppendActiveTimestampTlv()); + SuccessOrExit(error = message->AppendPendingTimestampTlv()); if (!aChild.IsStateValid()) { - SuccessOrExit(error = AppendTlvRequest(*message, tlvs, sizeof(tlvs))); + SuccessOrExit(error = message->AppendTlvRequestTlv(tlvs, sizeof(tlvs))); aChild.GenerateChallenge(); - SuccessOrExit(error = AppendChallenge(*message, aChild.GetChallenge(), aChild.GetChallengeSize())); + SuccessOrExit(error = message->AppendChallengeTlv(aChild.GetChallenge(), aChild.GetChallengeSize())); } destination.SetToLinkLocalAddress(aChild.GetExtAddress()); - SuccessOrExit(error = SendMessage(*message, destination)); + SuccessOrExit(error = message->SendTo(destination)); if (aChild.IsRxOnWhenIdle()) { @@ -3236,8 +3237,8 @@ void MleRouter::SendChildUpdateResponse(Child * aChild, uint8_t aTlvsLength, const Challenge & aChallenge) { - Error error = kErrorNone; - Message *message; + Error error = kErrorNone; + TxMessage *message; VerifyOrExit((message = NewMleMessage(kCommandChildUpdateResponse)) != nullptr, error = kErrorNoBufs); @@ -3246,59 +3247,59 @@ void MleRouter::SendChildUpdateResponse(Child * aChild, switch (aTlvs[i]) { case Tlv::kStatus: - SuccessOrExit(error = AppendStatus(*message, StatusTlv::kError)); + SuccessOrExit(error = message->AppendStatusTlv(StatusTlv::kError)); break; case Tlv::kAddressRegistration: - SuccessOrExit(error = AppendChildAddresses(*message, *aChild)); + SuccessOrExit(error = message->AppendAddresseRegisterationTlv(*aChild)); break; case Tlv::kLeaderData: - SuccessOrExit(error = AppendLeaderData(*message)); + SuccessOrExit(error = message->AppendLeaderDataTlv()); break; case Tlv::kMode: - SuccessOrExit(error = AppendMode(*message, aChild->GetDeviceMode())); + SuccessOrExit(error = message->AppendModeTlv(aChild->GetDeviceMode())); break; case Tlv::kNetworkData: - SuccessOrExit(error = AppendNetworkData(*message, aChild->GetNetworkDataType())); - SuccessOrExit(error = AppendActiveTimestamp(*message)); - SuccessOrExit(error = AppendPendingTimestamp(*message)); + SuccessOrExit(error = message->AppendNetworkDataTlv(aChild->GetNetworkDataType())); + SuccessOrExit(error = message->AppendActiveTimestampTlv()); + SuccessOrExit(error = message->AppendPendingTimestampTlv()); break; case Tlv::kResponse: - SuccessOrExit(error = AppendResponse(*message, aChallenge)); + SuccessOrExit(error = message->AppendResponseTlv(aChallenge)); break; case Tlv::kSourceAddress: - SuccessOrExit(error = AppendSourceAddress(*message)); + SuccessOrExit(error = message->AppendSourceAddressTlv()); break; case Tlv::kTimeout: - SuccessOrExit(error = AppendTimeout(*message, aChild->GetTimeout())); + SuccessOrExit(error = message->AppendTimeoutTlv(aChild->GetTimeout())); break; case Tlv::kMleFrameCounter: - SuccessOrExit(error = AppendMleFrameCounter(*message)); + SuccessOrExit(error = message->AppendMleFrameCounterTlv()); break; case Tlv::kLinkFrameCounter: - SuccessOrExit(error = AppendLinkFrameCounter(*message)); + SuccessOrExit(error = message->AppendLinkFrameCounterTlv()); break; #if OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE case Tlv::kCslClockAccuracy: if (!aChild->IsRxOnWhenIdle()) { - SuccessOrExit(error = AppendCslClockAccuracy(*message)); + SuccessOrExit(error = message->AppendCslClockAccuracyTlv()); } break; #endif } } - SuccessOrExit(error = SendMessage(*message, aMessageInfo.GetPeerAddr())); + SuccessOrExit(error = message->SendTo(aMessageInfo.GetPeerAddr())); if (aChild == nullptr) { @@ -3321,9 +3322,9 @@ void MleRouter::SendDataResponse(const Ip6::Address &aDestination, { OT_UNUSED_VARIABLE(aRequestMessage); - Error error = kErrorNone; - Message * message = nullptr; - Neighbor *neighbor; + Error error = kErrorNone; + TxMessage *message = nullptr; + Neighbor * neighbor; if (mRetrieveNewNetworkData) { @@ -3332,10 +3333,10 @@ void MleRouter::SendDataResponse(const Ip6::Address &aDestination, } VerifyOrExit((message = NewMleMessage(kCommandDataResponse)) != nullptr, error = kErrorNoBufs); - SuccessOrExit(error = AppendSourceAddress(*message)); - SuccessOrExit(error = AppendLeaderData(*message)); - SuccessOrExit(error = AppendActiveTimestamp(*message)); - SuccessOrExit(error = AppendPendingTimestamp(*message)); + SuccessOrExit(error = message->AppendSourceAddressTlv()); + SuccessOrExit(error = message->AppendLeaderDataTlv()); + SuccessOrExit(error = message->AppendActiveTimestampTlv()); + SuccessOrExit(error = message->AppendPendingTimestampTlv()); for (int i = 0; i < aTlvsLength; i++) { @@ -3343,16 +3344,16 @@ void MleRouter::SendDataResponse(const Ip6::Address &aDestination, { case Tlv::kNetworkData: neighbor = mNeighborTable.FindNeighbor(aDestination); - SuccessOrExit(error = AppendNetworkData(*message, (neighbor != nullptr) ? neighbor->GetNetworkDataType() - : NetworkData::kFullSet)); + SuccessOrExit(error = message->AppendNetworkDataTlv((neighbor != nullptr) ? neighbor->GetNetworkDataType() + : NetworkData::kFullSet)); break; case Tlv::kActiveDataset: - SuccessOrExit(error = AppendActiveDataset(*message)); + SuccessOrExit(error = message->AppendActiveDatasetTlv()); break; case Tlv::kPendingDataset: - SuccessOrExit(error = AppendPendingDataset(*message)); + SuccessOrExit(error = message->AppendPendingDatasetTlv()); break; #if OPENTHREAD_CONFIG_MLE_LINK_METRICS_SUBJECT_ENABLE @@ -3374,12 +3375,12 @@ void MleRouter::SendDataResponse(const Ip6::Address &aDestination, // Remove multicast MLE Data Response from Delayed Message Queue. RemoveDelayedDataResponseMessage(); - SuccessOrExit(error = AddDelayedResponse(*message, aDestination, aDelay)); + SuccessOrExit(error = message->SendAfterDelay(aDestination, aDelay)); Log(kMessageDelay, kTypeDataResponse, aDestination); } else { - SuccessOrExit(error = SendMessage(*message, aDestination)); + SuccessOrExit(error = message->SendTo(aDestination)); Log(kMessageSend, kTypeDataResponse, aDestination); } @@ -3659,7 +3660,7 @@ Error MleRouter::SendAddressSolicit(ThreadStatusTlv::Status aStatus) SuccessOrExit(error = Tlv::Append(*message, aStatus)); #if OPENTHREAD_CONFIG_TIME_SYNC_ENABLE - SuccessOrExit(error = AppendXtalAccuracy(*message)); + SuccessOrExit(error = Tlv::Append(*message, otPlatTimeGetXtalAccuracy())); #endif SuccessOrExit(error = messageInfo.SetSockAddrToRlocPeerAddrToLeaderRloc()); @@ -4104,27 +4105,27 @@ void MleRouter::FillConnectivityTlv(ConnectivityTlv &aTlv) aTlv.SetSedDatagramCount(OPENTHREAD_CONFIG_DEFAULT_SED_DATAGRAM_COUNT); } -Error MleRouter::AppendConnectivity(Message &aMessage) +Error Mle::TxMessage::AppendConnectivityTlv(void) { ConnectivityTlv tlv; tlv.Init(); - FillConnectivityTlv(tlv); + Get().FillConnectivityTlv(tlv); - return tlv.AppendTo(aMessage); + return tlv.AppendTo(*this); } -Error MleRouter::AppendChildAddresses(Message &aMessage, Child &aChild) +Error Mle::TxMessage::AppendAddresseRegisterationTlv(Child &aChild) { Error error; Tlv tlv; AddressRegistrationEntry entry; Lowpan::Context context; uint8_t length = 0; - uint16_t startOffset = aMessage.GetLength(); + uint16_t startOffset = GetLength(); tlv.SetType(Tlv::kAddressRegistration); - SuccessOrExit(error = aMessage.Append(tlv)); + SuccessOrExit(error = Append(tlv)); for (const Ip6::Address &address : aChild.IterateIp6Addresses()) { @@ -4145,12 +4146,12 @@ Error MleRouter::AppendChildAddresses(Message &aMessage, Child &aChild) continue; } - SuccessOrExit(error = aMessage.AppendBytes(&entry, entry.GetLength())); + SuccessOrExit(error = AppendBytes(&entry, entry.GetLength())); length += entry.GetLength(); } tlv.SetLength(length); - aMessage.Write(startOffset, tlv); + Write(startOffset, tlv); exit: return error; @@ -4257,24 +4258,24 @@ void MleRouter::FillRouteTlv(RouteTlv &aTlv, Neighbor *aNeighbor) aTlv.SetRouteDataLength(routerCount); } -Error MleRouter::AppendRoute(Message &aMessage, Neighbor *aNeighbor) +Error Mle::TxMessage::AppendRouteTlv(Neighbor *aNeighbor) { RouteTlv tlv; tlv.Init(); - FillRouteTlv(tlv, aNeighbor); + Get().FillRouteTlv(tlv, aNeighbor); - return tlv.AppendTo(aMessage); + return tlv.AppendTo(*this); } -Error MleRouter::AppendActiveDataset(Message &aMessage) +Error Mle::TxMessage::AppendActiveDatasetTlv(void) { - return Get().AppendMleDatasetTlv(aMessage); + return Get().AppendMleDatasetTlv(*this); } -Error MleRouter::AppendPendingDataset(Message &aMessage) +Error Mle::TxMessage::AppendPendingDatasetTlv(void) { - return Get().AppendMleDatasetTlv(aMessage); + return Get().AppendMleDatasetTlv(*this); } bool MleRouter::HasMinDowngradeNeighborRouters(void) @@ -4465,14 +4466,14 @@ Error MleRouter::SendTimeSync(void) { Error error = kErrorNone; Ip6::Address destination; - Message * message = nullptr; + TxMessage * message = nullptr; VerifyOrExit((message = NewMleMessage(kCommandTimeSync)) != nullptr, error = kErrorNoBufs); message->SetTimeSync(true); destination.SetToLinkLocalAllNodesMulticast(); - SuccessOrExit(error = SendMessage(*message, destination)); + SuccessOrExit(error = message->SendTo(destination)); Log(kMessageSend, kTypeTimeSync, destination); diff --git a/src/core/thread/mle_router.hpp b/src/core/thread/mle_router.hpp index 6645498e6..fc9434a4b 100644 --- a/src/core/thread/mle_router.hpp +++ b/src/core/thread/mle_router.hpp @@ -580,11 +580,6 @@ private: // Network Data). static constexpr uint8_t kRouterUpgradeBorderRouterRequestThreshold = 2; - Error AppendConnectivity(Message &aMessage); - Error AppendChildAddresses(Message &aMessage, Child &aChild); - Error AppendRoute(Message &aMessage, Neighbor *aNeighbor = nullptr); - Error AppendActiveDataset(Message &aMessage); - Error AppendPendingDataset(Message &aMessage); void HandleDetachStart(void); void HandleChildStart(AttachMode aMode); void HandleLinkRequest(RxInfo &aRxInfo); From 25c610a0eded7dfc0cbd1dc766dba7caf0104f79 Mon Sep 17 00:00:00 2001 From: Abtin Keshavarzian Date: Tue, 17 May 2022 15:39:15 -0700 Subject: [PATCH 09/80] [routing-manager] prepare route/prefix info options in place (#7699) This commit contains smaller enhancements in `RoutingManager ` and `Prefix/RouteInfoOption`. This commit changes how the options are added in a message so that we directly construct the option in the message buffer (instead of creating it separately and then copying the bytes). It also changes `RouteInfoOption::IsValid()` to validate that the option length can fit the specified prefix length. --- .../border_router/router_advertisement.cpp | 133 +++++----- .../border_router/router_advertisement.hpp | 247 +++++++++++------- src/core/border_router/routing_manager.cpp | 91 ++++--- tests/toranj/openthread-core-toranj-config.h | 8 + 4 files changed, 287 insertions(+), 192 deletions(-) diff --git a/src/core/border_router/router_advertisement.cpp b/src/core/border_router/router_advertisement.cpp index 66dbdd5dd..d2356c562 100644 --- a/src/core/border_router/router_advertisement.cpp +++ b/src/core/border_router/router_advertisement.cpp @@ -40,9 +40,7 @@ #include "common/code_utils.hpp" namespace ot { - namespace BorderRouter { - namespace RouterAdv { const Option *Option::GetNextOption(const Option *aCurOption, const uint8_t *aBuffer, uint16_t aBufferLength) @@ -70,41 +68,16 @@ exit: return reinterpret_cast(nextOption); } -PrefixInfoOption::PrefixInfoOption(void) - : Option(Type::kPrefixInfo, sizeof(*this) / kLengthUnit) - , mPrefixLength(0) - , mReserved1(0) - , mValidLifetime(0) - , mPreferredLifetime(0) - , mReserved2(0) +//---------------------------------------------------------------------------------------------------------------------- +// PrefixInfoOption + +void PrefixInfoOption::Init(void) { + Clear(); + SetType(Type::kPrefixInfo); + SetSize(sizeof(PrefixInfoOption)); + OT_UNUSED_VARIABLE(mReserved2); - - mPrefix.Clear(); -} - -void PrefixInfoOption::SetOnLink(bool aOnLink) -{ - if (aOnLink) - { - mReserved1 |= kOnLinkFlagMask; - } - else - { - mReserved1 &= ~kOnLinkFlagMask; - } -} - -void PrefixInfoOption::SetAutoAddrConfig(bool aAutoAddrConfig) -{ - if (aAutoAddrConfig) - { - mReserved1 |= kAutoConfigFlagMask; - } - else - { - mReserved1 &= ~kAutoConfigFlagMask; - } } void PrefixInfoOption::SetPrefix(const Ip6::Prefix &aPrefix) @@ -113,62 +86,89 @@ void PrefixInfoOption::SetPrefix(const Ip6::Prefix &aPrefix) mPrefix = AsCoreType(&aPrefix.mPrefix); } -Ip6::Prefix PrefixInfoOption::GetPrefix(void) const +void PrefixInfoOption::GetPrefix(Ip6::Prefix &aPrefix) const { - Ip6::Prefix prefix; - - prefix.Set(mPrefix.GetBytes(), mPrefixLength); - return prefix; + aPrefix.Set(mPrefix.GetBytes(), mPrefixLength); } -RouteInfoOption::RouteInfoOption(void) - : Option(Type::kRouteInfo, 0) - , mPrefixLength(0) - , mReserved(0) - , mRouteLifetime(0) +bool PrefixInfoOption::IsValid(void) const { - OT_UNUSED_VARIABLE(mReserved); + return (GetSize() >= sizeof(*this)) && (mPrefixLength <= Ip6::Prefix::kMaxLength) && + (GetPreferredLifetime() <= GetValidLifetime()); +} - mPrefix.Clear(); +//---------------------------------------------------------------------------------------------------------------------- +// RouteInfoOption + +void RouteInfoOption::Init(void) +{ + Clear(); + SetType(Type::kRouteInfo); } void RouteInfoOption::SetPreference(RoutePreference aPreference) { - mReserved &= ~kPreferenceMask; - mReserved |= (NetworkData::RoutePreferenceToValue(aPreference) << kPreferenceOffset) & kPreferenceMask; + mResvdPrf &= ~kPreferenceMask; + mResvdPrf |= (NetworkData::RoutePreferenceToValue(aPreference) << kPreferenceOffset) & kPreferenceMask; } RouteInfoOption::RoutePreference RouteInfoOption::GetPreference(void) const { - return NetworkData::RoutePreferenceFromValue((mReserved & kPreferenceMask) >> kPreferenceOffset); + return NetworkData::RoutePreferenceFromValue((mResvdPrf & kPreferenceMask) >> kPreferenceOffset); } void RouteInfoOption::SetPrefix(const Ip6::Prefix &aPrefix) { - // The total length (in bytes) of a Router Information Option - // is: (8 bytes fixed option header) + (0, 8, or 16 bytes prefix). - // Because the length of the option must be padded with 8 bytes, - // the length of the prefix (in bits) must be padded with 64 bits. - SetLength((aPrefix.mLength + kLengthUnit * CHAR_BIT - 1) / (kLengthUnit * CHAR_BIT) + 1); - + SetLength(OptionLengthForPrefix(aPrefix.mLength)); mPrefixLength = aPrefix.mLength; - mPrefix = AsCoreType(&aPrefix.mPrefix); + memcpy(GetPrefixBytes(), aPrefix.GetBytes(), aPrefix.GetBytesSize()); } -Ip6::Prefix RouteInfoOption::GetPrefix(void) const +void RouteInfoOption::GetPrefix(Ip6::Prefix &aPrefix) const { - Ip6::Prefix prefix; - - prefix.Set(mPrefix.GetBytes(), mPrefixLength); - return prefix; + aPrefix.Set(GetPrefixBytes(), mPrefixLength); } bool RouteInfoOption::IsValid(void) const { - return (GetLength() == 1 || GetLength() == 2 || GetLength() == 3) && - (mPrefixLength <= OT_IP6_ADDRESS_SIZE * CHAR_BIT) && NetworkData::IsRoutePreferenceValid(GetPreference()); + return (GetSize() >= kMinSize) && (mPrefixLength <= Ip6::Prefix::kMaxLength) && + (GetLength() >= OptionLengthForPrefix(mPrefixLength)) && + NetworkData::IsRoutePreferenceValid(GetPreference()); } +uint8_t RouteInfoOption::OptionLengthForPrefix(uint8_t aPrefixLength) +{ + static constexpr uint8_t kMaxPrefixLenForOptionLen1 = 0; + static constexpr uint8_t kMaxPrefixLenForOptionLen2 = 64; + + uint8_t length; + + // The Option Length can be 1, 2, or 3 depending on the prefix + // length + // + // - 1 when prefix len is zero. + // - 2 when prefix len is less then or equal to 64. + // - 3 otherwise. + + if (aPrefixLength == kMaxPrefixLenForOptionLen1) + { + length = 1; + } + else if (aPrefixLength <= kMaxPrefixLenForOptionLen2) + { + length = 2; + } + else + { + length = 3; + } + + return length; +} + +//---------------------------------------------------------------------------------------------------------------------- +// RouterAdvMessage + void RouterAdvMessage::SetToDefault(void) { mHeader.Clear(); @@ -196,6 +196,9 @@ bool RouterAdvMessage::operator==(const RouterAdvMessage &aOther) const mReachableTime == aOther.mReachableTime && mRetransTimer == aOther.mRetransTimer; } +//---------------------------------------------------------------------------------------------------------------------- +// RouterAdvMessage + RouterSolicitMessage::RouterSolicitMessage(void) { mHeader.Clear(); @@ -203,9 +206,7 @@ RouterSolicitMessage::RouterSolicitMessage(void) } } // namespace RouterAdv - } // namespace BorderRouter - } // namespace ot #endif // OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE diff --git a/src/core/border_router/router_advertisement.hpp b/src/core/border_router/router_advertisement.hpp index 060383b7b..2ca972c3a 100644 --- a/src/core/border_router/router_advertisement.hpp +++ b/src/core/border_router/router_advertisement.hpp @@ -46,6 +46,7 @@ #include #include +#include "common/const_cast.hpp" #include "common/encoding.hpp" #include "common/equatable.hpp" #include "net/icmp6.hpp" @@ -56,14 +57,11 @@ using ot::Encoding::BigEndian::HostSwap16; using ot::Encoding::BigEndian::HostSwap32; namespace ot { - namespace BorderRouter { - namespace RouterAdv { /** - * This class represents the variable length options in Neighbor - * Discovery messages. + * This class represents the variable length options in Neighbor Discovery messages. * * @sa PrefixInfoOption * @sa RouteInfoOption @@ -79,23 +77,10 @@ public: kRouteInfo = 24, ///< Route Information Option. }; - static constexpr uint8_t kLengthUnit = 8; ///< The unit of length in octets. + static constexpr uint16_t kLengthUnit = 8; ///< The unit of length in octets. /** - * This constructor initializes the option with given type and length. - * - * @param[in] aType The type of this option. - * @param[in] aLength The length of this option in unit of 8 octets. - * - */ - explicit Option(Type aType, uint8_t aLength = 0) - : mType(aType) - , mLength(aLength) - { - } - - /** - * This method returns the type of this option. + * This method gets the option type. * * @returns The option type. * @@ -103,20 +88,28 @@ public: Type GetType(void) const { return mType; } /** - * This method sets the size of the option (in bytes). + * This method sets the option type. * - * Since the option must end on their natural 64-bits boundaries, - * the actual length set to the option is padded to (aSize + 7) / 8 * 8. + * @param[in] aType The option type. * - * @param[in] aSize The size of the option in unit of 1 byte. + * + */ + void SetType(Type aType) { mType = aType; } + + /** + * This method sets the length based on a given total option size in bytes. + * + * Th option must end on a 64-bit boundary, so the length is derived as `(aSize + 7) / 8 * 8`. + * + * @param[in] aSize The size of option in bytes. * */ void SetSize(uint16_t aSize) { mLength = static_cast((aSize + kLengthUnit - 1) / kLengthUnit); } /** - * This method returns the size of the option (in bytes). + * This method returns the size of the option in bytes. * - * @returns The size of the option in unit of 1 byte. + * @returns The size of the option in bytes. * */ uint16_t GetSize(void) const { return mLength * kLengthUnit; } @@ -150,72 +143,81 @@ public: static const Option *GetNextOption(const Option *aCurOption, const uint8_t *aBuffer, uint16_t aBufferLength); /** - * This method tells whether this option is valid. + * This method indicates whether or not this option is valid. * - * @return A boolean that indicates whether this option is valid. + * @retval TRUE The option is valid. + * @retval FALSE The option is not valid. * */ bool IsValid(void) const { return mLength > 0; } private: Type mType; // Type of the option. - uint8_t mLength; // Length of the option in unit of 8 octets, - // including the `type` and `length` fields. + uint8_t mLength; // Length of the option in unit of 8 octets, including the `mType` and `mLength` fields. } OT_TOOL_PACKED_END; /** * This class represents the Prefix Information Option. * - * See section 4.6.2 of RFC 4861 for definition of this option. - * https://tools.ietf.org/html/rfc4861#section-4.6.2 + * See section 4.6.2 of RFC 4861 for definition of this option [https://tools.ietf.org/html/rfc4861#section-4.6.2] * */ OT_TOOL_PACKED_BEGIN -class PrefixInfoOption : public Option +class PrefixInfoOption : public Option, private Clearable { public: - /** - * This constructor initializes this option with zero prefix - * length, valid lifetime and preferred lifetime. - * - */ - PrefixInfoOption(void); + static constexpr Type kType = Type::kPrefixInfo; ///< Prefix Information Option Type. /** - * This method returns the on-link flag. - * - * @returns A boolean which indicates whether the on-link flag is set. + * This method initializes the Prefix Info option with proper type and length and sets all other fields to zero. * */ - bool GetOnLink(void) const { return (mReserved1 & kOnLinkFlagMask) != 0; } + void Init(void); + + /** + * This method indicates whether or not the on-link flag is set. + * + * @retval TRUE The on-link flag is set. + * @retval FALSE The on-link flag is not set. + * + */ + bool IsOnLinkFlagSet(void) const { return (mFlags & kOnLinkFlagMask) != 0; } /** * This method sets the on-link (L) flag. * - * @param[in] aOnLink A boolean indicates whether the prefix is on-link or off-link. - * */ - void SetOnLink(bool aOnLink); + void SetOnLinkFlag(void) { mFlags |= kOnLinkFlagMask; } /** - * This method returns the autonomous address-configuration (A) flag. - * - * @returns A boolean which indicates whether the A flag is set. + * This method clears the on-link (L) flag. * */ - bool GetAutoAddrConfig(void) const { return (mReserved1 & kAutoConfigFlagMask) != 0; } + void ClearOnLinkFlag(void) { mFlags &= ~kOnLinkFlagMask; } + + /** + * This method indicates whether or not the autonomous address-configuration (A) flag is set. + * + * @retval TRUE The auto address-config flag is set. + * @retval FALSE The auto address-config flag is not set. + * + */ + bool IsAutoAddrConfigFlagSet(void) const { return (mFlags & kAutoConfigFlagMask) != 0; } /** * This method sets the autonomous address-configuration (A) flag. * - * @param[in] aAutoAddrConfig A boolean indicates whether this prefix can be used - * for SLAAC. - * */ - void SetAutoAddrConfig(bool aAutoAddrConfig); + void SetAutoAddrConfigFlag(void) { mFlags |= kAutoConfigFlagMask; } /** - * This method set the valid lifetime of the prefix in seconds. + * This method clears the autonomous address-configuration (A) flag. + * + */ + void ClearAutoAddrConfigFlag(void) { mFlags &= ~kAutoConfigFlagMask; } + + /** + * This method sets the valid lifetime of the prefix in seconds. * * @param[in] aValidLifetime The valid lifetime in seconds. * @@ -223,7 +225,7 @@ public: void SetValidLifetime(uint32_t aValidLifetime) { mValidLifetime = HostSwap32(aValidLifetime); } /** - * THis method returns the valid lifetime of the prefix in seconds. + * THis method gets the valid lifetime of the prefix in seconds. * * @returns The valid lifetime in seconds. * @@ -255,31 +257,52 @@ public: void SetPrefix(const Ip6::Prefix &aPrefix); /** - * This method returns the prefix in this option. + * This method gets the prefix in this option. * - * @returns The IPv6 prefix in this option. + * @param[out] aPrefix Reference to an `Ip6::Prefix` to return the prefix. * */ - Ip6::Prefix GetPrefix(void) const; + void GetPrefix(Ip6::Prefix &aPrefix) const; /** - * This method tells whether this option is valid. + * This method indicates whether or not the option is valid. * - * @returns A boolean indicates whether this option is valid. + * @retval TRUE The option is valid + * @retval FALSE The option is not valid. * */ - bool IsValid(void) const - { - return (GetSize() == sizeof(*this)) && (mPrefixLength <= OT_IP6_ADDRESS_SIZE * CHAR_BIT) && - (GetPreferredLifetime() <= GetValidLifetime()); - } + bool IsValid(void) const; + + PrefixInfoOption(void) = delete; private: - static constexpr uint8_t kAutoConfigFlagMask = 0x40; // Bit mask of the Automatic Address Configure flag. - static constexpr uint8_t kOnLinkFlagMask = 0x80; // Bit mask of the On-link flag. + // Prefix Information Option + // + // 0 1 2 3 + // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // | Type | Length | Prefix Length |L|A| Reserved1 | + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // | Valid Lifetime | + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // | Preferred Lifetime | + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // | Reserved2 | + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // | | + // + + + // | | + // + Prefix + + // | | + // + + + // | | + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + static constexpr uint8_t kAutoConfigFlagMask = 0x40; // Autonomous address-configuration flag. + static constexpr uint8_t kOnLinkFlagMask = 0x80; // On-link flag. uint8_t mPrefixLength; // The prefix length in bits. - uint8_t mReserved1; // The reserved field. + uint8_t mFlags; // The flags field. uint32_t mValidLifetime; // The valid lifetime of the prefix. uint32_t mPreferredLifetime; // The preferred lifetime of the prefix. uint32_t mReserved2; // The reserved field. @@ -291,25 +314,23 @@ static_assert(sizeof(PrefixInfoOption) == 32, "invalid PrefixInfoOption structur /** * This class represents the Route Information Option. * - * See section 2.3 of RFC 4191 for definition of this option. - * https://tools.ietf.org/html/rfc4191#section-2.3 + * See section 2.3 of RFC 4191 for definition of this option. [https://tools.ietf.org/html/rfc4191#section-2.3] * */ OT_TOOL_PACKED_BEGIN -class RouteInfoOption : public Option +class RouteInfoOption : public Option, private Clearable { public: - /** - * This type represents a route preference. - * - */ - typedef NetworkData::RoutePreference RoutePreference; + static constexpr uint16_t kMinSize = kLengthUnit; ///< Minimum size (in bytes) of a Route Info Option + static constexpr Type kType = Type::kRouteInfo; ///< Route Information Option Type. + + typedef NetworkData::RoutePreference RoutePreference; ///< Route Preference /** - * This constructor initializes this option with zero prefix length. + * This method initializes the option setting the type and clearing (setting to zero) all other fields. * */ - RouteInfoOption(void); + void Init(void); /** * This method sets the route preference. @@ -320,7 +341,7 @@ public: void SetPreference(RoutePreference aPreference); /** - * This method returns the route preference. + * This method gets the route preference. * * @returns The route preference. * @@ -336,7 +357,7 @@ public: void SetRouteLifetime(uint32_t aLifetime) { mRouteLifetime = HostSwap32(aLifetime); } /** - * This method returns Route Lifetime in seconds. + * This method gets Route Lifetime in seconds. * * @returns The Route Lifetime in seconds. * @@ -344,7 +365,7 @@ public: uint32_t GetRouteLifetime(void) const { return HostSwap32(mRouteLifetime); } /** - * This method sets the prefix. + * This method sets the prefix and adjusts the option length based on the prefix length. * * @param[in] aPrefix The prefix contained in this option. * @@ -352,12 +373,12 @@ public: void SetPrefix(const Ip6::Prefix &aPrefix); /** - * This method returns the prefix in this option. + * This method gets the prefix in this option. * - * @returns The IPv6 prefix in this option. + * @param[out] aPrefix Reference to an `Ip6::Prefix` to return the prefix. * */ - Ip6::Prefix GetPrefix(void) const; + void GetPrefix(Ip6::Prefix &aPrefix) const; /** * This method tells whether this option is valid. @@ -367,17 +388,63 @@ public: */ bool IsValid(void) const; + /** + * This static method calculates the minimum option length for a given prefix length. + * + * The option length (which is in unit of 8 octets) can be 1, 2, or 3 depending on the prefix length. It can be 1 + * for a zero prefix length, 2 if the prefix length is not greater than 64, and 3 otherwise. + * + * @param[in] aPrefixLength The prefix length (in bits). + * + * @returns The option length (in unit of 8 octet) for @p aPrefixLength. + * + */ + static uint8_t OptionLengthForPrefix(uint8_t aPrefixLength); + + /** + * This static method calculates the minimum option size (in bytes) for a given prefix length. + * + * @param[in] aPrefixLength The prefix length (in bits). + * + * @returns The option size (in bytes) for @p aPrefixLength. + * + */ + static uint16_t OptionSizeForPrefix(uint8_t aPrefixLength) + { + return kLengthUnit * OptionLengthForPrefix(aPrefixLength); + } + + RouteInfoOption(void) = delete; + private: + // Route Information Option + // + // 0 1 2 3 + // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // | Type | Length | Prefix Length |Resvd|Prf|Resvd| + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // | Route Lifetime | + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // | Prefix (Variable Length) | + // . . + // . . + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + static constexpr uint8_t kPreferenceOffset = 3; static constexpr uint8_t kPreferenceMask = 3 << kPreferenceOffset; - uint8_t mPrefixLength; // The prefix length in bits. - uint8_t mReserved; // The reserved field. - uint32_t mRouteLifetime; // The lifetime in seconds. - Ip6::Address mPrefix; // The prefix. + uint8_t * GetPrefixBytes(void) { return AsNonConst(AsConst(this)->GetPrefixBytes()); } + const uint8_t *GetPrefixBytes(void) const { return reinterpret_cast(this) + sizeof(*this); } + + uint8_t mPrefixLength; // The prefix length in bits. + uint8_t mResvdPrf; // The preference. + uint32_t mRouteLifetime; // The lifetime in seconds. + // Followed by prefix bytes (variable length). + } OT_TOOL_PACKED_END; -static_assert(sizeof(RouteInfoOption) == 24, "invalid RouteInfoOption structure"); +static_assert(sizeof(RouteInfoOption) == 8, "invalid RouteInfoOption structure"); /** * This class implements the Router Advertisement message. @@ -501,9 +568,7 @@ private: static_assert(sizeof(RouterSolicitMessage) == 8, "invalid RouterSolicitMessage structure"); } // namespace RouterAdv - } // namespace BorderRouter - } // namespace ot #endif // OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE diff --git a/src/core/border_router/routing_manager.cpp b/src/core/border_router/routing_manager.cpp index 5f1884e38..6f1ae4a98 100644 --- a/src/core/border_router/routing_manager.cpp +++ b/src/core/border_router/routing_manager.cpp @@ -844,19 +844,21 @@ void RoutingManager::SendRouterAdvertisement(const OmrPrefixArray &aNewOmrPrefix if (aNewOnLinkPrefix != nullptr) { + RouterAdv::PrefixInfoOption *pio; + OT_ASSERT(aNewOnLinkPrefix == &mLocalOnLinkPrefix); + OT_ASSERT(bufferLength + sizeof(RouterAdv::PrefixInfoOption) <= sizeof(buffer)); - RouterAdv::PrefixInfoOption pio; + pio = reinterpret_cast(buffer + bufferLength); - pio.SetOnLink(true); - pio.SetAutoAddrConfig(true); - pio.SetValidLifetime(kDefaultOnLinkPrefixLifetime); - pio.SetPreferredLifetime(kDefaultOnLinkPrefixLifetime); - pio.SetPrefix(*aNewOnLinkPrefix); + pio->Init(); + pio->SetOnLinkFlag(); + pio->SetAutoAddrConfigFlag(); + pio->SetValidLifetime(kDefaultOnLinkPrefixLifetime); + pio->SetPreferredLifetime(kDefaultOnLinkPrefixLifetime); + pio->SetPrefix(*aNewOnLinkPrefix); - OT_ASSERT(bufferLength + pio.GetSize() <= sizeof(buffer)); - memcpy(buffer + bufferLength, &pio, pio.GetSize()); - bufferLength += pio.GetSize(); + bufferLength += pio->GetSize(); if (!mIsAdvertisingLocalOnLinkPrefix) { @@ -865,28 +867,31 @@ void RoutingManager::SendRouterAdvertisement(const OmrPrefixArray &aNewOmrPrefix } LogInfo("Send on-link prefix %s in PIO (preferred lifetime = %u seconds, valid lifetime = %u seconds)", - aNewOnLinkPrefix->ToString().AsCString(), pio.GetPreferredLifetime(), pio.GetValidLifetime()); + aNewOnLinkPrefix->ToString().AsCString(), pio->GetPreferredLifetime(), pio->GetValidLifetime()); mTimeAdvertisedOnLinkPrefix = TimerMilli::GetNow(); } else if (mOnLinkPrefixDeprecateTimer.IsRunning()) { - RouterAdv::PrefixInfoOption pio; + RouterAdv::PrefixInfoOption *pio; - pio.SetOnLink(true); - pio.SetAutoAddrConfig(true); - pio.SetValidLifetime(TimeMilli::MsecToSec(mOnLinkPrefixDeprecateTimer.GetFireTime() - TimerMilli::GetNow())); + OT_ASSERT(bufferLength + sizeof(RouterAdv::PrefixInfoOption) <= sizeof(buffer)); + + pio = reinterpret_cast(buffer + bufferLength); + + pio->Init(); + pio->SetOnLinkFlag(); + pio->SetAutoAddrConfigFlag(); + pio->SetValidLifetime(TimeMilli::MsecToSec(mOnLinkPrefixDeprecateTimer.GetFireTime() - TimerMilli::GetNow())); // Set zero preferred lifetime to immediately deprecate the advertised on-link prefix. - pio.SetPreferredLifetime(0); - pio.SetPrefix(mLocalOnLinkPrefix); + pio->SetPreferredLifetime(0); + pio->SetPrefix(mLocalOnLinkPrefix); - OT_ASSERT(bufferLength + pio.GetSize() <= sizeof(buffer)); - memcpy(buffer + bufferLength, &pio, pio.GetSize()); - bufferLength += pio.GetSize(); + bufferLength += pio->GetSize(); LogInfo("Send on-link prefix %s in PIO (preferred lifetime = %u seconds, valid lifetime = %u seconds)", - mLocalOnLinkPrefix.ToString().AsCString(), pio.GetPreferredLifetime(), pio.GetValidLifetime()); + mLocalOnLinkPrefix.ToString().AsCString(), pio->GetPreferredLifetime(), pio->GetValidLifetime()); } // Invalidate the advertised OMR prefixes if they are no longer in the new OMR prefix array. @@ -895,15 +900,19 @@ void RoutingManager::SendRouterAdvertisement(const OmrPrefixArray &aNewOmrPrefix { if (!aNewOmrPrefixes.Contains(advertisedOmrPrefix)) { - RouterAdv::RouteInfoOption rio; + RouterAdv::RouteInfoOption *rio; + + OT_ASSERT(bufferLength + RouterAdv::RouteInfoOption::OptionSizeForPrefix(advertisedOmrPrefix.GetLength()) <= + sizeof(buffer)); + + rio = reinterpret_cast(buffer + bufferLength); // Set zero route lifetime to immediately invalidate the advertised OMR prefix. - rio.SetRouteLifetime(0); - rio.SetPrefix(advertisedOmrPrefix); + rio->Init(); + rio->SetRouteLifetime(0); + rio->SetPrefix(advertisedOmrPrefix); - OT_ASSERT(bufferLength + rio.GetSize() <= sizeof(buffer)); - memcpy(buffer + bufferLength, &rio, rio.GetSize()); - bufferLength += rio.GetSize(); + bufferLength += rio->GetSize(); LogInfo("Stop advertising OMR prefix %s on interface %u", advertisedOmrPrefix.ToString().AsCString(), mInfraIfIndex); @@ -912,14 +921,18 @@ void RoutingManager::SendRouterAdvertisement(const OmrPrefixArray &aNewOmrPrefix for (const Ip6::Prefix &newOmrPrefix : aNewOmrPrefixes) { - RouterAdv::RouteInfoOption rio; + RouterAdv::RouteInfoOption *rio; - rio.SetRouteLifetime(kDefaultOmrPrefixLifetime); - rio.SetPrefix(newOmrPrefix); + OT_ASSERT(bufferLength + RouterAdv::RouteInfoOption::OptionSizeForPrefix(newOmrPrefix.GetLength()) <= + sizeof(buffer)); - OT_ASSERT(bufferLength + rio.GetSize() <= sizeof(buffer)); - memcpy(buffer + bufferLength, &rio, rio.GetSize()); - bufferLength += rio.GetSize(); + rio = reinterpret_cast(buffer + bufferLength); + + rio->Init(); + rio->SetRouteLifetime(kDefaultOmrPrefixLifetime); + rio->SetPrefix(newOmrPrefix); + + bufferLength += rio->GetSize(); LogInfo("Send OMR prefix %s in RIO (valid lifetime = %u seconds)", newOmrPrefix.ToString().AsCString(), kDefaultOmrPrefixLifetime); @@ -969,7 +982,11 @@ bool RoutingManager::IsValidOmrPrefix(const Ip6::Prefix &aOmrPrefix) bool RoutingManager::IsValidOnLinkPrefix(const RouterAdv::PrefixInfoOption &aPio) { - return IsValidOnLinkPrefix(aPio.GetPrefix()) && aPio.GetOnLink() && aPio.GetAutoAddrConfig(); + Ip6::Prefix prefix; + + aPio.GetPrefix(prefix); + + return IsValidOnLinkPrefix(prefix) && aPio.IsOnLinkFlagSet() && aPio.IsAutoAddrConfigFlagSet(); } bool RoutingManager::IsValidOnLinkPrefix(const Ip6::Prefix &aOnLinkPrefix) @@ -1214,11 +1231,13 @@ exit: // routing policy evaluation. bool RoutingManager::UpdateDiscoveredOnLinkPrefix(const RouterAdv::PrefixInfoOption &aPio) { - Ip6::Prefix prefix = aPio.GetPrefix(); + Ip6::Prefix prefix; bool needReevaluate = false; ExternalPrefix onLinkPrefix; ExternalPrefix *existingPrefix = nullptr; + aPio.GetPrefix(prefix); + if (!IsValidOnLinkPrefix(aPio)) { LogInfo("Ignore invalid on-link prefix in PIO: %s", prefix.ToString().AsCString()); @@ -1307,10 +1326,12 @@ exit: // from the Thread network). void RoutingManager::UpdateDiscoveredOmrPrefix(const RouterAdv::RouteInfoOption &aRio) { - Ip6::Prefix prefix = aRio.GetPrefix(); + Ip6::Prefix prefix; ExternalPrefix omrPrefix; ExternalPrefix *existingPrefix = nullptr; + aRio.GetPrefix(prefix); + if (!IsValidOmrPrefix(prefix)) { LogInfo("Ignore invalid OMR prefix in RIO: %s", prefix.ToString().AsCString()); diff --git a/tests/toranj/openthread-core-toranj-config.h b/tests/toranj/openthread-core-toranj-config.h index c04317d7f..7a180cc3e 100644 --- a/tests/toranj/openthread-core-toranj-config.h +++ b/tests/toranj/openthread-core-toranj-config.h @@ -67,6 +67,14 @@ */ #define OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE 1 +/** + * @def OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE + * + * Define to 1 to enable Border Routing support. + * + */ +#define OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE 1 + /** * @def OPENTHREAD_CONFIG_COMMISSIONER_ENABLE * From d53bcde8903fb44e30ffeb6770b222f115406434 Mon Sep 17 00:00:00 2001 From: Abtin Keshavarzian Date: Tue, 3 May 2022 14:07:32 -0700 Subject: [PATCH 10/80] [routing-manager] use `NetData::Publisher` to publish external routes (#7660) This commit updates `RoutingManager` to use `NetData::Publisher` for external routes. This ensures to limit the number of similar entries added in Network Data if there are many BRs within Thread mesh discovering same set of prefixes and adding them to Thread Network Data (avoid overflow of Thread Network Data). --- src/core/border_router/routing_manager.cpp | 43 ++++++++++------------ src/core/border_router/routing_manager.hpp | 4 +- src/core/config/netdata_publisher.h | 10 ++++- src/core/thread/network_data_publisher.hpp | 6 +++ 4 files changed, 36 insertions(+), 27 deletions(-) diff --git a/src/core/border_router/routing_manager.cpp b/src/core/border_router/routing_manager.cpp index 6f1ae4a98..91b8a6d4a 100644 --- a/src/core/border_router/routing_manager.cpp +++ b/src/core/border_router/routing_manager.cpp @@ -268,7 +268,7 @@ void RoutingManager::Stop(void) if (mIsAdvertisingLocalOnLinkPrefix) { - RemoveExternalRoute(mLocalOnLinkPrefix); + UnpublishExternalRoute(mLocalOnLinkPrefix); // Start deprecating the local on-link prefix to send a PIO // with zero preferred lifetime in `SendRouterAdvertisement`. @@ -278,7 +278,7 @@ void RoutingManager::Stop(void) #if OPENTHREAD_CONFIG_BORDER_ROUTING_NAT64_ENABLE if (mIsAdvertisingLocalNat64Prefix) { - RemoveExternalRoute(mLocalNat64Prefix); + UnpublishExternalRoute(mLocalNat64Prefix); mIsAdvertisingLocalNat64Prefix = false; } #endif @@ -545,7 +545,7 @@ bool RoutingManager::IsOmrPrefixAddedToLocalNetworkData(void) const return Get().ContainsOnMeshPrefix(mLocalOmrPrefix); } -Error RoutingManager::AddExternalRoute(const Ip6::Prefix &aPrefix, RoutePreference aRoutePreference, bool aNat64) +Error RoutingManager::PublishExternalRoute(const Ip6::Prefix &aPrefix, RoutePreference aRoutePreference, bool aNat64) { Error error; NetworkData::ExternalRouteConfig routeConfig; @@ -558,36 +558,31 @@ Error RoutingManager::AddExternalRoute(const Ip6::Prefix &aPrefix, RoutePreferen routeConfig.mNat64 = aNat64; routeConfig.mPreference = aRoutePreference; - error = Get().AddHasRoutePrefix(routeConfig); + error = Get().PublishExternalRoute(routeConfig); + if (error != kErrorNone) { - LogWarn("Failed to add external route %s: %s", aPrefix.ToString().AsCString(), ErrorToString(error)); - } - else - { - Get().HandleServerDataUpdated(); - LogInfo("Adding external route %s", aPrefix.ToString().AsCString()); + LogWarn("Failed to publish external route %s: %s", aPrefix.ToString().AsCString(), ErrorToString(error)); } return error; } -void RoutingManager::RemoveExternalRoute(const Ip6::Prefix &aPrefix) +void RoutingManager::UnpublishExternalRoute(const Ip6::Prefix &aPrefix) { Error error = kErrorNone; VerifyOrExit(mIsRunning); - SuccessOrExit(error = Get().RemoveHasRoutePrefix(aPrefix)); + error = Get().UnpublishPrefix(aPrefix); - Get().HandleServerDataUpdated(); - LogInfo("Removing external route %s", aPrefix.ToString().AsCString()); - -exit: if (error != kErrorNone) { - LogWarn("Failed to remove external route %s: %s", aPrefix.ToString().AsCString(), ErrorToString(error)); + LogWarn("Failed to unpublish route %s: %s", aPrefix.ToString().AsCString(), ErrorToString(error)); } + +exit: + return; } const Ip6::Prefix *RoutingManager::EvaluateOnLinkPrefix(void) @@ -616,7 +611,7 @@ const Ip6::Prefix *RoutingManager::EvaluateOnLinkPrefix(void) if (smallestOnLinkPrefix == nullptr) { if (mIsAdvertisingLocalOnLinkPrefix || - (AddExternalRoute(mLocalOnLinkPrefix, NetworkData::kRoutePreferenceMedium) == kErrorNone)) + (PublishExternalRoute(mLocalOnLinkPrefix, NetworkData::kRoutePreferenceMedium) == kErrorNone)) { newOnLinkPrefix = &mLocalOnLinkPrefix; } @@ -653,7 +648,7 @@ void RoutingManager::HandleOnLinkPrefixDeprecateTimer(Timer &aTimer) void RoutingManager::HandleOnLinkPrefixDeprecateTimer(void) { LogInfo("Local on-link prefix %s expired", mLocalOnLinkPrefix.ToString().AsCString()); - RemoveExternalRoute(mLocalOnLinkPrefix); + UnpublishExternalRoute(mLocalOnLinkPrefix); } void RoutingManager::DeprecateOnLinkPrefix(void) @@ -697,7 +692,7 @@ void RoutingManager::EvaluateNat64Prefix(void) // Advertise local NAT64 prefix. if (!mIsAdvertisingLocalNat64Prefix && - AddExternalRoute(mLocalNat64Prefix, NetworkData::kRoutePreferenceLow, /* aNat64= */ true) == kErrorNone) + PublishExternalRoute(mLocalNat64Prefix, NetworkData::kRoutePreferenceLow, /* aNat64= */ true) == kErrorNone) { mIsAdvertisingLocalNat64Prefix = true; } @@ -709,7 +704,7 @@ void RoutingManager::EvaluateNat64Prefix(void) LogNote("Withdrawing local NAT64 prefix since a smaller one %s exists.", smallestNat64Prefix.ToString().AsCString()); - RemoveExternalRoute(mLocalNat64Prefix); + UnpublishExternalRoute(mLocalNat64Prefix); mIsAdvertisingLocalNat64Prefix = false; } } @@ -1272,7 +1267,7 @@ bool RoutingManager::UpdateDiscoveredOnLinkPrefix(const RouterAdv::PrefixInfoOpt if (!mDiscoveredPrefixes.IsFull()) { - SuccessOrExit(AddExternalRoute(prefix, NetworkData::kRoutePreferenceMedium)); + SuccessOrExit(PublishExternalRoute(prefix, NetworkData::kRoutePreferenceMedium)); existingPrefix = mDiscoveredPrefixes.PushBack(); *existingPrefix = onLinkPrefix; needReevaluate = true; @@ -1386,7 +1381,7 @@ void RoutingManager::UpdateDiscoveredOmrPrefix(const RouterAdv::RouteInfoOption if (!mDiscoveredPrefixes.IsFull()) { - SuccessOrExit(AddExternalRoute(prefix, omrPrefix.mRoutePreference)); + SuccessOrExit(PublishExternalRoute(prefix, omrPrefix.mRoutePreference)); existingPrefix = mDiscoveredPrefixes.PushBack(); } else @@ -1424,7 +1419,7 @@ void RoutingManager::InvalidateDiscoveredPrefixes(const Ip6::Prefix *aPrefix, bo (!prefix.mIsOnLinkPrefix && (mAdvertisedOmrPrefixes.Contains(prefix.mPrefix) || NetworkDataContainsOmrPrefix(prefix.mPrefix)))) { - RemoveExternalRoute(prefix.mPrefix); + UnpublishExternalRoute(prefix.mPrefix); } else { diff --git a/src/core/border_router/routing_manager.hpp b/src/core/border_router/routing_manager.hpp index c601370ef..d447d4f3c 100644 --- a/src/core/border_router/routing_manager.hpp +++ b/src/core/border_router/routing_manager.hpp @@ -333,8 +333,8 @@ private: Error PublishLocalOmrPrefix(void); void UnpublishLocalOmrPrefix(void); bool IsOmrPrefixAddedToLocalNetworkData(void) const; - Error AddExternalRoute(const Ip6::Prefix &aPrefix, RoutePreference aRoutePreference, bool aNat64 = false); - void RemoveExternalRoute(const Ip6::Prefix &aPrefix); + Error PublishExternalRoute(const Ip6::Prefix &aPrefix, RoutePreference aRoutePreference, bool aNat64 = false); + void UnpublishExternalRoute(const Ip6::Prefix &aPrefix); void StartRouterSolicitationDelay(void); Error SendRouterSolicitation(void); void SendRouterAdvertisement(const OmrPrefixArray &aNewOmrPrefixes, const Ip6::Prefix *aNewOnLinkPrefix); diff --git a/src/core/config/netdata_publisher.h b/src/core/config/netdata_publisher.h index 7f9fe0708..bd3ef8f64 100644 --- a/src/core/config/netdata_publisher.h +++ b/src/core/config/netdata_publisher.h @@ -35,6 +35,7 @@ #ifndef CONFIG_NETDATA_PUBLISHER_H_ #define CONFIG_NETDATA_PUBLISHER_H_ +#include "config/border_router.h" #include "config/srp_server.h" /** @@ -48,7 +49,8 @@ * */ #ifndef OPENTHREAD_CONFIG_NETDATA_PUBLISHER_ENABLE -#define OPENTHREAD_CONFIG_NETDATA_PUBLISHER_ENABLE OPENTHREAD_CONFIG_SRP_SERVER_ENABLE +#define OPENTHREAD_CONFIG_NETDATA_PUBLISHER_ENABLE \ + (OPENTHREAD_CONFIG_SRP_SERVER_ENABLE || OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE) #endif /** @@ -149,7 +151,13 @@ * */ #ifndef OPENTHREAD_CONFIG_NETDATA_PUBLISHER_MAX_PREFIX_ENTRIES + +#if OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE +#define OPENTHREAD_CONFIG_NETDATA_PUBLISHER_MAX_PREFIX_ENTRIES \ + (OPENTHREAD_CONFIG_BORDER_ROUTING_MAX_DISCOVERED_PREFIXES + 5) +#else #define OPENTHREAD_CONFIG_NETDATA_PUBLISHER_MAX_PREFIX_ENTRIES 3 #endif +#endif #endif // CONFIG_NETDATA_PUBLISHER_H_ diff --git a/src/core/thread/network_data_publisher.hpp b/src/core/thread/network_data_publisher.hpp index 28ad6db17..1da84d70f 100644 --- a/src/core/thread/network_data_publisher.hpp +++ b/src/core/thread/network_data_publisher.hpp @@ -43,6 +43,12 @@ "or OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE" #endif +#if OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE && (OPENTHREAD_CONFIG_NETDATA_PUBLISHER_MAX_PREFIX_ENTRIES < \ + (OPENTHREAD_CONFIG_BORDER_ROUTING_MAX_DISCOVERED_PREFIXES + 4)) +#error "OPENTHREAD_CONFIG_NETDATA_PUBLISHER_MAX_PREFIX_ENTRIES needs to support more entries when "\ + "OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE is enabled to accommodate for max on-link prefixes" +#endif + #include #include "common/clearable.hpp" From 8440b5f85d6212f1147a45d6d34ab7f3d841a43a Mon Sep 17 00:00:00 2001 From: Abtin Keshavarzian Date: Wed, 11 May 2022 21:53:36 -0700 Subject: [PATCH 11/80] [test] relax wait time in `test_nat64_multi_border_routers.py` (#7660) This commit increases the wait time in `nat64_multi_border_routers` test to accommodate for the fact that prefixes are added and removed using `NetworkData::Publisher` which can add additional initial random wait time (before adding the prefix into network data). --- .../border_router/test_nat64_multi_border_routers.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tests/scripts/thread-cert/border_router/test_nat64_multi_border_routers.py b/tests/scripts/thread-cert/border_router/test_nat64_multi_border_routers.py index f139836f5..c09383604 100644 --- a/tests/scripts/thread-cert/border_router/test_nat64_multi_border_routers.py +++ b/tests/scripts/thread-cert/border_router/test_nat64_multi_border_routers.py @@ -105,6 +105,7 @@ class Nat64MultiBorderRouter(thread_cert.TestCase): self.assertEqual('router', br2.get_state()) # Only 1 NAT64 prefix in Network Data. + self.simulator.go(30) self.assertEqual(len(br1.get_netdata_nat64_prefix()), 1) self.assertEqual(len(br2.get_netdata_nat64_prefix()), 1) self.assertEqual(br1.get_netdata_nat64_prefix()[0], br2.get_netdata_nat64_prefix()[0]) @@ -120,7 +121,7 @@ class Nat64MultiBorderRouter(thread_cert.TestCase): # Case 2. Disable and re-enable border routing on BR1. # br1.disable_br() - self.simulator.go(5) + self.simulator.go(30) # BR1 withdraws its prefix and BR2 advertises its prefix. self.assertEqual(len(br1.get_netdata_nat64_prefix()), 1) @@ -128,7 +129,7 @@ class Nat64MultiBorderRouter(thread_cert.TestCase): self.assertNotEqual(br1_nat64_prefix, br1.get_netdata_nat64_prefix()[0]) br1.enable_br() - self.simulator.go(5) + self.simulator.go(30) # NAT64 prefix in Network Data is still advertised by BR2. self.assertEqual(len(br1.get_netdata_nat64_prefix()), 1) From 0fdbfbe613cd8bdddeadd93653a0265986020edf Mon Sep 17 00:00:00 2001 From: Jonathan Hui Date: Tue, 17 May 2022 15:51:56 -0700 Subject: [PATCH 12/80] [routing-manager] fix build and test failures (#7707) `RemoveExternalRoute` -> `UnpublishExternalRoute` --- src/core/border_router/routing_manager.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/border_router/routing_manager.cpp b/src/core/border_router/routing_manager.cpp index 91b8a6d4a..0d99264c9 100644 --- a/src/core/border_router/routing_manager.cpp +++ b/src/core/border_router/routing_manager.cpp @@ -383,7 +383,7 @@ void RoutingManager::HandleNotifierEvents(Events aEvents) { if (mIsAdvertisingLocalOnLinkPrefix) { - RemoveExternalRoute(mLocalOnLinkPrefix); + UnpublishExternalRoute(mLocalOnLinkPrefix); // TODO: consider deprecating/invalidating existing // on-link prefix mIsAdvertisingLocalOnLinkPrefix = false; From d3a6268149ac7f537da0c6e421404367afa83db8 Mon Sep 17 00:00:00 2001 From: Abtin Keshavarzian Date: Wed, 18 May 2022 09:23:29 -0700 Subject: [PATCH 13/80] [routing-manager] `PublishExternalRoute()` to return success on `kErrorAlready` (#7715) This commit changes `RoutingManager::PublishExternalRoute()` method to return `kErrorAlready` as `kErrorNone` (i.e., if the prefix to be published was previously published). This commit keeps the logging the same (i.e. we still log on `Already` error so we can see when/if this happens). --- src/core/border_router/routing_manager.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/border_router/routing_manager.cpp b/src/core/border_router/routing_manager.cpp index 0d99264c9..01bbee4f8 100644 --- a/src/core/border_router/routing_manager.cpp +++ b/src/core/border_router/routing_manager.cpp @@ -565,7 +565,7 @@ Error RoutingManager::PublishExternalRoute(const Ip6::Prefix &aPrefix, RoutePref LogWarn("Failed to publish external route %s: %s", aPrefix.ToString().AsCString(), ErrorToString(error)); } - return error; + return (error == kErrorAlready) ? kErrorNone : error; } void RoutingManager::UnpublishExternalRoute(const Ip6::Prefix &aPrefix) From a51a6311e96ab282d26f6700eb7c7448961596ce Mon Sep 17 00:00:00 2001 From: Rongli Sun Date: Thu, 19 May 2022 05:08:14 +0800 Subject: [PATCH 14/80] [tests] add wait time in test_border_router_as_fed.py (#7714) This commit adds some wait time before connectivity validation to mitigate the impact of #7660 which introduces some random delay before publishing external route into network data. --- .../thread-cert/border_router/test_border_router_as_fed.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/scripts/thread-cert/border_router/test_border_router_as_fed.py b/tests/scripts/thread-cert/border_router/test_border_router_as_fed.py index 74679649a..a21ffa570 100644 --- a/tests/scripts/thread-cert/border_router/test_border_router_as_fed.py +++ b/tests/scripts/thread-cert/border_router/test_border_router_as_fed.py @@ -87,7 +87,7 @@ class TestBorderRouterAsFed(thread_cert.TestCase): self.simulator.go(5) self.assertEqual('child', br.get_state()) - self.simulator.go(10) + self.simulator.go(20) self.assertEqual('child', br.get_state()) # Leader can ping to/from the Host on infra link. From ab57ca2f44b4d74fd47d19dd065d95b0ca5d7cb4 Mon Sep 17 00:00:00 2001 From: Abtin Keshavarzian Date: Wed, 18 May 2022 15:35:05 -0700 Subject: [PATCH 15/80] [ip6] simplify `Ip6::ShouldForwardToThread()` (#7706) --- src/core/net/ip6.cpp | 32 +++++++++----------------------- 1 file changed, 9 insertions(+), 23 deletions(-) diff --git a/src/core/net/ip6.cpp b/src/core/net/ip6.cpp index 3f5ec0de3..b510fb12c 100644 --- a/src/core/net/ip6.cpp +++ b/src/core/net/ip6.cpp @@ -1309,43 +1309,29 @@ exit: bool Ip6::ShouldForwardToThread(const MessageInfo &aMessageInfo, bool aFromHost) const { - OT_UNUSED_VARIABLE(aFromHost); + bool shouldForward = false; - bool rval = false; - - if (aMessageInfo.GetSockAddr().IsMulticast()) + if (aMessageInfo.GetSockAddr().IsMulticast() || aMessageInfo.GetSockAddr().IsLinkLocal()) { - // multicast - ExitNow(rval = true); - } - else if (aMessageInfo.GetSockAddr().IsLinkLocal()) - { - // on-link link-local address - ExitNow(rval = true); + shouldForward = true; } else if (IsOnLink(aMessageInfo.GetSockAddr())) { - // on-link global address #if OPENTHREAD_FTD && OPENTHREAD_CONFIG_BACKBONE_ROUTER_DUA_NDPROXYING_ENABLE - ExitNow(rval = (aFromHost || - !Get().ShouldForwardDuaToBackbone(aMessageInfo.GetSockAddr()))); + shouldForward = + (aFromHost || !Get().ShouldForwardDuaToBackbone(aMessageInfo.GetSockAddr())); #else - ExitNow(rval = true); + OT_UNUSED_VARIABLE(aFromHost); + shouldForward = true; #endif } else if (Get().RouteLookup(aMessageInfo.GetPeerAddr(), aMessageInfo.GetSockAddr(), nullptr) == kErrorNone) { - // route - ExitNow(rval = true); - } - else - { - ExitNow(rval = false); + shouldForward = true; } -exit: - return rval; + return shouldForward; } const Netif::UnicastAddress *Ip6::SelectSourceAddress(MessageInfo &aMessageInfo) From 5107ee623837ed5b20cae4e6758644c1ca772c56 Mon Sep 17 00:00:00 2001 From: Jonathan Hui Date: Thu, 19 May 2022 09:15:54 -0700 Subject: [PATCH 16/80] [thread-cert] update Cert_5_5_05_SplitMergeREED to expect 16 routers (#7719) Cert_5_5_05_SplitMergeREED.py was intermittently failing to find an MLE Advertisement that had a Route TLV with 15 entries. However, the test topology has 16 routers in steady state. Update packet filter to look for 16 routers. --- tests/scripts/thread-cert/Cert_5_5_05_SplitMergeREED.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/scripts/thread-cert/Cert_5_5_05_SplitMergeREED.py b/tests/scripts/thread-cert/Cert_5_5_05_SplitMergeREED.py index 89492c73b..65a02df1d 100755 --- a/tests/scripts/thread-cert/Cert_5_5_05_SplitMergeREED.py +++ b/tests/scripts/thread-cert/Cert_5_5_05_SplitMergeREED.py @@ -176,10 +176,10 @@ class Cert_5_5_5_SplitMergeREED(thread_cert.TestCase): router1_pkts.range(_start_idx).must_next() router1_pkts.filter_mle_cmd(MLE_PARENT_REQUEST).must_next() - # filter MLE_ADVERTISEMENT with 15 routing table entry: - # 1 byte ID Sequence + 8 bytes ID Mask + 15 bytes Routing Table Entry = - # 24 (Router64 tlv length) - pkts.range(_start_idx).filter_mle_cmd(MLE_ADVERTISEMENT).filter(lambda p: 24 in p.mle.tlv.len).must_next() + # filter MLE_ADVERTISEMENT with 16 routing table entry: + # 1 byte ID Sequence + 8 bytes ID Mask + 16 bytes Routing Table Entry = + # 25 (Router64 tlv length) + pkts.range(_start_idx).filter_mle_cmd(MLE_ADVERTISEMENT).filter(lambda p: 25 in p.mle.tlv.len).must_next() _end_idx = pkts.index # Step 2: The DUT MUST NOT attempt to become an active router by sending an Address Solicit Request From dbb8a5801f0da6a32d7d136a2a31489f04edeacb Mon Sep 17 00:00:00 2001 From: Jonathan Hui Date: Thu, 19 May 2022 10:32:31 -0700 Subject: [PATCH 17/80] [codecov] migrate to v2 uploader (#7718) --- .github/workflows/otbr.yml | 2 +- .github/workflows/posix.yml | 2 +- .github/workflows/simulation-1.1.yml | 2 +- .github/workflows/simulation-1.2.yml | 2 +- .github/workflows/toranj.yml | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/otbr.yml b/.github/workflows/otbr.yml index e9bc9f3d6..159c84d59 100644 --- a/.github/workflows/otbr.yml +++ b/.github/workflows/otbr.yml @@ -220,7 +220,7 @@ jobs: script/test combine_coverage - name: Upload Coverage continue-on-error: true - uses: codecov/codecov-action@v1 + uses: codecov/codecov-action@v2 with: files: final.info fail_ci_if_error: true diff --git a/.github/workflows/posix.yml b/.github/workflows/posix.yml index abe6f0b88..93dd49cc2 100644 --- a/.github/workflows/posix.yml +++ b/.github/workflows/posix.yml @@ -293,7 +293,7 @@ jobs: run: | script/test combine_coverage - name: Upload Coverage - uses: codecov/codecov-action@v1 + uses: codecov/codecov-action@v2 with: files: final.info fail_ci_if_error: true diff --git a/.github/workflows/simulation-1.1.yml b/.github/workflows/simulation-1.1.yml index a16bf124b..3754c9d3c 100644 --- a/.github/workflows/simulation-1.1.yml +++ b/.github/workflows/simulation-1.1.yml @@ -372,7 +372,7 @@ jobs: run: | script/test combine_coverage - name: Upload Coverage - uses: codecov/codecov-action@v1 + uses: codecov/codecov-action@v2 with: files: final.info fail_ci_if_error: true diff --git a/.github/workflows/simulation-1.2.yml b/.github/workflows/simulation-1.2.yml index f8bc267ea..7b7a949c9 100644 --- a/.github/workflows/simulation-1.2.yml +++ b/.github/workflows/simulation-1.2.yml @@ -323,7 +323,7 @@ jobs: run: | script/test combine_coverage - name: Upload Coverage - uses: codecov/codecov-action@v1 + uses: codecov/codecov-action@v2 with: files: final.info fail_ci_if_error: true diff --git a/.github/workflows/toranj.yml b/.github/workflows/toranj.yml index 46f9520ef..221913fde 100644 --- a/.github/workflows/toranj.yml +++ b/.github/workflows/toranj.yml @@ -139,7 +139,7 @@ jobs: run: | script/test combine_coverage - name: Upload Coverage - uses: codecov/codecov-action@v1 + uses: codecov/codecov-action@v2 with: files: final.info fail_ci_if_error: true From 39ac118a03766f111a8126df5303d6047f62f26d Mon Sep 17 00:00:00 2001 From: Abtin Keshavarzian Date: Thu, 19 May 2022 10:51:47 -0700 Subject: [PATCH 18/80] [tcp6] `Endpoint` and `Listener` to act as `GetProvider` (#7722) This commit updates `Tcp::Endpoint` and `Tcp::Listener` types to inherit from `GetProvider` (which provides `Get()` methods allowing them to directly access different components in an OpenThread `Instance`). --- src/core/net/tcp6.cpp | 40 ++++++++++++++++------------------------ src/core/net/tcp6.hpp | 8 ++++---- 2 files changed, 20 insertions(+), 28 deletions(-) diff --git a/src/core/net/tcp6.cpp b/src/core/net/tcp6.cpp index 8fdb407ff..9b18a06dd 100644 --- a/src/core/net/tcp6.cpp +++ b/src/core/net/tcp6.cpp @@ -123,11 +123,9 @@ exit: return error; } -Instance &Tcp::Endpoint::GetInstance(void) +Instance &Tcp::Endpoint::GetInstance(void) const { - struct tcpcb &tp = GetTcb(); - - return AsCoreType(tp.instance); + return AsNonConst(AsCoreType(GetTcb().instance)); } const SockAddr &Tcp::Endpoint::GetLocalAddress(void) const @@ -160,7 +158,7 @@ Error Tcp::Endpoint::Bind(const SockAddr &aSockName) struct tcpcb &tp = GetTcb(); VerifyOrExit(!AsCoreType(&aSockName.mAddress).IsUnspecified(), error = kErrorInvalidArgs); - VerifyOrExit(GetInstance().Get().CanBind(aSockName), error = kErrorInvalidState); + VerifyOrExit(Get().CanBind(aSockName), error = kErrorInvalidState); memcpy(&tp.laddr, &aSockName.mAddress, sizeof(tp.laddr)); tp.lport = HostSwap16(aSockName.mPort); @@ -275,9 +273,7 @@ Error Tcp::Endpoint::Deinitialize(void) { Error error; - Tcp &tcp = GetInstance().Get(); - - SuccessOrExit(error = tcp.mEndpoints.Remove(*this)); + SuccessOrExit(error = Get().mEndpoints.Remove(*this)); SetNext(nullptr); SuccessOrExit(error = Abort()); @@ -356,7 +352,7 @@ void Tcp::Endpoint::SetTimer(uint8_t aTimerFlag, uint32_t aDelay) LogDebg("Endpoint %p set timer %u to %u ms", static_cast(this), static_cast(timerIndex), static_cast(aDelay)); - GetInstance().Get().mTimer.FireAtIfEarlier(newFireTime); + Get().mTimer.FireAtIfEarlier(newFireTime); } void Tcp::Endpoint::CancelTimer(uint8_t aTimerFlag) @@ -451,7 +447,7 @@ void Tcp::Endpoint::PostCallbacksAfterSend(size_t aSent, size_t aBacklogBefore) if (backlogAfter < aBacklogBefore + aSent && mForwardProgressCallback != nullptr) { mPendingCallbacks |= kForwardProgressCallbackFlag; - GetInstance().Get().mTasklet.Post(); + Get().mTasklet.Post(); } } @@ -542,11 +538,9 @@ exit: return error; } -Instance &Tcp::Listener::GetInstance(void) +Instance &Tcp::Listener::GetInstance(void) const { - struct tcpcb_listen *tpl = &GetTcbListen(); - - return AsCoreType(tpl->instance); + return AsNonConst(AsCoreType(GetTcbListen().instance)); } Error Tcp::Listener::Listen(const SockAddr &aSockName) @@ -555,7 +549,7 @@ Error Tcp::Listener::Listen(const SockAddr &aSockName) uint16_t port = HostSwap16(aSockName.mPort); struct tcpcb_listen *tpl = &GetTcbListen(); - VerifyOrExit(GetInstance().Get().CanBind(aSockName), error = kErrorInvalidState); + VerifyOrExit(Get().CanBind(aSockName), error = kErrorInvalidState); memcpy(&tpl->laddr, &aSockName.mAddress, sizeof(tpl->laddr)); tpl->lport = port; @@ -580,9 +574,7 @@ Error Tcp::Listener::Deinitialize(void) { Error error; - Tcp &tcp = GetInstance().Get(); - - SuccessOrExit(error = tcp.mListeners.Remove(*this)); + SuccessOrExit(error = Get().mListeners.Remove(*this)); SetNext(nullptr); exit: @@ -812,7 +804,7 @@ bool Tcp::AutoBind(const SockAddr &aPeer, SockAddr &aToBind, bool aBindAddress, peerInfo.Clear(); peerInfo.SetPeerAddr(aPeer.GetAddress()); - netifAddress = InstanceLocator::GetInstance().Get().SelectSourceAddress(peerInfo); + netifAddress = Get().SelectSourceAddress(peerInfo); VerifyOrExit(netifAddress != nullptr, success = false); aToBind.GetAddress() = netifAddress->GetAddress(); } @@ -854,12 +846,12 @@ exit: void Tcp::HandleTimer(Timer &aTimer) { - OT_ASSERT(&aTimer == &aTimer.GetInstance().Get().mTimer); + OT_ASSERT(&aTimer == &aTimer.Get().mTimer); LogDebg("Main TCP timer expired"); - aTimer.GetInstance().Get().ProcessTimers(); + aTimer.Get().ProcessTimers(); } -void Tcp::ProcessTimers() +void Tcp::ProcessTimers(void) { TimeMilli now = TimerMilli::GetNow(); bool pendingTimer; @@ -1014,7 +1006,7 @@ void tcplp_sys_stop_timer(struct tcpcb *aTcb, uint8_t aTimerFlag) struct tcpcb *tcplp_sys_accept_ready(struct tcpcb_listen *aTcbListen, struct in6_addr *aAddr, uint16_t aPort) { Tcp::Listener & listener = Tcp::Listener::FromTcbListen(*aTcbListen); - Tcp & tcp = listener.GetInstance().Get(); + Tcp & tcp = listener.Get(); struct tcpcb * rv = (struct tcpcb *)-1; otSockAddr addr; otTcpEndpoint * endpointPtr; @@ -1065,7 +1057,7 @@ bool tcplp_sys_accepted_connection(struct tcpcb_listen *aTcbListen, { Tcp::Listener &listener = Tcp::Listener::FromTcbListen(*aTcbListen); Tcp::Endpoint &endpoint = Tcp::Endpoint::FromTcb(*aAccepted); - Tcp & tcp = endpoint.GetInstance().Get(); + Tcp & tcp = endpoint.Get(); bool accepted = true; if (listener.mAcceptDoneCallback != nullptr) diff --git a/src/core/net/tcp6.hpp b/src/core/net/tcp6.hpp index ef827a33d..501fd5ae1 100644 --- a/src/core/net/tcp6.hpp +++ b/src/core/net/tcp6.hpp @@ -83,7 +83,7 @@ public: * This class represents an endpoint of a TCP/IPv6 connection. * */ - class Endpoint : public otTcpEndpoint, public LinkedListEntry + class Endpoint : public otTcpEndpoint, public LinkedListEntry, public GetProvider { friend class Tcp; friend class LinkedList; @@ -118,7 +118,7 @@ public: * @returns The Instance pointer associated with this Endpoint. * */ - Instance &GetInstance(void); + Instance &GetInstance(void) const; /** * Obtains the context pointer that was associated this Endpoint upon @@ -402,7 +402,7 @@ public: * This class represents a TCP/IPv6 listener. * */ - class Listener : public otTcpListener, public LinkedListEntry + class Listener : public otTcpListener, public LinkedListEntry, public GetProvider { friend class LinkedList; @@ -436,7 +436,7 @@ public: * @returns The otInstance pointer associated with this Listener. * */ - Instance &GetInstance(void); + Instance &GetInstance(void) const; /** * Obtains the context pointer that was associated with this Listener upon From 33bd1f425b64cdaf63a15f783f1eadff10467fda Mon Sep 17 00:00:00 2001 From: Abtin Keshavarzian Date: Fri, 20 May 2022 08:51:38 -0700 Subject: [PATCH 19/80] [border-router] adding `InfraIf` class (#7700) This commit adds `InfraIf` class which represents an infrastructure network interface on a border router providing methods & definitions mirroring the platform APIs `otPlatInfraIf{}`. This commit also updates the `RoutingManager` to to use the `InfraIf` class. --- Android.mk | 2 +- src/core/BUILD.gn | 3 +- src/core/CMakeLists.txt | 2 +- src/core/Makefile.am | 3 +- src/core/border_router/infra_if.cpp | 162 ++++++++++++++++ src/core/border_router/infra_if.hpp | 186 +++++++++++++++++++ src/core/border_router/infra_if_platform.cpp | 61 ------ src/core/border_router/routing_manager.cpp | 124 +++++-------- src/core/border_router/routing_manager.hpp | 39 +--- src/core/common/instance.hpp | 5 + 10 files changed, 417 insertions(+), 170 deletions(-) create mode 100644 src/core/border_router/infra_if.cpp create mode 100644 src/core/border_router/infra_if.hpp delete mode 100644 src/core/border_router/infra_if_platform.cpp diff --git a/Android.mk b/Android.mk index 4d09fcc91..a13194cd2 100644 --- a/Android.mk +++ b/Android.mk @@ -220,7 +220,7 @@ LOCAL_SRC_FILES := \ src/core/backbone_router/bbr_manager.cpp \ src/core/backbone_router/multicast_listeners_table.cpp \ src/core/backbone_router/ndproxy_table.cpp \ - src/core/border_router/infra_if_platform.cpp \ + src/core/border_router/infra_if.cpp \ src/core/border_router/router_advertisement.cpp \ src/core/border_router/routing_manager.cpp \ src/core/coap/coap.cpp \ diff --git a/src/core/BUILD.gn b/src/core/BUILD.gn index 1518546d5..8b2b2c05e 100644 --- a/src/core/BUILD.gn +++ b/src/core/BUILD.gn @@ -363,7 +363,8 @@ openthread_core_files = [ "backbone_router/multicast_listeners_table.hpp", "backbone_router/ndproxy_table.cpp", "backbone_router/ndproxy_table.hpp", - "border_router/infra_if_platform.cpp", + "border_router/infra_if.cpp", + "border_router/infra_if.hpp", "border_router/router_advertisement.cpp", "border_router/router_advertisement.hpp", "border_router/routing_manager.cpp", diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index e09f7e5f2..a9cc62329 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -86,7 +86,7 @@ set(COMMON_SOURCES backbone_router/bbr_manager.cpp backbone_router/multicast_listeners_table.cpp backbone_router/ndproxy_table.cpp - border_router/infra_if_platform.cpp + border_router/infra_if.cpp border_router/router_advertisement.cpp border_router/routing_manager.cpp coap/coap.cpp diff --git a/src/core/Makefile.am b/src/core/Makefile.am index 850228d49..a63ab7e77 100644 --- a/src/core/Makefile.am +++ b/src/core/Makefile.am @@ -176,7 +176,7 @@ SOURCES_COMMON = \ backbone_router/bbr_manager.cpp \ backbone_router/multicast_listeners_table.cpp \ backbone_router/ndproxy_table.cpp \ - border_router/infra_if_platform.cpp \ + border_router/infra_if.cpp \ border_router/router_advertisement.cpp \ border_router/routing_manager.cpp \ coap/coap.cpp \ @@ -415,6 +415,7 @@ HEADERS_COMMON = \ backbone_router/bbr_manager.hpp \ backbone_router/multicast_listeners_table.hpp \ backbone_router/ndproxy_table.hpp \ + border_router/infra_if.hpp \ border_router/router_advertisement.hpp \ border_router/routing_manager.hpp \ coap/coap.hpp \ diff --git a/src/core/border_router/infra_if.cpp b/src/core/border_router/infra_if.cpp new file mode 100644 index 000000000..7fe20c942 --- /dev/null +++ b/src/core/border_router/infra_if.cpp @@ -0,0 +1,162 @@ +/* + * Copyright (c) 2022, The OpenThread Authors. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holder nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * @file + * This file implements infrastructure network interface. + */ + +#include "infra_if.hpp" + +#if OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE + +#include "border_router/routing_manager.hpp" +#include "common/as_core_type.hpp" +#include "common/instance.hpp" +#include "common/locator_getters.hpp" +#include "common/logging.hpp" +#include "net/icmp6.hpp" + +namespace ot { +namespace BorderRouter { + +RegisterLogModule("InfraIf"); + +InfraIf::InfraIf(Instance &aInstance) + : InstanceLocator(aInstance) + , mInitialized(false) + , mIsRunning(false) + , mIfIndex(0) +{ +} + +Error InfraIf::Init(uint32_t aIfIndex) +{ + Error error = kErrorNone; + + VerifyOrExit(!mInitialized, error = kErrorInvalidState); + VerifyOrExit(aIfIndex > 0, error = kErrorInvalidArgs); + + mIfIndex = aIfIndex; + mInitialized = true; + + LogInfo("Init %s", ToString().AsCString()); + +exit: + return error; +} + +void InfraIf::Deinit(void) +{ + mInitialized = false; + mIsRunning = false; + mIfIndex = 0; + + LogInfo("Deinit"); +} + +bool InfraIf::HasAddress(const Ip6::Address &aAddress) +{ + OT_ASSERT(mInitialized); + + return otPlatInfraIfHasAddress(mIfIndex, &aAddress); +} + +Error InfraIf::Send(const Icmp6Packet &aPacket, const Ip6::Address &aDestination) +{ + OT_ASSERT(mInitialized); + + return otPlatInfraIfSendIcmp6Nd(mIfIndex, &aDestination, aPacket.GetBytes(), aPacket.GetLength()); +} + +void InfraIf::HandledReceived(uint32_t aIfIndex, const Ip6::Address &aSource, const Icmp6Packet &aPacket) +{ + Error error = kErrorNone; + + VerifyOrExit(mInitialized && mIsRunning, error = kErrorInvalidState); + VerifyOrExit(aIfIndex == mIfIndex, error = kErrorDrop); + VerifyOrExit(aPacket.GetBytes() != nullptr, error = kErrorInvalidArgs); + VerifyOrExit(aPacket.GetLength() >= sizeof(Ip6::Icmp::Header), error = kErrorParse); + + Get().HandleReceived(aPacket, aSource); + +exit: + if (error != kErrorNone) + { + LogDebg("Dropped ICMPv6 message: %s", ErrorToString(error)); + } +} + +Error InfraIf::HandleStateChanged(uint32_t aIfIndex, bool aIsRunning) +{ + Error error = kErrorNone; + + VerifyOrExit(mInitialized, error = kErrorInvalidState); + VerifyOrExit(aIfIndex == mIfIndex, error = kErrorInvalidArgs); + + VerifyOrExit(aIsRunning != mIsRunning); + LogInfo("State changed: %sRUNNING -> %sRUNNING", mIsRunning ? "" : "NOT ", aIsRunning ? "" : "NOT "); + + mIsRunning = aIsRunning; + + Get().HandleInfraIfStateChanged(); + +exit: + return error; +} + +InfraIf::InfoString InfraIf::ToString(void) const +{ + InfoString string; + + string.Append("infra netif %u", mIfIndex); + return string; +} + +//--------------------------------------------------------------------------------------------------------------------- + +extern "C" void otPlatInfraIfRecvIcmp6Nd(otInstance * aInstance, + uint32_t aInfraIfIndex, + const otIp6Address *aSrcAddress, + const uint8_t * aBuffer, + uint16_t aBufferLength) +{ + InfraIf::Icmp6Packet packet; + + packet.Init(aBuffer, aBufferLength); + AsCoreType(aInstance).Get().HandledReceived(aInfraIfIndex, AsCoreType(aSrcAddress), packet); +} + +extern "C" otError otPlatInfraIfStateChanged(otInstance *aInstance, uint32_t aInfraIfIndex, bool aIsRunning) +{ + return AsCoreType(aInstance).Get().HandleStateChanged(aInfraIfIndex, aIsRunning); +} + +} // namespace BorderRouter +} // namespace ot + +#endif // OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE diff --git a/src/core/border_router/infra_if.hpp b/src/core/border_router/infra_if.hpp new file mode 100644 index 000000000..b67009e81 --- /dev/null +++ b/src/core/border_router/infra_if.hpp @@ -0,0 +1,186 @@ +/* + * Copyright (c) 2022, The OpenThread Authors. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holder nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * @file + * This file includes definitions for infrastructure network interface. + * + */ + +#ifndef INFRA_IF_HPP_ +#define INFRA_IF_HPP_ + +#include "openthread-core-config.h" + +#if OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE + +#include + +#include "common/data.hpp" +#include "common/error.hpp" +#include "common/locator.hpp" +#include "common/string.hpp" +#include "net/ip6.hpp" + +namespace ot { +namespace BorderRouter { + +/** + * This class represents the infrastructure network interface on a border router. + * + */ +class InfraIf : public InstanceLocator +{ +public: + static constexpr uint16_t kInfoStringSize = 20; ///< Max chars for the info string (`ToString()`). + + typedef String InfoString; ///< String type returned from `ToString()`. + typedef Data Icmp6Packet; ///< An IMCPv6 packet (data containing the IP payload) + + /** + * This constructor initializes the `InfraIf`. + * + * @param[in] aInstance A OpenThread instance. + * + */ + explicit InfraIf(Instance &aInstance); + + /** + * This method initializes the `InfraIf`. + * + * @param[in] aIfIndex The infrastructure interface index. + * + * @retval kErrorNone Successfully initialized the `InfraIf`. + * @retval kErrorInvalidArgs The index of the infra interface is not valid. + * @retval kErrorInvalidState The `InfraIf` is already initialized. + * + */ + Error Init(uint32_t aIfIndex); + + /** + * This method deinitilaizes the `InfraIf`. + * + */ + void Deinit(void); + + /** + * This method indicates whether or not the `InfraIf` is initialized. + * + * @retval TRUE The `InfraIf` is initialized. + * @retval FALSE The `InfraIf` is not initialized. + * + */ + bool IsInitialized(void) const { return mInitialized; } + + /** + * This method indicates whether or not the infra interface is running. + * + * @retval TRUE The infrastructure interface is running. + * @retval FALSE The infrastructure interface is not running. + * + */ + bool IsRunning(void) const { return mIsRunning; } + + /** + * This method returns the infrastructure interface index. + * + * @returns The interface index or zero if not initialized. + * + */ + uint32_t GetIfIndex(void) const { return mIfIndex; } + + /** + * This method indicates whether or not the infra interface has the given IPv6 address assigned. + * + * This method MUST be used when interface is initialized. + * + * @param[in] aAddress The IPv6 address. + * + * @retval TRUE The infrastructure interface has @p aAddress. + * @retval FALSE The infrastructure interface does not have @p aAddress. + * + */ + bool HasAddress(const Ip6::Address &aAddress); + + /** + * This method sends an ICMPv6 Neighbor Discovery packet on the infrastructure interface. + * + * This method MUST be used when interface is initialized. + * + * @param[in] aPacket The ICMPv6 packet to send. + * @param[in] aDestination The destination address. + * + * @retval kErrorNone Successfully sent the ICMPv6 message. + * @retval kErrorFailed Failed to send the ICMPv6 message. + * + */ + Error Send(const Icmp6Packet &aPacket, const Ip6::Address &aDestination); + + /** + * This method processes a received ICMPv6 Neighbor Discovery packet from an infrastructure interface. + * + * @param[in] aIfIndex The infrastructure interface index on which the ICMPv6 message is received. + * @param[in] aSource The IPv6 source address. + * @param[in] aPacket The ICMPv6 packet. + * + */ + void HandledReceived(uint32_t aIfIndex, const Ip6::Address &aSource, const Icmp6Packet &aPacket); + + /** + * This method handles infrastructure interface state changes. + * + * @param[in] aIfIndex The infrastructure interface index. + * @param[in] aIsRunning A boolean that indicates whether the infrastructure interface is running. + * + * @retval kErrorNone Successfully updated the infra interface status. + * @retval kErrorInvalidState The `InfraIf` is not initialized. + * @retval kErrorInvalidArgs The @p IfIndex does not match the interface index of `InfraIf`. + * + */ + Error HandleStateChanged(uint32_t aIfIndex, bool aIsRunning); + + /** + * This method converts the `InfraIf` to a human-readable string. + * + * @returns The string representation of `InfraIf`. + * + */ + InfoString ToString(void) const; + +private: + bool mInitialized : 1; + bool mIsRunning : 1; + uint32_t mIfIndex; +}; + +} // namespace BorderRouter +} // namespace ot + +#endif // OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE + +#endif // INFRA_IF_HPP_ diff --git a/src/core/border_router/infra_if_platform.cpp b/src/core/border_router/infra_if_platform.cpp deleted file mode 100644 index 33e0c4c7b..000000000 --- a/src/core/border_router/infra_if_platform.cpp +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright (c) 2020, The OpenThread Authors. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the copyright holder nor the - * names of its contributors may be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -/** - * @file - * This file implements infrastructure interface platform APIs. - */ - -#include "openthread-core-config.h" - -#if OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE - -#include - -#include "border_router/routing_manager.hpp" -#include "common/as_core_type.hpp" -#include "common/instance.hpp" - -using namespace ot; - -extern "C" void otPlatInfraIfRecvIcmp6Nd(otInstance * aInstance, - uint32_t aInfraIfIndex, - const otIp6Address *aSrcAddress, - const uint8_t * aBuffer, - uint16_t aBufferLength) -{ - AsCoreType(aInstance).Get().RecvIcmp6Message(aInfraIfIndex, AsCoreType(aSrcAddress), - aBuffer, aBufferLength); -} - -extern "C" otError otPlatInfraIfStateChanged(otInstance *aInstance, uint32_t aInfraIfIndex, bool aIsRunning) -{ - return AsCoreType(aInstance).Get().HandleInfraIfStateChanged(aInfraIfIndex, - aIsRunning); -} - -#endif // OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE diff --git a/src/core/border_router/routing_manager.cpp b/src/core/border_router/routing_manager.cpp index 01bbee4f8..509ea838f 100644 --- a/src/core/border_router/routing_manager.cpp +++ b/src/core/border_router/routing_manager.cpp @@ -63,8 +63,7 @@ RoutingManager::RoutingManager(Instance &aInstance) : InstanceLocator(aInstance) , mIsRunning(false) , mIsEnabled(false) - , mInfraIfIsRunning(false) - , mInfraIfIndex(0) + , mInfraIf(aInstance) , mIsAdvertisingLocalOnLinkPrefix(false) , mOnLinkPrefixDeprecateTimer(aInstance, HandleOnLinkPrefixDeprecateTimer) , mIsAdvertisingLocalNat64Prefix(false) @@ -94,8 +93,7 @@ Error RoutingManager::Init(uint32_t aInfraIfIndex, bool aInfraIfIsRunning) { Error error; - VerifyOrExit(!IsInitialized(), error = kErrorInvalidState); - VerifyOrExit(aInfraIfIndex > 0, error = kErrorInvalidArgs); + SuccessOrExit(error = mInfraIf.Init(aInfraIfIndex)); SuccessOrExit(error = LoadOrGenerateRandomBrUlaPrefix()); GenerateOmrPrefix(); @@ -104,16 +102,14 @@ Error RoutingManager::Init(uint32_t aInfraIfIndex, bool aInfraIfIsRunning) #endif GenerateOnLinkPrefix(); - mInfraIfIndex = aInfraIfIndex; - - // Initialize the infra interface status. - SuccessOrExit(error = HandleInfraIfStateChanged(mInfraIfIndex, aInfraIfIsRunning)); + error = mInfraIf.HandleStateChanged(mInfraIf.GetIfIndex(), aInfraIfIsRunning); exit: if (error != kErrorNone) { - mInfraIfIndex = 0; + mInfraIf.Deinit(); } + return error; } @@ -239,7 +235,7 @@ void RoutingManager::GenerateOnLinkPrefix(void) void RoutingManager::EvaluateState(void) { - if (mIsEnabled && Get().IsAttached() && mInfraIfIsRunning) + if (mIsEnabled && Get().IsAttached() && mInfraIf.IsRunning()) { Start(); } @@ -312,55 +308,28 @@ exit: return; } -void RoutingManager::RecvIcmp6Message(uint32_t aInfraIfIndex, - const Ip6::Address &aSrcAddress, - const uint8_t * aBuffer, - uint16_t aBufferLength) +void RoutingManager::HandleReceived(const InfraIf::Icmp6Packet &aPacket, const Ip6::Address &aSrcAddress) { - Error error = kErrorNone; const Ip6::Icmp::Header *icmp6Header; - VerifyOrExit(IsInitialized() && mIsRunning, error = kErrorDrop); - VerifyOrExit(aInfraIfIndex == mInfraIfIndex, error = kErrorDrop); - VerifyOrExit(aBuffer != nullptr && aBufferLength >= sizeof(Ip6::Icmp::Header), error = kErrorParse); + VerifyOrExit(mIsRunning); - icmp6Header = reinterpret_cast(aBuffer); + icmp6Header = reinterpret_cast(aPacket.GetBytes()); switch (icmp6Header->GetType()) { case Ip6::Icmp::Header::kTypeRouterAdvert: - HandleRouterAdvertisement(aSrcAddress, aBuffer, aBufferLength); + HandleRouterAdvertisement(aPacket, aSrcAddress); break; case Ip6::Icmp::Header::kTypeRouterSolicit: - HandleRouterSolicit(aSrcAddress, aBuffer, aBufferLength); + HandleRouterSolicit(aPacket, aSrcAddress); break; default: break; } exit: - if (error != kErrorNone) - { - LogDebg("Dropped ICMPv6 message: %s", ErrorToString(error)); - } -} - -Error RoutingManager::HandleInfraIfStateChanged(uint32_t aInfraIfIndex, bool aIsRunning) -{ - Error error = kErrorNone; - - VerifyOrExit(IsInitialized(), error = kErrorInvalidState); - VerifyOrExit(aInfraIfIndex == mInfraIfIndex, error = kErrorInvalidArgs); - VerifyOrExit(aIsRunning != mInfraIfIsRunning); - - LogInfo("Infra interface (%u) state changed: %sRUNNING -> %sRUNNING", aInfraIfIndex, - (mInfraIfIsRunning ? "" : "NOT "), (aIsRunning ? "" : "NOT ")); - - mInfraIfIsRunning = aIsRunning; - EvaluateState(); - -exit: - return error; + return; } void RoutingManager::HandleNotifierEvents(Events aEvents) @@ -630,8 +599,8 @@ const Ip6::Prefix *RoutingManager::EvaluateOnLinkPrefix(void) } else { - LogInfo("EvaluateOnLinkPrefix: There is already smaller on-link prefix %s on interface %u", - smallestOnLinkPrefix->ToString().AsCString(), mInfraIfIndex); + LogInfo("EvaluateOnLinkPrefix: There is already smaller on-link prefix %s on %s", + smallestOnLinkPrefix->ToString().AsCString(), mInfraIf.ToString().AsCString()); DeprecateOnLinkPrefix(); } } @@ -815,12 +784,14 @@ Error RoutingManager::SendRouterSolicitation(void) { Ip6::Address destAddress; RouterAdv::RouterSolicitMessage routerSolicit; + InfraIf::Icmp6Packet packet; OT_ASSERT(IsInitialized()); + packet.InitFrom(routerSolicit); destAddress.SetToLinkLocalAllRoutersMulticast(); - return otPlatInfraIfSendIcmp6Nd(mInfraIfIndex, &destAddress, reinterpret_cast(&routerSolicit), - sizeof(routerSolicit)); + + return mInfraIf.Send(packet, destAddress); } // This method sends Router Advertisement messages to advertise on-link prefix and route for OMR prefix. @@ -857,8 +828,8 @@ void RoutingManager::SendRouterAdvertisement(const OmrPrefixArray &aNewOmrPrefix if (!mIsAdvertisingLocalOnLinkPrefix) { - LogInfo("Start advertising new on-link prefix %s on interface %u", aNewOnLinkPrefix->ToString().AsCString(), - mInfraIfIndex); + LogInfo("Start advertising new on-link prefix %s on %s", aNewOnLinkPrefix->ToString().AsCString(), + mInfraIf.ToString().AsCString()); } LogInfo("Send on-link prefix %s in PIO (preferred lifetime = %u seconds, valid lifetime = %u seconds)", @@ -909,8 +880,8 @@ void RoutingManager::SendRouterAdvertisement(const OmrPrefixArray &aNewOmrPrefix bufferLength += rio->GetSize(); - LogInfo("Stop advertising OMR prefix %s on interface %u", advertisedOmrPrefix.ToString().AsCString(), - mInfraIfIndex); + LogInfo("Stop advertising OMR prefix %s on %s", advertisedOmrPrefix.ToString().AsCString(), + mInfraIf.ToString().AsCString()); } } @@ -936,23 +907,27 @@ void RoutingManager::SendRouterAdvertisement(const OmrPrefixArray &aNewOmrPrefix // Send the message only when there are options. if (bufferLength > sizeof(mRouterAdvMessage)) { - Error error; - Ip6::Address destAddress; + Error error; + Ip6::Address destAddress; + InfraIf::Icmp6Packet packet; ++mRouterAdvertisementCount; + packet.Init(buffer, bufferLength); destAddress.SetToLinkLocalAllNodesMulticast(); - error = otPlatInfraIfSendIcmp6Nd(mInfraIfIndex, &destAddress, buffer, bufferLength); + + error = mInfraIf.Send(packet, destAddress); if (error == kErrorNone) { mLastRouterAdvertisementSendTime = TimerMilli::GetNow(); - LogInfo("Sent Router Advertisement on interface %u", mInfraIfIndex); + LogInfo("Sent Router Advertisement on %s", mInfraIf.ToString().AsCString()); DumpDebg("[BR-CERT] direction=send | type=RA |", buffer, bufferLength); } else { - LogWarn("Failed to send Router Advertisement on interface %u: %s", mInfraIfIndex, ErrorToString(error)); + LogWarn("Failed to send Router Advertisement on %s: %s", mInfraIf.ToString().AsCString(), + ErrorToString(error)); } } } @@ -1108,15 +1083,13 @@ void RoutingManager::HandleRoutingPolicyTimer(Timer &aTimer) aTimer.Get().EvaluateRoutingPolicy(); } -void RoutingManager::HandleRouterSolicit(const Ip6::Address &aSrcAddress, - const uint8_t * aBuffer, - uint16_t aBufferLength) +void RoutingManager::HandleRouterSolicit(const InfraIf::Icmp6Packet &aPacket, const Ip6::Address &aSrcAddress) { + OT_UNUSED_VARIABLE(aPacket); OT_UNUSED_VARIABLE(aSrcAddress); - OT_UNUSED_VARIABLE(aBuffer); - OT_UNUSED_VARIABLE(aBufferLength); - LogInfo("Received Router Solicitation from %s on interface %u", aSrcAddress.ToString().AsCString(), mInfraIfIndex); + LogInfo("Received Router Solicitation from %s on %s", aSrcAddress.ToString().AsCString(), + mInfraIf.ToString().AsCString()); #if OPENTHREAD_CONFIG_BORDER_ROUTING_VICARIOUS_RS_ENABLE if (!mVicariousRouterSolicitTimer.IsRunning()) @@ -1146,9 +1119,7 @@ uint32_t RoutingManager::ExternalPrefix::GetPrefixExpireDelay(uint32_t aValidLif return delay; } -void RoutingManager::HandleRouterAdvertisement(const Ip6::Address &aSrcAddress, - const uint8_t * aBuffer, - uint16_t aBufferLength) +void RoutingManager::HandleRouterAdvertisement(const InfraIf::Icmp6Packet &aPacket, const Ip6::Address &aSrcAddress) { OT_ASSERT(mIsRunning); OT_UNUSED_VARIABLE(aSrcAddress); @@ -1164,14 +1135,15 @@ void RoutingManager::HandleRouterAdvertisement(const Ip6::Address &aSrcAddress, const Option * option; const RouterAdvMessage *routerAdvMessage; - VerifyOrExit(aBufferLength >= sizeof(RouterAdvMessage)); + VerifyOrExit(aPacket.GetLength() >= sizeof(RouterAdvMessage)); - LogInfo("Received Router Advertisement from %s on interface %u", aSrcAddress.ToString().AsCString(), mInfraIfIndex); - DumpDebg("[BR-CERT] direction=recv | type=RA |", aBuffer, aBufferLength); + LogInfo("Received Router Advertisement from %s on %s", aSrcAddress.ToString().AsCString(), + mInfraIf.ToString().AsCString()); + DumpDebg("[BR-CERT] direction=recv | type=RA |", aPacket.GetBytes(), aPacket.GetLength()); - routerAdvMessage = reinterpret_cast(aBuffer); - optionsBegin = aBuffer + sizeof(RouterAdvMessage); - optionsLength = aBufferLength - sizeof(RouterAdvMessage); + routerAdvMessage = reinterpret_cast(aPacket.GetBytes()); + optionsBegin = aPacket.GetBytes() + sizeof(RouterAdvMessage); + optionsLength = aPacket.GetLength() - sizeof(RouterAdvMessage); option = nullptr; while ((option = Option::GetNextOption(option, optionsBegin, optionsLength)) != nullptr) @@ -1207,7 +1179,7 @@ void RoutingManager::HandleRouterAdvertisement(const Ip6::Address &aSrcAddress, // Remember the header and parameters of RA messages which are // initiated from the infra interface. - if (otPlatInfraIfHasAddress(mInfraIfIndex, &aSrcAddress)) + if (mInfraIf.HasAddress(aSrcAddress)) { needReevaluate |= UpdateRouterAdvMessage(routerAdvMessage); } @@ -1241,8 +1213,8 @@ bool RoutingManager::UpdateDiscoveredOnLinkPrefix(const RouterAdv::PrefixInfoOpt VerifyOrExit(!mIsAdvertisingLocalOnLinkPrefix || prefix != mLocalOnLinkPrefix); - LogInfo("Discovered on-link prefix (%s, %u seconds) from interface %u", prefix.ToString().AsCString(), - aPio.GetValidLifetime(), mInfraIfIndex); + LogInfo("Discovered on-link prefix (%s, %u seconds) from %s", prefix.ToString().AsCString(), + aPio.GetValidLifetime(), mInfraIf.ToString().AsCString()); onLinkPrefix.mIsOnLinkPrefix = true; onLinkPrefix.mPrefix = prefix; @@ -1349,8 +1321,8 @@ void RoutingManager::UpdateDiscoveredOmrPrefix(const RouterAdv::RouteInfoOption VerifyOrExit(!mAdvertisedOmrPrefixes.Contains(prefix)); VerifyOrExit(!NetworkDataContainsOmrPrefix(prefix)); - LogInfo("Discovered OMR prefix (%s, %u seconds) from interface %u", prefix.ToString().AsCString(), - aRio.GetRouteLifetime(), mInfraIfIndex); + LogInfo("Discovered OMR prefix (%s, %u seconds) from %s", prefix.ToString().AsCString(), aRio.GetRouteLifetime(), + mInfraIf.ToString().AsCString()); if (aRio.GetRouteLifetime() == 0) { diff --git a/src/core/border_router/routing_manager.hpp b/src/core/border_router/routing_manager.hpp index d447d4f3c..02c68de5c 100644 --- a/src/core/border_router/routing_manager.hpp +++ b/src/core/border_router/routing_manager.hpp @@ -48,8 +48,8 @@ #endif #include -#include +#include "border_router/infra_if.hpp" #include "border_router/router_advertisement.hpp" #include "common/array.hpp" #include "common/error.hpp" @@ -73,6 +73,7 @@ namespace BorderRouter { class RoutingManager : public InstanceLocator { friend class ot::Notifier; + friend class ot::Instance; public: /** @@ -155,35 +156,21 @@ public: #endif // OPENTHREAD_CONFIG_BORDER_ROUTING_NAT64_ENABLE /** - * This method receives an ICMPv6 message on the infrastructure interface. + * This method processes a received ICMPv6 message from the infrastructure interface. * * Malformed or undesired messages are dropped silently. * - * @param[in] aInfraIfIndex The infrastructure interface index. + * @param[in] aPacket The received ICMPv6 packet. * @param[in] aSrcAddress The source address this message is sent from. - * @param[in] aBuffer THe ICMPv6 message buffer. - * @param[in] aLength The length of the ICMPv6 message buffer. * */ - void RecvIcmp6Message(uint32_t aInfraIfIndex, - const Ip6::Address &aSrcAddress, - const uint8_t * aBuffer, - uint16_t aBufferLength); + void HandleReceived(const InfraIf::Icmp6Packet &aPacket, const Ip6::Address &aSrcAddress); /** * This method handles infrastructure interface state changes. * - * @param[in] aInfraIfIndex The index of the infrastructure interface. - * @param[in] aIsRunning A boolean that indicates whether the infrastructure - * interface is running. - * - * @retval kErrorNone Successfully updated the infra interface status. - * @retval kErrorInvalidState The Routing Manager is not initialized. - * @retval kErrorInvalidArgs The @p aInfraIfIndex doesn't match the infra interface - * the Routing Manager is initialized with. - * */ - Error HandleInfraIfStateChanged(uint32_t aInfraIfIndex, bool aIsRunning); + void HandleInfraIfStateChanged(void) { EvaluateState(); } /** * This method checks if the on-mesh prefix configuration is a valid OMR prefix. @@ -313,7 +300,7 @@ private: void Start(void); void Stop(void); void HandleNotifierEvents(Events aEvents); - bool IsInitialized(void) const { return mInfraIfIndex != 0; } + bool IsInitialized(void) const { return mInfraIf.IsInitialized(); } bool IsEnabled(void) const { return mIsEnabled; } Error LoadOrGenerateRandomBrUlaPrefix(void); void GenerateOmrPrefix(void); @@ -355,8 +342,8 @@ private: static void HandleOnLinkPrefixDeprecateTimer(Timer &aTimer); void DeprecateOnLinkPrefix(void); - void HandleRouterSolicit(const Ip6::Address &aSrcAddress, const uint8_t *aBuffer, uint16_t aBufferLength); - void HandleRouterAdvertisement(const Ip6::Address &aSrcAddress, const uint8_t *aBuffer, uint16_t aBufferLength); + void HandleRouterSolicit(const InfraIf::Icmp6Packet &aPacket, const Ip6::Address &aSrcAddress); + void HandleRouterAdvertisement(const InfraIf::Icmp6Packet &aPacket, const Ip6::Address &aSrcAddress); bool UpdateDiscoveredOnLinkPrefix(const RouterAdv::PrefixInfoOption &aPio); void UpdateDiscoveredOmrPrefix(const RouterAdv::RouteInfoOption &aRio); void InvalidateDiscoveredPrefixes(const Ip6::Prefix *aPrefix = nullptr, bool aIsOnLinkPrefix = true); @@ -376,13 +363,7 @@ private: // Manager will be stopped if we are disabled. bool mIsEnabled; - // Indicates whether the infra interface is running. The Routing - // Manager will be stopped when the Infra interface is not running. - bool mInfraIfIsRunning; - - // The index of the infra interface on which Router Advertisement - // messages will be sent. - uint32_t mInfraIfIndex; + InfraIf mInfraIf; // The /48 BR ULA prefix loaded from local persistent storage or // randomly generated if none is found in persistent storage. diff --git a/src/core/common/instance.hpp b/src/core/common/instance.hpp index caba4dd37..ff0e37243 100644 --- a/src/core/common/instance.hpp +++ b/src/core/common/instance.hpp @@ -972,6 +972,11 @@ template <> inline BorderRouter::RoutingManager &Instance::Get(void) { return mRoutingManager; } + +template <> inline BorderRouter::InfraIf &Instance::Get(void) +{ + return mRoutingManager.mInfraIf; +} #endif #if OPENTHREAD_CONFIG_SRP_SERVER_ENABLE From 5fbf1bec88fa9dfeb131d07a2854528db0df0253 Mon Sep 17 00:00:00 2001 From: Jonathan Hui Date: Fri, 20 May 2022 08:53:34 -0700 Subject: [PATCH 20/80] [tests] use common definition for border router startup delay (#7721) --- .../thread-cert/border_router/test_advertising_proxy.py | 4 ++-- .../thread-cert/border_router/test_border_router_as_fed.py | 2 +- .../border_router/test_dnssd_instance_name_with_space.py | 4 ++-- .../scripts/thread-cert/border_router/test_dnssd_server.py | 2 +- .../border_router/test_dnssd_server_multi_border_routers.py | 4 ++-- .../border_router/test_end_device_udp_reachability.py | 2 +- .../thread-cert/border_router/test_external_route.py | 4 ++-- tests/scripts/thread-cert/border_router/test_firewall.py | 2 +- .../thread-cert/border_router/test_manual_address.py | 2 +- .../thread-cert/border_router/test_manual_maddress.py | 2 +- .../thread-cert/border_router/test_manual_omr_prefix.py | 6 ++++-- .../scripts/thread-cert/border_router/test_mdns_restart.py | 4 ++-- .../thread-cert/border_router/test_multi_border_routers.py | 4 ++-- .../thread-cert/border_router/test_multi_thread_networks.py | 4 ++-- .../border_router/test_nat64_multi_border_routers.py | 6 +++--- .../border_router/test_nat64_single_border_router.py | 4 ++-- .../thread-cert/border_router/test_plat_udp_accessiblity.py | 2 +- .../border_router/test_publish_meshcop_service.py | 6 +++--- .../scripts/thread-cert/border_router/test_radvd_coexist.py | 2 +- .../thread-cert/border_router/test_single_border_router.py | 4 ++-- .../thread-cert/border_router/test_trel_connectivity.py | 2 +- .../border_router/test_vicarious_router_solicit.py | 4 ++-- tests/scripts/thread-cert/config.py | 1 + 23 files changed, 40 insertions(+), 37 deletions(-) diff --git a/tests/scripts/thread-cert/border_router/test_advertising_proxy.py b/tests/scripts/thread-cert/border_router/test_advertising_proxy.py index 7516ec88f..0c088dce9 100755 --- a/tests/scripts/thread-cert/border_router/test_advertising_proxy.py +++ b/tests/scripts/thread-cert/border_router/test_advertising_proxy.py @@ -85,7 +85,7 @@ class SingleHostAndService(thread_cert.TestCase): server.srp_server_set_enabled(True) server.srp_server_set_lease_range(LEASE, LEASE, KEY_LEASE, KEY_LEASE) server.start() - self.simulator.go(10) + self.simulator.go(config.BORDER_ROUTER_STARTUP_DELAY) self.assertEqual('leader', server.get_state()) self.assertEqual(server.srp_server_get_state(), 'running') @@ -304,7 +304,7 @@ class SrpClientRemoveNonExistingHost(thread_cert.TestCase): server.srp_server_set_enabled(True) server.srp_server_set_lease_range(LEASE, LEASE, KEY_LEASE, KEY_LEASE) server.start() - self.simulator.go(10) + self.simulator.go(config.BORDER_ROUTER_STARTUP_DELAY) self.assertEqual('leader', server.get_state()) self.assertEqual(server.srp_server_get_state(), 'running') diff --git a/tests/scripts/thread-cert/border_router/test_border_router_as_fed.py b/tests/scripts/thread-cert/border_router/test_border_router_as_fed.py index a21ffa570..0a20239df 100644 --- a/tests/scripts/thread-cert/border_router/test_border_router_as_fed.py +++ b/tests/scripts/thread-cert/border_router/test_border_router_as_fed.py @@ -87,7 +87,7 @@ class TestBorderRouterAsFed(thread_cert.TestCase): self.simulator.go(5) self.assertEqual('child', br.get_state()) - self.simulator.go(20) + self.simulator.go(config.BORDER_ROUTER_STARTUP_DELAY) self.assertEqual('child', br.get_state()) # Leader can ping to/from the Host on infra link. diff --git a/tests/scripts/thread-cert/border_router/test_dnssd_instance_name_with_space.py b/tests/scripts/thread-cert/border_router/test_dnssd_instance_name_with_space.py index f0eefa281..d20040dbd 100644 --- a/tests/scripts/thread-cert/border_router/test_dnssd_instance_name_with_space.py +++ b/tests/scripts/thread-cert/border_router/test_dnssd_instance_name_with_space.py @@ -90,12 +90,12 @@ class TestDnssdInstanceNameWithSpace(thread_cert.TestCase): self.simulator.go(5) br1.start() - self.simulator.go(5) + self.simulator.go(config.BORDER_ROUTER_STARTUP_DELAY) self.assertEqual('leader', br1.get_state()) server.srp_server_set_enabled(True) br2.start() - self.simulator.go(config.ROUTER_STARTUP_DELAY) + self.simulator.go(config.BORDER_ROUTER_STARTUP_DELAY) self.assertEqual('router', br2.get_state()) client.start() diff --git a/tests/scripts/thread-cert/border_router/test_dnssd_server.py b/tests/scripts/thread-cert/border_router/test_dnssd_server.py index be3b8bebd..9d07d28a8 100644 --- a/tests/scripts/thread-cert/border_router/test_dnssd_server.py +++ b/tests/scripts/thread-cert/border_router/test_dnssd_server.py @@ -94,7 +94,7 @@ class TestDnssdServerOnBr(thread_cert.TestCase): self.simulator.go(5) br1.start() - self.simulator.go(5) + self.simulator.go(config.BORDER_ROUTER_STARTUP_DELAY) self.assertEqual('leader', br1.get_state()) server.srp_server_set_enabled(True) diff --git a/tests/scripts/thread-cert/border_router/test_dnssd_server_multi_border_routers.py b/tests/scripts/thread-cert/border_router/test_dnssd_server_multi_border_routers.py index b78398080..0f267e0aa 100644 --- a/tests/scripts/thread-cert/border_router/test_dnssd_server_multi_border_routers.py +++ b/tests/scripts/thread-cert/border_router/test_dnssd_server_multi_border_routers.py @@ -111,7 +111,7 @@ class TestDnssdServerOnMultiBr(thread_cert.TestCase): self.simulator.go(5) br1.start() - self.simulator.go(5) + self.simulator.go(config.BORDER_ROUTER_STARTUP_DELAY) self.assertEqual('leader', br1.get_state()) br1.srp_server_set_enabled(True) @@ -140,7 +140,7 @@ class TestDnssdServerOnMultiBr(thread_cert.TestCase): br2.start_otbr_service() br2.start() - self.simulator.go(5) + self.simulator.go(config.BORDER_ROUTER_STARTUP_DELAY) br2_addr = br2.get_ip6_address(config.ADDRESS_TYPE.OMR)[0] diff --git a/tests/scripts/thread-cert/border_router/test_end_device_udp_reachability.py b/tests/scripts/thread-cert/border_router/test_end_device_udp_reachability.py index 96455c70c..bef4a6a1b 100644 --- a/tests/scripts/thread-cert/border_router/test_end_device_udp_reachability.py +++ b/tests/scripts/thread-cert/border_router/test_end_device_udp_reachability.py @@ -82,7 +82,7 @@ class TestEndDeviceUdpReachability(thread_cert.TestCase): self.simulator.go(5) br1.start() - self.simulator.go(5) + self.simulator.go(config.BORDER_ROUTER_STARTUP_DELAY) self.assertEqual('leader', br1.get_state()) br1.udp_start("::", PORT, bind_unspecified=True) diff --git a/tests/scripts/thread-cert/border_router/test_external_route.py b/tests/scripts/thread-cert/border_router/test_external_route.py index 7ed71dcb9..236a8286b 100644 --- a/tests/scripts/thread-cert/border_router/test_external_route.py +++ b/tests/scripts/thread-cert/border_router/test_external_route.py @@ -93,11 +93,11 @@ class ExternalRoutes(thread_cert.TestCase): host = self.nodes[HOST] br1.start() - self.simulator.go(5) + self.simulator.go(config.BORDER_ROUTER_STARTUP_DELAY) self.assertEqual('leader', br1.get_state()) br2.start() - self.simulator.go(config.ROUTER_STARTUP_DELAY) + self.simulator.go(config.BORDER_ROUTER_STARTUP_DELAY) self.assertEqual('router', br2.get_state()) router1.start() diff --git a/tests/scripts/thread-cert/border_router/test_firewall.py b/tests/scripts/thread-cert/border_router/test_firewall.py index 84b00500f..abc6d3614 100644 --- a/tests/scripts/thread-cert/border_router/test_firewall.py +++ b/tests/scripts/thread-cert/border_router/test_firewall.py @@ -81,7 +81,7 @@ class Firewall(thread_cert.TestCase): host = self.nodes[HOST] br1.start() - self.simulator.go(5) + self.simulator.go(config.BORDER_ROUTER_STARTUP_DELAY) self.assertEqual('leader', br1.get_state()) router1.start() diff --git a/tests/scripts/thread-cert/border_router/test_manual_address.py b/tests/scripts/thread-cert/border_router/test_manual_address.py index 62edbaa33..aeb6c6051 100644 --- a/tests/scripts/thread-cert/border_router/test_manual_address.py +++ b/tests/scripts/thread-cert/border_router/test_manual_address.py @@ -71,7 +71,7 @@ class ManualAddressConfig(thread_cert.TestCase): router = self.nodes[ROUTER] br1.start() - self.simulator.go(5) + self.simulator.go(config.BORDER_ROUTER_STARTUP_DELAY) self.assertEqual('leader', br1.get_state()) self.assertTrue(br1.is_primary_backbone_router) diff --git a/tests/scripts/thread-cert/border_router/test_manual_maddress.py b/tests/scripts/thread-cert/border_router/test_manual_maddress.py index b39a4189f..93f94b55b 100644 --- a/tests/scripts/thread-cert/border_router/test_manual_maddress.py +++ b/tests/scripts/thread-cert/border_router/test_manual_maddress.py @@ -76,7 +76,7 @@ class ManualMulticastAddressConfig(thread_cert.TestCase): host = self.nodes[HOST] br1.start() - self.simulator.go(5) + self.simulator.go(config.BORDER_ROUTER_STARTUP_DELAY) self.assertEqual('leader', br1.get_state()) self.assertTrue(br1.is_primary_backbone_router) diff --git a/tests/scripts/thread-cert/border_router/test_manual_omr_prefix.py b/tests/scripts/thread-cert/border_router/test_manual_omr_prefix.py index 66f4cf2dd..e9035c69d 100644 --- a/tests/scripts/thread-cert/border_router/test_manual_omr_prefix.py +++ b/tests/scripts/thread-cert/border_router/test_manual_omr_prefix.py @@ -28,6 +28,8 @@ # import ipaddress import unittest + +import config import thread_cert # Test description: @@ -65,7 +67,7 @@ class ManualOmrsPrefix(thread_cert.TestCase): br2 = self.nodes[BR_2] br1.start() - self.simulator.go(10) + self.simulator.go(config.BORDER_ROUTER_STARTUP_DELAY) self.assertEqual('leader', br1.get_state()) self.simulator.go(10) @@ -73,7 +75,7 @@ class ManualOmrsPrefix(thread_cert.TestCase): br2.disable_br() br2.start() - self.simulator.go(15) + self.simulator.go(config.BORDER_ROUTER_STARTUP_DELAY) self.assertEqual('router', br2.get_state()) self.assertEqual(br1.get_netdata_omr_prefixes(), [br1.get_br_omr_prefix()]) diff --git a/tests/scripts/thread-cert/border_router/test_mdns_restart.py b/tests/scripts/thread-cert/border_router/test_mdns_restart.py index 2ea86d02a..585e9da07 100755 --- a/tests/scripts/thread-cert/border_router/test_mdns_restart.py +++ b/tests/scripts/thread-cert/border_router/test_mdns_restart.py @@ -100,13 +100,13 @@ class MdnsRestart(thread_cert.TestCase): self.simulator.go(5) br1.start() - self.simulator.go(5) + self.simulator.go(config.BORDER_ROUTER_STARTUP_DELAY) self.assertEqual('leader', br1.get_state()) self.simulator.go(5) br2.start() - self.simulator.go(config.ROUTER_STARTUP_DELAY) + self.simulator.go(config.BORDER_ROUTER_STARTUP_DELAY) self.assertEqual('router', br2.get_state()) ed1.start() diff --git a/tests/scripts/thread-cert/border_router/test_multi_border_routers.py b/tests/scripts/thread-cert/border_router/test_multi_border_routers.py index 5e4eeabd7..894882a06 100755 --- a/tests/scripts/thread-cert/border_router/test_multi_border_routers.py +++ b/tests/scripts/thread-cert/border_router/test_multi_border_routers.py @@ -102,7 +102,7 @@ class MultiBorderRouters(thread_cert.TestCase): self.simulator.go(5) br1.start() - self.simulator.go(5) + self.simulator.go(config.BORDER_ROUTER_STARTUP_DELAY) self.assertEqual('leader', br1.get_state()) router1.start() @@ -112,7 +112,7 @@ class MultiBorderRouters(thread_cert.TestCase): self.simulator.go(5) br2.start() - self.simulator.go(config.ROUTER_STARTUP_DELAY) + self.simulator.go(config.BORDER_ROUTER_STARTUP_DELAY) self.assertEqual('router', br2.get_state()) router2.start() diff --git a/tests/scripts/thread-cert/border_router/test_multi_thread_networks.py b/tests/scripts/thread-cert/border_router/test_multi_thread_networks.py index c5ab786ac..9ad442d4f 100755 --- a/tests/scripts/thread-cert/border_router/test_multi_thread_networks.py +++ b/tests/scripts/thread-cert/border_router/test_multi_thread_networks.py @@ -93,7 +93,7 @@ class MultiThreadNetworks(thread_cert.TestCase): router2 = self.nodes[ROUTER2] br1.start() - self.simulator.go(5) + self.simulator.go(config.BORDER_ROUTER_STARTUP_DELAY) self.assertEqual('leader', br1.get_state()) router1.start() @@ -101,7 +101,7 @@ class MultiThreadNetworks(thread_cert.TestCase): self.assertEqual('router', router1.get_state()) br2.start() - self.simulator.go(5) + self.simulator.go(config.BORDER_ROUTER_STARTUP_DELAY) self.assertEqual('leader', br2.get_state()) router2.start() diff --git a/tests/scripts/thread-cert/border_router/test_nat64_multi_border_routers.py b/tests/scripts/thread-cert/border_router/test_nat64_multi_border_routers.py index c09383604..569699721 100644 --- a/tests/scripts/thread-cert/border_router/test_nat64_multi_border_routers.py +++ b/tests/scripts/thread-cert/border_router/test_nat64_multi_border_routers.py @@ -89,7 +89,7 @@ class Nat64MultiBorderRouter(thread_cert.TestCase): self.simulator.go(5) br1.start() - self.simulator.go(5) + self.simulator.go(config.BORDER_ROUTER_STARTUP_DELAY) self.assertEqual('leader', br1.get_state()) router.start() @@ -101,7 +101,7 @@ class Nat64MultiBorderRouter(thread_cert.TestCase): # its local nat64 prefix to Network Data. # br2.start() - self.simulator.go(config.ROUTER_STARTUP_DELAY) + self.simulator.go(config.BORDER_ROUTER_STARTUP_DELAY) self.assertEqual('router', br2.get_state()) # Only 1 NAT64 prefix in Network Data. @@ -129,7 +129,7 @@ class Nat64MultiBorderRouter(thread_cert.TestCase): self.assertNotEqual(br1_nat64_prefix, br1.get_netdata_nat64_prefix()[0]) br1.enable_br() - self.simulator.go(30) + self.simulator.go(config.BORDER_ROUTER_STARTUP_DELAY) # NAT64 prefix in Network Data is still advertised by BR2. self.assertEqual(len(br1.get_netdata_nat64_prefix()), 1) diff --git a/tests/scripts/thread-cert/border_router/test_nat64_single_border_router.py b/tests/scripts/thread-cert/border_router/test_nat64_single_border_router.py index bcd4a9560..4ac418924 100644 --- a/tests/scripts/thread-cert/border_router/test_nat64_single_border_router.py +++ b/tests/scripts/thread-cert/border_router/test_nat64_single_border_router.py @@ -84,7 +84,7 @@ class Nat64SingleBorderRouter(thread_cert.TestCase): self.simulator.go(5) br.start() - self.simulator.go(5) + self.simulator.go(config.BORDER_ROUTER_STARTUP_DELAY) self.assertEqual('leader', br.get_state()) router.start() @@ -130,7 +130,7 @@ class Nat64SingleBorderRouter(thread_cert.TestCase): self.assertEqual(len(br.get_netdata_nat64_prefix()), 0) br.enable_br() - self.simulator.go(5) + self.simulator.go(config.BORDER_ROUTER_STARTUP_DELAY) # Same NAT64 prefix is advertised to Network Data. self.assertEqual(len(br.get_netdata_nat64_prefix()), 1) diff --git a/tests/scripts/thread-cert/border_router/test_plat_udp_accessiblity.py b/tests/scripts/thread-cert/border_router/test_plat_udp_accessiblity.py index f97c1e652..12f83e50c 100644 --- a/tests/scripts/thread-cert/border_router/test_plat_udp_accessiblity.py +++ b/tests/scripts/thread-cert/border_router/test_plat_udp_accessiblity.py @@ -67,7 +67,7 @@ class TestPlatUdpAccessibility(thread_cert.TestCase): router = self.nodes[ROUTER] br.start() - self.simulator.go(5) + self.simulator.go(config.BORDER_ROUTER_STARTUP_DELAY) self.assertEqual('leader', br.get_state()) br.srp_server_set_enabled(True) diff --git a/tests/scripts/thread-cert/border_router/test_publish_meshcop_service.py b/tests/scripts/thread-cert/border_router/test_publish_meshcop_service.py index 17a201bd9..93f049736 100755 --- a/tests/scripts/thread-cert/border_router/test_publish_meshcop_service.py +++ b/tests/scripts/thread-cert/border_router/test_publish_meshcop_service.py @@ -92,7 +92,7 @@ class PublishMeshCopService(thread_cert.TestCase): # TODO enable this line when renaming with mDNSResponder is stable # self.check_meshcop_service(br1, host) br1.start() - self.simulator.go(20) + self.simulator.go(config.BORDER_ROUTER_STARTUP_DELAY) self.assertEqual('leader', br1.get_state()) self.check_meshcop_service(br1, host) @@ -103,7 +103,7 @@ class PublishMeshCopService(thread_cert.TestCase): br1.stop() br1.set_network_name('ot-br1-1') br1.start() - self.simulator.go(10) + self.simulator.go(config.BORDER_ROUTER_STARTUP_DELAY) self.check_meshcop_service(br1, host) # verify that there are two meshcop services @@ -111,7 +111,7 @@ class PublishMeshCopService(thread_cert.TestCase): br2.start() br2.disable_backbone_router() br2.enable_br() - self.simulator.go(25) + self.simulator.go(config.BORDER_ROUTER_STARTUP_DELAY) service_instances = host.browse_mdns_services('_meshcop._udp') self.assertEqual(len(service_instances), 2) diff --git a/tests/scripts/thread-cert/border_router/test_radvd_coexist.py b/tests/scripts/thread-cert/border_router/test_radvd_coexist.py index 106afefff..9492e7d72 100755 --- a/tests/scripts/thread-cert/border_router/test_radvd_coexist.py +++ b/tests/scripts/thread-cert/border_router/test_radvd_coexist.py @@ -93,7 +93,7 @@ class SingleBorderRouter(thread_cert.TestCase): self.simulator.go(config.ROUTER_STARTUP_DELAY) self.assertEqual('router', router.get_state()) - self.simulator.go(10) + self.simulator.go(config.BORDER_ROUTER_STARTUP_DELAY) self.collect_ipaddrs() logging.info("BR addrs: %r", br.get_addrs()) 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 4776fefbe..8f03644be 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 @@ -85,7 +85,7 @@ class SingleBorderRouter(thread_cert.TestCase): self.simulator.go(5) br.start() - self.simulator.go(5) + self.simulator.go(config.BORDER_ROUTER_STARTUP_DELAY) self.assertEqual('leader', br.get_state()) router.start() @@ -216,7 +216,7 @@ class SingleBorderRouter(thread_cert.TestCase): br.enable_br() # It takes around 10 seconds to start sending RA messages. - self.simulator.go(15) + self.simulator.go(config.BORDER_ROUTER_STARTUP_DELAY) self.collect_ipaddrs() logging.info("BR addrs: %r", br.get_addrs()) diff --git a/tests/scripts/thread-cert/border_router/test_trel_connectivity.py b/tests/scripts/thread-cert/border_router/test_trel_connectivity.py index ceba885ce..718113819 100644 --- a/tests/scripts/thread-cert/border_router/test_trel_connectivity.py +++ b/tests/scripts/thread-cert/border_router/test_trel_connectivity.py @@ -126,7 +126,7 @@ class TestTrelConnectivity(thread_cert.TestCase): self.wait_node_state(router2, 'router', 10) # Allow the network to stabilize - self.simulator.go(config.ROUTER_STARTUP_DELAY) + self.simulator.go(config.BORDER_ROUTER_STARTUP_DELAY) self.collect_ipaddrs() self.collect_rloc16s() diff --git a/tests/scripts/thread-cert/border_router/test_vicarious_router_solicit.py b/tests/scripts/thread-cert/border_router/test_vicarious_router_solicit.py index 5c1e55847..065cdae1b 100755 --- a/tests/scripts/thread-cert/border_router/test_vicarious_router_solicit.py +++ b/tests/scripts/thread-cert/border_router/test_vicarious_router_solicit.py @@ -112,7 +112,7 @@ class MultiThreadNetworks(thread_cert.TestCase): self.simulator.go(5) br1.start() - self.simulator.go(5) + self.simulator.go(config.BORDER_ROUTER_STARTUP_DELAY) self.assertEqual('leader', br1.get_state()) router1.start() @@ -139,7 +139,7 @@ class MultiThreadNetworks(thread_cert.TestCase): host.kill_radvd_service() br2.start() - self.simulator.go(5) + self.simulator.go(config.BORDER_ROUTER_STARTUP_DELAY) self.assertEqual('leader', br2.get_state()) router2.start() diff --git a/tests/scripts/thread-cert/config.py b/tests/scripts/thread-cert/config.py index 9ba199ee4..7f10f27a6 100755 --- a/tests/scripts/thread-cert/config.py +++ b/tests/scripts/thread-cert/config.py @@ -126,6 +126,7 @@ SNIFFER_ID = int(os.getenv('SNIFFER_ID', 34)) PANID = 0xface ROUTER_STARTUP_DELAY = 10 +BORDER_ROUTER_STARTUP_DELAY = 20 MAX_NEIGHBOR_AGE = 100 INFINITE_COST_TIMEOUT = 90 From 36b2a173f951565cbd20d6923009fc6482317f8f Mon Sep 17 00:00:00 2001 From: Yakun Xu Date: Sat, 21 May 2022 02:00:44 +0800 Subject: [PATCH 21/80] [simulation] simulate energy scan (#7393) This commit implements energy scan in simulation platform. Verified locally ```bash > ifconfig up Done > scan energy 100 | Ch | RSSI | +----+------+ | 11 | -98 | | 12 | -98 | | 13 | -98 | | 14 | -98 | | 15 | -30 | | 16 | -30 | | 17 | -98 | | 18 | -98 | | 19 | -30 | | 20 | -98 | | 21 | -98 | | 22 | -98 | | 23 | -30 | | 24 | -30 | | 25 | -30 | | 26 | -98 | Done ``` --- .../simulation/platform-simulation.h | 3 +- examples/platforms/simulation/radio.c | 69 ++++++++++++++++--- examples/platforms/simulation/system.c | 24 ++++--- include/openthread/instance.h | 2 +- include/openthread/link_raw.h | 1 + include/openthread/platform/radio.h | 1 + src/core/mac/link_raw.hpp | 1 + src/core/mac/mac_links.hpp | 1 + src/core/mac/sub_mac.hpp | 1 + src/core/radio/radio.hpp | 1 + .../scripts/expect/posix-rcp-energy-scan.exp | 42 +++++++++++ 11 files changed, 126 insertions(+), 20 deletions(-) create mode 100755 tests/scripts/expect/posix-rcp-energy-scan.exp diff --git a/examples/platforms/simulation/platform-simulation.h b/examples/platforms/simulation/platform-simulation.h index 4e7970b22..9379421d4 100644 --- a/examples/platforms/simulation/platform-simulation.h +++ b/examples/platforms/simulation/platform-simulation.h @@ -163,10 +163,11 @@ void platformRadioReceive(otInstance *aInstance, uint8_t *aBuf, uint16_t aBufLen * * @param[in,out] aReadFdSet A pointer to the read file descriptors. * @param[in,out] aWriteFdSet A pointer to the write file descriptors. + * @param[in,out] aTimeout A pointer to the timeout. * @param[in,out] aMaxFd A pointer to the max file descriptor. * */ -void platformRadioUpdateFdSet(fd_set *aReadFdSet, fd_set *aWriteFdSet, int *aMaxFd); +void platformRadioUpdateFdSet(fd_set *aReadFdSet, fd_set *aWriteFdSet, struct timeval *aTimeout, int *aMaxFd); /** * This function performs radio driver processing. diff --git a/examples/platforms/simulation/radio.c b/examples/platforms/simulation/radio.c index 2faaa2f5f..2b627c2f2 100644 --- a/examples/platforms/simulation/radio.c +++ b/examples/platforms/simulation/radio.c @@ -29,6 +29,7 @@ #include "platform-simulation.h" #include +#include #include #include @@ -47,6 +48,9 @@ // The IPv4 group for receiving packets of radio simulation #define OT_RADIO_GROUP "224.0.0.116" +#define MS_PER_S 1000 +#define US_PER_MS 1000 + enum { IEEE802154_ACK_LENGTH = 5, @@ -75,6 +79,10 @@ static uint16_t sPortOffset = 0; static uint16_t sPort = 0; #endif +static int8_t sEnergyScanResult = OT_RADIO_RSSI_INVALID; +static bool sEnergyScanning = false; +static uint32_t sEnergyScanEndTime = 0; + enum { SIM_RADIO_CHANNEL_MIN = OT_RADIO_2P4GHZ_OQPSK_CHANNEL_MIN, @@ -156,6 +164,13 @@ static otMacKeyMaterial sCurrKey; static otMacKeyMaterial sNextKey; static otRadioKeyType sKeyType; +static int8_t GetRssi(uint16_t aChannel); + +static bool IsTimeAfterOrEqual(uint32_t aTimeA, uint32_t aTimeB) +{ + return (aTimeA - aTimeB) < (1U << 31); +} + static void ReverseExtAddress(otExtAddress *aReversed, const otExtAddress *aOrigin) { for (size_t i = 0; i < sizeof(*aReversed); i++) @@ -493,20 +508,23 @@ otRadioFrame *otPlatRadioGetTransmitBuffer(otInstance *aInstance) int8_t otPlatRadioGetRssi(otInstance *aInstance) { OT_UNUSED_VARIABLE(aInstance); - assert(aInstance != NULL); - int8_t rssi = SIM_LOW_RSSI_SAMPLE; - uint8_t channel = sReceiveFrame.mChannel; + return GetRssi(sReceiveFrame.mChannel); +} + +static int8_t GetRssi(uint16_t aChannel) +{ + int8_t rssi = SIM_LOW_RSSI_SAMPLE; uint32_t probabilityThreshold; - otEXPECT((SIM_RADIO_CHANNEL_MIN <= channel) && channel <= (SIM_RADIO_CHANNEL_MAX)); + otEXPECT((SIM_RADIO_CHANNEL_MIN <= aChannel) && aChannel <= (SIM_RADIO_CHANNEL_MAX)); // To emulate a simple interference model, we return either a high or // a low RSSI value with a fixed probability per each channel. The // probability is increased per channel by a constant. - probabilityThreshold = (channel - SIM_RADIO_CHANNEL_MIN) * SIM_HIGH_RSSI_PROB_INC_PER_CHANNEL; + probabilityThreshold = (aChannel - SIM_RADIO_CHANNEL_MIN) * SIM_HIGH_RSSI_PROB_INC_PER_CHANNEL; if (otRandomNonCryptoGetUint16() < (probabilityThreshold * 0xffff / 100)) { @@ -736,7 +754,7 @@ void platformRadioReceive(otInstance *aInstance, uint8_t *aBuf, uint16_t aBufLen radioReceive(aInstance); } #else -void platformRadioUpdateFdSet(fd_set *aReadFdSet, fd_set *aWriteFdSet, int *aMaxFd) +void platformRadioUpdateFdSet(fd_set *aReadFdSet, fd_set *aWriteFdSet, struct timeval *aTimeout, int *aMaxFd) { if (aReadFdSet != NULL && (sState != OT_RADIO_STATE_TRANSMIT || sTxWait)) { @@ -757,6 +775,25 @@ void platformRadioUpdateFdSet(fd_set *aReadFdSet, fd_set *aWriteFdSet, int *aMax *aMaxFd = sTxFd; } } + + if (sEnergyScanning) + { + struct timeval tv = {0, 0}; + uint32_t now = otPlatAlarmMilliGetNow(); + + if (IsTimeAfterOrEqual(sEnergyScanEndTime, now)) + { + uint32_t remaining = sEnergyScanEndTime - now; + + tv.tv_sec = remaining / MS_PER_S; + tv.tv_usec = (remaining % MS_PER_S) * US_PER_MS; + } + + if (timercmp(&tv, aTimeout, <)) + { + *aTimeout = tv; + } + } } // no need to close in virtual time mode. @@ -811,11 +848,16 @@ void platformRadioProcess(otInstance *aInstance, const fd_set *aReadFdSet, const } } #endif - if (platformRadioIsTransmitPending()) { radioSendMessage(aInstance); } + + if (sEnergyScanning && IsTimeAfterOrEqual(otPlatAlarmMilliGetNow(), sEnergyScanEndTime)) + { + sEnergyScanning = false; + otPlatRadioEnergyScanDone(aInstance, sEnergyScanResult); + } } void radioTransmit(struct RadioMessage *aMessage, const struct otRadioFrame *aFrame) @@ -982,13 +1024,22 @@ otError otPlatRadioEnergyScan(otInstance *aInstance, uint8_t aScanChannel, uint1 { OT_UNUSED_VARIABLE(aInstance); OT_UNUSED_VARIABLE(aScanChannel); - OT_UNUSED_VARIABLE(aScanDuration); + + otError error = OT_ERROR_NONE; assert(aInstance != NULL); assert(aScanChannel >= SIM_RADIO_CHANNEL_MIN && aScanChannel <= SIM_RADIO_CHANNEL_MAX); assert(aScanDuration > 0); - return OT_ERROR_NOT_IMPLEMENTED; + otEXPECT_ACTION((gRadioCaps & OT_RADIO_CAPS_ENERGY_SCAN), error = OT_ERROR_NOT_IMPLEMENTED); + otEXPECT_ACTION(!sEnergyScanning, error = OT_ERROR_BUSY); + + sEnergyScanResult = GetRssi(aScanChannel); + sEnergyScanning = true; + sEnergyScanEndTime = otPlatAlarmMilliGetNow() + aScanDuration; + +exit: + return error; } otError otPlatRadioGetTransmitPower(otInstance *aInstance, int8_t *aPower) diff --git a/examples/platforms/simulation/system.c b/examples/platforms/simulation/system.c index 57bb30cd4..f9282a64a 100644 --- a/examples/platforms/simulation/system.c +++ b/examples/platforms/simulation/system.c @@ -70,10 +70,11 @@ static void handleSignal(int aSignal) */ enum { - OT_SIM_OPT_HELP = 'h', - OT_SIM_OPT_SLEEP_TO_TX = 't', - OT_SIM_OPT_TIME_SPEED = 's', - OT_SIM_OPT_UNKNOWN = '?', + OT_SIM_OPT_HELP = 'h', + OT_SIM_OPT_ENABLE_ENERGY_SCAN = 'E', + OT_SIM_OPT_SLEEP_TO_TX = 't', + OT_SIM_OPT_TIME_SPEED = 's', + OT_SIM_OPT_UNKNOWN = '?', }; static void PrintUsage(const char *aProgramName, int aExitCode) @@ -82,9 +83,10 @@ static void PrintUsage(const char *aProgramName, int aExitCode) "Syntax:\n" " %s [Options] NodeId\n" "Options:\n" - " -h --help Display this usage information.\n" - " -t --sleep-to-tx Let radio support direct transition from sleep to TX with CSMA.\n" - " -s --time-speed=val Speed up the time in simulation.\n", + " -h --help Display this usage information.\n" + " -E --enable-energy-scan Enable energy scan capability.\n" + " -t --sleep-to-tx Let radio support direct transition from sleep to TX with CSMA.\n" + " -s --time-speed=val Speed up the time in simulation.\n", aProgramName); exit(aExitCode); @@ -97,6 +99,7 @@ void otSysInit(int aArgCount, char *aArgVector[]) static const struct option long_options[] = { {"help", no_argument, 0, OT_SIM_OPT_HELP}, + {"enable-energy-scan", no_argument, 0, OT_SIM_OPT_SLEEP_TO_TX}, {"sleep-to-tx", no_argument, 0, OT_SIM_OPT_SLEEP_TO_TX}, {"time-speed", required_argument, 0, OT_SIM_OPT_TIME_SPEED}, {0, 0, 0, 0}, @@ -112,7 +115,7 @@ void otSysInit(int aArgCount, char *aArgVector[]) while (true) { - int c = getopt_long(aArgCount, aArgVector, "hts:", long_options, NULL); + int c = getopt_long(aArgCount, aArgVector, "Ehts:", long_options, NULL); if (c == -1) { @@ -127,6 +130,9 @@ void otSysInit(int aArgCount, char *aArgVector[]) case OT_SIM_OPT_HELP: PrintUsage(aArgVector[0], EXIT_SUCCESS); break; + case OT_SIM_OPT_ENABLE_ENERGY_SCAN: + gRadioCaps |= OT_RADIO_CAPS_ENERGY_SCAN; + break; case OT_SIM_OPT_SLEEP_TO_TX: gRadioCaps |= OT_RADIO_CAPS_SLEEP_TO_TX; break; @@ -197,8 +203,8 @@ void otSysProcessDrivers(otInstance *aInstance) FD_ZERO(&error_fds); platformUartUpdateFdSet(&read_fds, &write_fds, &error_fds, &max_fd); - platformRadioUpdateFdSet(&read_fds, &write_fds, &max_fd); platformAlarmUpdateTimeout(&timeout); + platformRadioUpdateFdSet(&read_fds, &write_fds, &timeout, &max_fd); #if OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE platformTrelUpdateFdSet(&read_fds, &write_fds, &timeout, &max_fd); #endif diff --git a/include/openthread/instance.h b/include/openthread/instance.h index f917e7f77..5bf972369 100644 --- a/include/openthread/instance.h +++ b/include/openthread/instance.h @@ -53,7 +53,7 @@ extern "C" { * @note This number versions both OpenThread platform and user APIs. * */ -#define OPENTHREAD_API_VERSION (208) +#define OPENTHREAD_API_VERSION (209) /** * @addtogroup api-instance diff --git a/include/openthread/link_raw.h b/include/openthread/link_raw.h index 116fea654..02293e990 100644 --- a/include/openthread/link_raw.h +++ b/include/openthread/link_raw.h @@ -236,6 +236,7 @@ typedef void (*otLinkRawEnergyScanDone)(otInstance *aInstance, int8_t aEnergySca * @param[in] aCallback A pointer to a function called on completion of a scanned channel. * * @retval OT_ERROR_NONE Successfully started scanning the channel. + * @retval OT_ERROR_BUSY The radio is performing enery scanning. * @retval OT_ERROR_NOT_IMPLEMENTED The radio doesn't support energy scanning. * @retval OT_ERROR_INVALID_STATE If the raw link-layer isn't enabled. * diff --git a/include/openthread/platform/radio.h b/include/openthread/platform/radio.h index ba7314ebb..e3d0dda25 100644 --- a/include/openthread/platform/radio.h +++ b/include/openthread/platform/radio.h @@ -857,6 +857,7 @@ int8_t otPlatRadioGetRssi(otInstance *aInstance); * @param[in] aScanDuration The duration, in milliseconds, for the channel to be scanned. * * @retval OT_ERROR_NONE Successfully started scanning the channel. + * @retval OT_ERROR_BUSY The radio is performing enery scanning. * @retval OT_ERROR_NOT_IMPLEMENTED The radio doesn't support energy scanning. * */ diff --git a/src/core/mac/link_raw.hpp b/src/core/mac/link_raw.hpp index 16204b9e6..e0a66c99f 100644 --- a/src/core/mac/link_raw.hpp +++ b/src/core/mac/link_raw.hpp @@ -164,6 +164,7 @@ public: * @param[in] aCallback A pointer to a function called on completion of a scanned channel. * * @retval kErrorNone Successfully started scanning the channel. + * @retval kErrorBusy The radio is performing energy scanning. * @retval kErrorNotImplemented The radio doesn't support energy scanning. * @retval kErrorInvalidState If the raw link-layer isn't enabled. * diff --git a/src/core/mac/mac_links.hpp b/src/core/mac/mac_links.hpp index ff6821cf5..85aa0e54b 100644 --- a/src/core/mac/mac_links.hpp +++ b/src/core/mac/mac_links.hpp @@ -572,6 +572,7 @@ public: * @param[in] aScanDuration The duration, in milliseconds, for the channel to be scanned. * * @retval kErrorNone Successfully started scanning the channel. + * @retval kErrorBusy The radio is performing energy scanning. * @retval kErrorInvalidState The radio was disabled or transmitting. * @retval kErrorNotImplemented Energy scan is not supported by radio link. * diff --git a/src/core/mac/sub_mac.hpp b/src/core/mac/sub_mac.hpp index 4e243e97b..da0dbc6ae 100644 --- a/src/core/mac/sub_mac.hpp +++ b/src/core/mac/sub_mac.hpp @@ -379,6 +379,7 @@ public: * @param[in] aScanDuration The duration, in milliseconds, for the channel to be scanned. * * @retval kErrorNone Successfully started scanning the channel. + * @retval kErrorBusy The radio is performing energy scanning. * @retval kErrorInvalidState The radio was disabled or transmitting. * @retval kErrorNotImplemented Energy scan is not supported (applicable in link-raw/radio mode only). * diff --git a/src/core/radio/radio.hpp b/src/core/radio/radio.hpp index 10e5fd1c9..aa7233e18 100644 --- a/src/core/radio/radio.hpp +++ b/src/core/radio/radio.hpp @@ -522,6 +522,7 @@ public: * @param[in] aScanDuration The duration, in milliseconds, for the channel to be scanned. * * @retval kErrorNone Successfully started scanning the channel. + * @retval kErrorBusy The radio is performing energy scanning. * @retval kErrorNotImplemented The radio doesn't support energy scanning. * */ diff --git a/tests/scripts/expect/posix-rcp-energy-scan.exp b/tests/scripts/expect/posix-rcp-energy-scan.exp new file mode 100755 index 000000000..93a7a2ade --- /dev/null +++ b/tests/scripts/expect/posix-rcp-energy-scan.exp @@ -0,0 +1,42 @@ +#!/usr/bin/expect -f +# +# Copyright (c) 2022, The OpenThread Authors. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. Neither the name of the copyright holder nor the +# names of its contributors may be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. +# + +source "tests/scripts/expect/_common.exp" + +spawn_node 1 "rcp" "spinel+hdlc+forkpty://$::env(OT_SIMULATION_APPS)/ncp/ot-rcp?forkpty-arg=1&forkpty-arg=-E&no-reset=1" + +send "ifconfig up\n" +expect_line "Done" +# Actually returns last otPlatRadioGetRssi +send "scan energy 0\n" +expect_line "Done" +# Actually performs energy scan on RCP +send "scan energy 100\n" +expect_line "Done" +dispose_all From bffbebc6465ed7c3ca07945fffbe5b136e95b951 Mon Sep 17 00:00:00 2001 From: Abtin Keshavarzian Date: Fri, 20 May 2022 11:16:04 -0700 Subject: [PATCH 22/80] [router-advert] `RouterAdvMessage` to provide default router prf (#7720) This commit updates and simplifies `RouterAdvMessage`: - Adds new method to get and set the "default router preference" - Removes custom overloads of operator `=` and `==` (which excluded the checksum field). Instead when we save a received RA in `RoutingManager` we directly set the checksum to zero so the normal (byte by byte) comparison would properly check all fields. --- .../border_router/router_advertisement.cpp | 31 +++---- .../border_router/router_advertisement.hpp | 85 +++++++++---------- src/core/border_router/routing_manager.cpp | 8 +- 3 files changed, 62 insertions(+), 62 deletions(-) diff --git a/src/core/border_router/router_advertisement.cpp b/src/core/border_router/router_advertisement.cpp index d2356c562..c1c3c1d14 100644 --- a/src/core/border_router/router_advertisement.cpp +++ b/src/core/border_router/router_advertisement.cpp @@ -112,7 +112,7 @@ void RouteInfoOption::SetPreference(RoutePreference aPreference) mResvdPrf |= (NetworkData::RoutePreferenceToValue(aPreference) << kPreferenceOffset) & kPreferenceMask; } -RouteInfoOption::RoutePreference RouteInfoOption::GetPreference(void) const +RoutePreference RouteInfoOption::GetPreference(void) const { return NetworkData::RoutePreferenceFromValue((mResvdPrf & kPreferenceMask) >> kPreferenceOffset); } @@ -171,29 +171,24 @@ uint8_t RouteInfoOption::OptionLengthForPrefix(uint8_t aPrefixLength) void RouterAdvMessage::SetToDefault(void) { - mHeader.Clear(); - mHeader.SetType(Ip6::Icmp::Header::kTypeRouterAdvert); - mReachableTime = 0; - mRetransTimer = 0; + OT_UNUSED_VARIABLE(mCode); + OT_UNUSED_VARIABLE(mCurHopLimit); + OT_UNUSED_VARIABLE(mReachableTime); + OT_UNUSED_VARIABLE(mRetransTimer); + + Clear(); + mType = Ip6::Icmp::Header::kTypeRouterAdvert; } -const RouterAdvMessage &RouterAdvMessage::operator=(const RouterAdvMessage &aOther) +RoutePreference RouterAdvMessage::GetDefaultRouterPreference(void) const { - mHeader = aOther.mHeader; - - // Set zero value and let platform do the calculation. - mHeader.SetChecksum(0); - - mReachableTime = aOther.mReachableTime; - mRetransTimer = aOther.mRetransTimer; - - return *this; + return NetworkData::RoutePreferenceFromValue((mFlags & kPreferenceMask) >> kPreferenceOffset); } -bool RouterAdvMessage::operator==(const RouterAdvMessage &aOther) const +void RouterAdvMessage::SetDefaultRouterPreference(RoutePreference aPreference) { - return memcmp(&mHeader.mData, &aOther.mHeader.mData, sizeof(mHeader.mData)) == 0 && - mReachableTime == aOther.mReachableTime && mRetransTimer == aOther.mRetransTimer; + mFlags &= ~kPreferenceMask; + mFlags |= (NetworkData::RoutePreferenceToValue(aPreference) << kPreferenceOffset) & kPreferenceMask; } //---------------------------------------------------------------------------------------------------------------------- diff --git a/src/core/border_router/router_advertisement.hpp b/src/core/border_router/router_advertisement.hpp index 2ca972c3a..c98f44072 100644 --- a/src/core/border_router/router_advertisement.hpp +++ b/src/core/border_router/router_advertisement.hpp @@ -60,6 +60,8 @@ namespace ot { namespace BorderRouter { namespace RouterAdv { +typedef NetworkData::RoutePreference RoutePreference; ///< Route Preference + /** * This class represents the variable length options in Neighbor Discovery messages. * @@ -324,8 +326,6 @@ public: static constexpr uint16_t kMinSize = kLengthUnit; ///< Minimum size (in bytes) of a Route Info Option static constexpr Type kType = Type::kRouteInfo; ///< Route Information Option Type. - typedef NetworkData::RoutePreference RoutePreference; ///< Route Preference - /** * This method initializes the option setting the type and clearing (setting to zero) all other fields. * @@ -447,14 +447,13 @@ private: static_assert(sizeof(RouteInfoOption) == 8, "invalid RouteInfoOption structure"); /** - * This class implements the Router Advertisement message. + * This class implements the Router Advertisement message header. * - * See section 4.2 of RFC 4861 for definition of this message. - * https://tools.ietf.org/html/rfc4861#section-4.2 + * See section 2.2 of RFC 4191 [https://datatracker.ietf.org/doc/html/rfc4191] * */ OT_TOOL_PACKED_BEGIN -class RouterAdvMessage : public Unequatable +class RouterAdvMessage : public Equatable, private Clearable { public: /** @@ -476,70 +475,70 @@ public: * @param[in] aChecksum The checksum value. * */ - void SetChecksum(uint16_t aChecksum) { mHeader.SetChecksum(aChecksum); } + void SetChecksum(uint16_t aChecksum) { mChecksum = HostSwap16(aChecksum); } /** * This method sets the Router Lifetime in seconds. * - * Zero Router Lifetime means we are not a default router. - * * @param[in] aRouterLifetime The router lifetime in seconds. * */ - void SetRouterLifetime(uint16_t aRouterLifetime) - { - mHeader.mData.m16[kRouteLifetimeIdx] = HostSwap16(aRouterLifetime); - } + void SetRouterLifetime(uint16_t aRouterLifetime) { mRouterLifetime = HostSwap16(aRouterLifetime); } /** - * This method returns the Router Lifetime. + * This method gets the Router Lifetime (in seconds). * - * Zero Router Lifetime means we are not a default router. + * Router Lifetime set to zero indicates that the sender is not a default router. * * @returns The router lifetime in seconds. * */ - uint16_t GetRouterLifetime(void) const { return HostSwap16(mHeader.mData.m16[kRouteLifetimeIdx]); } + uint16_t GetRouterLifetime(void) const { return HostSwap16(mRouterLifetime); } /** - * This method returns the Managed Address Configuration ('m') flag. + * This method sets the default router preference. * - * @returns A boolean which indicates whether the 'm' flag is set. + * @param[in] aPreference The router preference. * */ - bool GetManagedAddrConfig(void) const { return (mHeader.mData.m8[kReservedIdx] & kManagedAddressConfigMask) != 0; } + void SetDefaultRouterPreference(RoutePreference aPreference); /** - * This method overloads the assignment operator. + * This method gets the default router preference. + * + * @returns The router preference. * */ - const RouterAdvMessage &operator=(const RouterAdvMessage &aOther); - - /** - * This method overloads operator `==` to evaluate whether or not - * two instances of `RouterAdvMessage` are equal. - * - * @param[in] aOther The other `RouterAdvMessage` instance to compare with. - * - * @retval TRUE If the two `RouterAdvMessage` instances are equal. - * @retval FALSE If the two `RouterAdvMessage` instances are not equal. - * - */ - bool operator==(const RouterAdvMessage &aOther) const; + RoutePreference GetDefaultRouterPreference(void) const; private: - // The index of Route Lifetime in ICMPv6 Header Data. In unit of 2 octets. - static constexpr uint8_t kRouteLifetimeIdx = 1; + // Router Advertisement Message + // + // 0 1 2 3 + // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // | Type | Code | Checksum | + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // | Cur Hop Limit |M|O|H|Prf|Resvd| Router Lifetime | + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // | Reachable Time | + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // | Retrans Timer | + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // | Options ... + // +-+-+-+-+-+-+-+-+-+-+-+- - // The index of Reserved byte in ICMPv6 Header Data. In unit of 1 octet. - static constexpr uint8_t kReservedIdx = 1; + static constexpr uint8_t kPreferenceOffset = 3; + static constexpr uint8_t kPreferenceMask = 3 << kPreferenceOffset; - // The bitmask of the Managed Address Configuration ('m') flag. - static constexpr uint8_t kManagedAddressConfigMask = 0x80; - - Ip6::Icmp::Header mHeader; // The common ICMPv6 header. - uint32_t mReachableTime; // The reachable time. In milliseconds. - uint32_t mRetransTimer; // The retransmission timer. In milliseconds. + uint8_t mType; + uint8_t mCode; + uint16_t mChecksum; + uint8_t mCurHopLimit; + uint8_t mFlags; + uint16_t mRouterLifetime; + uint32_t mReachableTime; + uint32_t mRetransTimer; } OT_TOOL_PACKED_END; static_assert(sizeof(RouterAdvMessage) == 16, "invalid RouterAdvMessage structure"); diff --git a/src/core/border_router/routing_manager.cpp b/src/core/border_router/routing_manager.cpp index 509ea838f..341d9d49c 100644 --- a/src/core/border_router/routing_manager.cpp +++ b/src/core/border_router/routing_manager.cpp @@ -1455,6 +1455,7 @@ bool RoutingManager::UpdateRouterAdvMessage(const RouterAdv::RouterAdvMessage *a oldRouterAdvMessage = mRouterAdvMessage; mTimeRouterAdvMessageLastUpdate = TimerMilli::GetNow(); + if (aRouterAdvMessage == nullptr || aRouterAdvMessage->GetRouterLifetime() == 0) { mRouterAdvMessage.SetToDefault(); @@ -1462,7 +1463,12 @@ bool RoutingManager::UpdateRouterAdvMessage(const RouterAdv::RouterAdvMessage *a } else { - mRouterAdvMessage = *aRouterAdvMessage; + // The checksum is set to zero in `mRouterAdvMessage` + // which indicates to platform that it needs to do the + // calculation and update it. + + mRouterAdvMessage = *aRouterAdvMessage; + mRouterAdvMessage.SetChecksum(0); mLearntRouterAdvMessageFromHost = true; } From 0f4531b22fbc2dc45921d775a09ba52592c62b91 Mon Sep 17 00:00:00 2001 From: Jonathan Hui Date: Fri, 20 May 2022 13:02:37 -0700 Subject: [PATCH 23/80] [data-poll-sender] return `kErrorNone` when Data Request is already enqueued (#7725) There are no use cases where `kErrorAlready` is useful. --- include/openthread/instance.h | 2 +- include/openthread/link.h | 1 - src/core/mac/data_poll_sender.cpp | 5 ----- src/core/mac/mac.cpp | 2 +- src/core/mac/mac.hpp | 1 - 5 files changed, 2 insertions(+), 9 deletions(-) diff --git a/include/openthread/instance.h b/include/openthread/instance.h index 5bf972369..d11583e36 100644 --- a/include/openthread/instance.h +++ b/include/openthread/instance.h @@ -53,7 +53,7 @@ extern "C" { * @note This number versions both OpenThread platform and user APIs. * */ -#define OPENTHREAD_API_VERSION (209) +#define OPENTHREAD_API_VERSION (210) /** * @addtogroup api-instance diff --git a/include/openthread/link.h b/include/openthread/link.h index 4be9a79a7..7c77e964d 100644 --- a/include/openthread/link.h +++ b/include/openthread/link.h @@ -486,7 +486,6 @@ bool otLinkIsEnergyScanInProgress(otInstance *aInstance); * @param[in] aInstance A pointer to an OpenThread instance. * * @retval OT_ERROR_NONE Successfully enqueued an IEEE 802.15.4 Data Request message. - * @retval OT_ERROR_ALREADY An IEEE 802.15.4 Data Request message is already enqueued. * @retval OT_ERROR_INVALID_STATE Device is not in rx-off-when-idle mode. * @retval OT_ERROR_NO_BUFS Insufficient message buffers available. * diff --git a/src/core/mac/data_poll_sender.cpp b/src/core/mac/data_poll_sender.cpp index ab1ad0db6..2c5b0899f 100644 --- a/src/core/mac/data_poll_sender.cpp +++ b/src/core/mac/data_poll_sender.cpp @@ -123,11 +123,6 @@ exit: StopPolling(); break; - case kErrorAlready: - LogDebg("Data poll tx requested when a previous data request still in send queue."); - ScheduleNextPoll(kUsePreviousPollPeriod); - break; - default: LogWarn("Unexpected error %s requesting data poll", ErrorToString(error)); ScheduleNextPoll(kRecalculatePollPeriod); diff --git a/src/core/mac/mac.cpp b/src/core/mac/mac.cpp index 7e2f6503c..faaea8094 100644 --- a/src/core/mac/mac.cpp +++ b/src/core/mac/mac.cpp @@ -482,7 +482,7 @@ Error Mac::RequestDataPollTransmission(void) Error error = kErrorNone; VerifyOrExit(IsEnabled(), error = kErrorInvalidState); - VerifyOrExit(!IsActiveOrPending(kOperationTransmitPoll), error = kErrorAlready); + VerifyOrExit(!IsActiveOrPending(kOperationTransmitPoll)); // We ensure data frame and data poll tx requests are handled in the // order they are requested. So if we have a pending direct data frame diff --git a/src/core/mac/mac.hpp b/src/core/mac/mac.hpp index a5929a6d1..940a458bc 100644 --- a/src/core/mac/mac.hpp +++ b/src/core/mac/mac.hpp @@ -228,7 +228,6 @@ public: * This method requests transmission of a data poll (MAC Data Request) frame. * * @retval kErrorNone Data poll transmission request is scheduled successfully. - * @retval kErrorAlready MAC is busy sending earlier poll transmission request. * @retval kErrorInvalidState The MAC layer is not enabled. * */ From fcbc4bef9b634a4d3987fde9ed7f3a954970cd58 Mon Sep 17 00:00:00 2001 From: Jonathan Hui Date: Fri, 20 May 2022 15:34:50 -0700 Subject: [PATCH 24/80] [srp-client] add API to specify TTL (#7735) --- include/openthread/instance.h | 2 +- include/openthread/srp_client.h | 28 +++++++++++++++++++ src/cli/README_SRP_CLIENT.md | 22 +++++++++++++++ src/cli/cli_srp_client.cpp | 7 ++++- src/core/api/srp_client_api.cpp | 10 +++++++ src/core/net/srp_client.cpp | 9 +++--- src/core/net/srp_client.hpp | 26 +++++++++++++++++ tests/scripts/thread-cert/node.py | 10 +++++++ .../test_srp_client_change_lease.py | 14 ++++++++++ 9 files changed, 122 insertions(+), 6 deletions(-) diff --git a/include/openthread/instance.h b/include/openthread/instance.h index d11583e36..c946217c5 100644 --- a/include/openthread/instance.h +++ b/include/openthread/instance.h @@ -53,7 +53,7 @@ extern "C" { * @note This number versions both OpenThread platform and user APIs. * */ -#define OPENTHREAD_API_VERSION (210) +#define OPENTHREAD_API_VERSION (211) /** * @addtogroup api-instance diff --git a/include/openthread/srp_client.h b/include/openthread/srp_client.h index 72c03610a..1dc0c9e3e 100644 --- a/include/openthread/srp_client.h +++ b/include/openthread/srp_client.h @@ -315,6 +315,34 @@ void otSrpClientDisableAutoStartMode(otInstance *aInstance); */ bool otSrpClientIsAutoStartModeEnabled(otInstance *aInstance); +/** + * This function gets the TTL value in every record included in SRP update requests. + * + * Note that this is the TTL requested by the SRP client. The server may choose to accept a different TTL. + * + * By default, the TTL will equal the lease interval. Passing 0 or a value larger than the lease interval via + * `otSrpClientSetTtl()` will also cause the TTL to equal the lease interval. + * + * @param[in] aInstance A pointer to the OpenThread instance. + * + * @returns The TTL (in seconds). + * + */ +uint32_t otSrpClientGetTtl(otInstance *aInstance); + +/** + * This function sets the TTL value in every record included in SRP update requests. + * + * Changing the TTL does not impact the TTL of already registered services/host-info. + * It only affects future SRP update messages (i.e., adding new services and/or refreshes of the existing services). + * + * @param[in] aInstance A pointer to the OpenThread instance. + * @param[in] aTtl The TTL (in seconds). If value is zero or greater than lease interval, the TTL is set to the + * lease interval. + * + */ +void otSrpClientSetTtl(otInstance *aInstance, uint32_t aTtl); + /** * This function gets the lease interval used in SRP update requests. * diff --git a/src/cli/README_SRP_CLIENT.md b/src/cli/README_SRP_CLIENT.md index 5cbe94fac..ab5dcdb77 100644 --- a/src/cli/README_SRP_CLIENT.md +++ b/src/cli/README_SRP_CLIENT.md @@ -15,6 +15,7 @@ Usage : `srp client [command] ...` - [start](#start) - [state](#state) - [stop](#stop) +- [ttl](#ttl) ## Command Details @@ -36,6 +37,7 @@ service start state stop +ttl Done ``` @@ -409,3 +411,23 @@ Stop the SRP client. > srp client stop Done ``` + +### ttl + +Usage: `srp client ttl [value]` + +Get the TTL (in seconds). + +```bash +> srp client ttl +7200 +Done +> +``` + +Set the TTL. + +```bash +> srp client ttl 3600 +Done +``` diff --git a/src/cli/cli_srp_client.cpp b/src/cli/cli_srp_client.cpp index fa1b740b1..4d81cd901 100644 --- a/src/cli/cli_srp_client.cpp +++ b/src/cli/cli_srp_client.cpp @@ -525,6 +525,11 @@ exit: return error; } +template <> otError SrpClient::Process(Arg aArgs[]) +{ + return Interpreter::GetInterpreter().ProcessGetSet(aArgs, otSrpClientGetTtl, otSrpClientSetTtl); +} + void SrpClient::HandleCallback(otError aError, const otSrpClientHostInfo *aHostInfo, const otSrpClientService * aServices, @@ -578,7 +583,7 @@ otError SrpClient::Process(Arg aArgs[]) static constexpr Command kCommands[] = { CmdEntry("autostart"), CmdEntry("callback"), CmdEntry("host"), CmdEntry("keyleaseinterval"), CmdEntry("leaseinterval"), CmdEntry("server"), CmdEntry("service"), CmdEntry("start"), - CmdEntry("state"), CmdEntry("stop"), + CmdEntry("state"), CmdEntry("stop"), CmdEntry("ttl"), }; static_assert(BinarySearch::IsSorted(kCommands), "kCommands is not sorted"); diff --git a/src/core/api/srp_client_api.cpp b/src/core/api/srp_client_api.cpp index 304a2fe2d..a0c16e3d9 100644 --- a/src/core/api/srp_client_api.cpp +++ b/src/core/api/srp_client_api.cpp @@ -84,6 +84,16 @@ bool otSrpClientIsAutoStartModeEnabled(otInstance *aInstance) } #endif // OPENTHREAD_CONFIG_SRP_CLIENT_AUTO_START_API_ENABLE +uint32_t otSrpClientGetTtl(otInstance *aInstance) +{ + return AsCoreType(aInstance).Get().GetTtl(); +} + +void otSrpClientSetTtl(otInstance *aInstance, uint32_t aTtl) +{ + return AsCoreType(aInstance).Get().SetTtl(aTtl); +} + uint32_t otSrpClientGetLeaseInterval(otInstance *aInstance) { return AsCoreType(aInstance).Get().GetLeaseInterval(); diff --git a/src/core/net/srp_client.cpp b/src/core/net/srp_client.cpp index 0667d3096..f9fad2e4b 100644 --- a/src/core/net/srp_client.cpp +++ b/src/core/net/srp_client.cpp @@ -245,6 +245,7 @@ Client::Client(Instance &aInstance) , mUpdateMessageId(0) , mRetryWaitInterval(kMinRetryWaitInterval) , mAcceptedLeaseInterval(0) + , mTtl(0) , mLeaseInterval(kDefaultLease) , mKeyLeaseInterval(kDefaultKeyLease) , mSocket(aInstance) @@ -911,7 +912,7 @@ Error Client::AppendServiceInstructions(Service &aService, Message &aMessage, In // to NONE and TTL to zero (RFC 2136 - section 2.5.4). rr.Init(Dns::ResourceRecord::kTypePtr, removing ? Dns::PtrRecord::kClassNone : Dns::PtrRecord::kClassInternet); - rr.SetTtl(removing ? 0 : mLeaseInterval); + rr.SetTtl(removing ? 0 : GetTtl()); offset = aMessage.GetLength(); SuccessOrExit(error = aMessage.Append(rr)); @@ -970,7 +971,7 @@ Error Client::AppendServiceInstructions(Service &aService, Message &aMessage, In SuccessOrExit(error = Dns::Name::AppendPointerLabel(instanceNameOffset, aMessage)); srv.Init(); - srv.SetTtl(mLeaseInterval); + srv.SetTtl(GetTtl()); srv.SetPriority(aService.GetPriority()); srv.SetWeight(aService.GetWeight()); srv.SetPort(aService.GetPort()); @@ -1024,7 +1025,7 @@ Error Client::AppendHostDescriptionInstruction(Message &aMessage, Info &aInfo) c // AAAA RRs rr.Init(Dns::ResourceRecord::kTypeAaaa); - rr.SetTtl(mLeaseInterval); + rr.SetTtl(GetTtl()); rr.SetLength(sizeof(Ip6::Address)); for (uint8_t index = 0; index < mHostInfo.GetNumAddresses(); index++) @@ -1051,7 +1052,7 @@ Error Client::AppendKeyRecord(Message &aMessage, Info &aInfo) const Crypto::Ecdsa::P256::PublicKey publicKey; key.Init(); - key.SetTtl(mLeaseInterval); + key.SetTtl(GetTtl()); key.SetFlags(Dns::KeyRecord::kAuthConfidPermitted, Dns::KeyRecord::kOwnerNonZone, Dns::KeyRecord::kSignatoryFlagGeneral); key.SetProtocol(Dns::KeyRecord::kProtocolDnsSec); diff --git a/src/core/net/srp_client.hpp b/src/core/net/srp_client.hpp index 80b381ffd..8e7cca7dc 100644 --- a/src/core/net/srp_client.hpp +++ b/src/core/net/srp_client.hpp @@ -410,6 +410,31 @@ public: */ void SetCallback(Callback aCallback, void *aContext); + /** + * This method gets the TTL used in SRP update requests. + * + * Note that this is the TTL requested by the SRP client. The server may choose to accept a different TTL. + * + * By default, the TTL will equal the lease interval. Passing 0 or a value larger than the lease interval via + * `otSrpClientSetTtl()` will also cause the TTL to equal the lease interval. + * + * @returns The TTL (in seconds). + * + */ + uint32_t GetTtl(void) const { return (0 < mTtl && mTtl < mLeaseInterval) ? mTtl : mLeaseInterval; } + + /** + * This method sets the TTL used in SRP update requests. + * + * Changing the TTL does not impact the TTL of already registered services/host-info. + * It only changes any future SRP update messages (i.e adding new services and/or refreshes of existing services). + * + * @param[in] aTtl The TTL (in seconds). If value is zero or greater than lease interval, the TTL is set to the + * lease interval. + * + */ + void SetTtl(uint32_t aTtl) { mTtl = aTtl; } + /** * This method gets the lease interval used in SRP update requests. * @@ -922,6 +947,7 @@ private: TimeMilli mLeaseRenewTime; uint32_t mAcceptedLeaseInterval; + uint32_t mTtl; uint32_t mLeaseInterval; uint32_t mKeyLeaseInterval; diff --git a/tests/scripts/thread-cert/node.py b/tests/scripts/thread-cert/node.py index 21bb6bed2..add478fa5 100755 --- a/tests/scripts/thread-cert/node.py +++ b/tests/scripts/thread-cert/node.py @@ -1156,6 +1156,16 @@ class NodeImpl: self.send_command(cmd) return int(self._expect_result('\d+')) + def srp_client_set_ttl(self, ttl: int): + cmd = f'srp client ttl {ttl}' + self.send_command(cmd) + self._expect_done() + + def srp_client_get_ttl(self) -> int: + cmd = 'srp client ttl' + self.send_command(cmd) + return int(self._expect_result('\d+')) + # # TREL utilities # diff --git a/tests/scripts/thread-cert/test_srp_client_change_lease.py b/tests/scripts/thread-cert/test_srp_client_change_lease.py index 532194a3d..5c1245da0 100755 --- a/tests/scripts/thread-cert/test_srp_client_change_lease.py +++ b/tests/scripts/thread-cert/test_srp_client_change_lease.py @@ -50,9 +50,11 @@ DEFAULT_LEASE_TIME = 7200 LEASE_TIME = 60 NEW_LEASE_TIME = 120 KEY_LEASE_TIME = 240 +NEW_TTL = 10 assert LEASE_TIME < KEY_LEASE_TIME assert NEW_LEASE_TIME < KEY_LEASE_TIME +assert NEW_TTL < NEW_LEASE_TIME class SrpClientChangeLeaseTime(thread_cert.TestCase): @@ -112,6 +114,14 @@ class SrpClientChangeLeaseTime(thread_cert.TestCase): self.simulator.go(KEY_LEASE_TIME * 2) self.assertEqual(client.srp_client_get_host_state(), 'Registered') + client.srp_client_set_ttl(NEW_TTL) + self.simulator.go(NEW_LEASE_TIME) + self.assertEqual(client.srp_client_get_host_state(), 'Registered') + + client.srp_client_set_ttl(0) + self.simulator.go(NEW_LEASE_TIME) + self.assertEqual(client.srp_client_get_host_state(), 'Registered') + def verify(self, pv): pkts: PacketFilter = pv.pkts pv.summary.show() @@ -122,6 +132,10 @@ class SrpClientChangeLeaseTime(thread_cert.TestCase): DEFAULT_LEASE_TIME=DEFAULT_LEASE_TIME).must_next() pkts.filter_wpan_src64(CLIENT_SRC64).filter('dns.flags.response == 0 and dns.resp.ttl == {NEW_LEASE_TIME}', NEW_LEASE_TIME=NEW_LEASE_TIME).must_next() + pkts.filter_wpan_src64(CLIENT_SRC64).filter('dns.flags.response == 0 and dns.resp.ttl == {NEW_TTL}', + NEW_TTL=NEW_TTL).must_next() + pkts.filter_wpan_src64(CLIENT_SRC64).filter('dns.flags.response == 0 and dns.resp.ttl == {NEW_LEASE_TIME}', + NEW_LEASE_TIME=NEW_LEASE_TIME).must_next() def check_host_and_service(self, server, client): """Check that we have properly registered host and service instance. From c319046599e68f3c85ae050f574ad267be8c0d53 Mon Sep 17 00:00:00 2001 From: Simon Lin Date: Tue, 24 May 2022 02:04:28 +0800 Subject: [PATCH 25/80] [routing-manager] fix BR incorrectly removing route for local on-link prefix (#7724) This commit fixes a bug that BR could incorrectly remove the route of the local on-link prefix in Network Data when it's still advertising the local on-link prefix. This is a short-term fix because in the long term we want to refactor how `RoutingManager` manages external routes and the default route. --- src/core/border_router/routing_manager.cpp | 44 +++++++++++++++++++--- 1 file changed, 39 insertions(+), 5 deletions(-) diff --git a/src/core/border_router/routing_manager.cpp b/src/core/border_router/routing_manager.cpp index 341d9d49c..f6a7a4eca 100644 --- a/src/core/border_router/routing_manager.cpp +++ b/src/core/border_router/routing_manager.cpp @@ -616,8 +616,25 @@ void RoutingManager::HandleOnLinkPrefixDeprecateTimer(Timer &aTimer) void RoutingManager::HandleOnLinkPrefixDeprecateTimer(void) { + bool discoveredLocalOnLinkPrefix = false; + + OT_ASSERT(!mIsAdvertisingLocalOnLinkPrefix); + LogInfo("Local on-link prefix %s expired", mLocalOnLinkPrefix.ToString().AsCString()); - UnpublishExternalRoute(mLocalOnLinkPrefix); + + for (const ExternalPrefix &prefix : mDiscoveredPrefixes) + { + if (prefix.mIsOnLinkPrefix && prefix.mPrefix == mLocalOnLinkPrefix) + { + discoveredLocalOnLinkPrefix = true; + break; + } + } + + if (!discoveredLocalOnLinkPrefix) + { + UnpublishExternalRoute(mLocalOnLinkPrefix); + } } void RoutingManager::DeprecateOnLinkPrefix(void) @@ -726,8 +743,17 @@ void RoutingManager::EvaluateRoutingPolicy(void) } // 3. Update advertised on-link & OMR prefixes information. - mIsAdvertisingLocalOnLinkPrefix = (newOnLinkPrefix == &mLocalOnLinkPrefix); - mAdvertisedOmrPrefixes = newOmrPrefixes; + { + bool wasAdvertisingLocalOnLinkPrefix = mIsAdvertisingLocalOnLinkPrefix; + + mIsAdvertisingLocalOnLinkPrefix = (newOnLinkPrefix == &mLocalOnLinkPrefix); + if (!wasAdvertisingLocalOnLinkPrefix && mIsAdvertisingLocalOnLinkPrefix) + { + InvalidateDiscoveredPrefixes(); + } + + mAdvertisedOmrPrefixes = newOmrPrefixes; + } } void RoutingManager::StartRoutingPolicyEvaluationJitter(uint32_t aJitterMilli) @@ -1382,6 +1408,9 @@ void RoutingManager::InvalidateDiscoveredPrefixes(const Ip6::Prefix *aPrefix, bo for (const ExternalPrefix &prefix : mDiscoveredPrefixes) { + bool isAdvertisedLocalOnLinkPrefix = + mIsAdvertisingLocalOnLinkPrefix && prefix.mIsOnLinkPrefix && mLocalOnLinkPrefix == prefix.mPrefix; + if ( // Invalidate specified prefix (aPrefix != nullptr && prefix.mPrefix == *aPrefix && prefix.mIsOnLinkPrefix == aIsOnLinkPrefix) || @@ -1389,9 +1418,14 @@ void RoutingManager::InvalidateDiscoveredPrefixes(const Ip6::Prefix *aPrefix, bo (prefix.GetExpireTime() <= now) || // Invalidate Local OMR prefixes (!prefix.mIsOnLinkPrefix && - (mAdvertisedOmrPrefixes.Contains(prefix.mPrefix) || NetworkDataContainsOmrPrefix(prefix.mPrefix)))) + (mAdvertisedOmrPrefixes.Contains(prefix.mPrefix) || NetworkDataContainsOmrPrefix(prefix.mPrefix))) || + // Remove local on-link prefix if the BR is advertising on-link prefix + isAdvertisedLocalOnLinkPrefix) { - UnpublishExternalRoute(prefix.mPrefix); + if (!isAdvertisedLocalOnLinkPrefix) + { + UnpublishExternalRoute(prefix.mPrefix); + } } else { From 65c6f39300fcd8e87cf140f2c2cdebdd672c2e29 Mon Sep 17 00:00:00 2001 From: Simon Lin Date: Tue, 24 May 2022 02:07:40 +0800 Subject: [PATCH 26/80] [routing-manager] handle deprecated OMR prefix (#7703) This commit enhances routing-manager to avoid using deprecated (i.e. P_preferred=false) OMR prefixes as the winning OMR prefix, but still sends RA RIO for the deprecating OMR prefixes. --- src/core/border_router/routing_manager.cpp | 17 ++++++------- .../border_router/test_manual_omr_prefix.py | 24 +++++++++++++++++++ tests/scripts/thread-cert/pktverify/consts.py | 2 ++ .../thread-cert/pktverify/layer_fields.py | 8 +------ 4 files changed, 36 insertions(+), 15 deletions(-) diff --git a/src/core/border_router/routing_manager.cpp b/src/core/border_router/routing_manager.cpp index f6a7a4eca..f606bfc2b 100644 --- a/src/core/border_router/routing_manager.cpp +++ b/src/core/border_router/routing_manager.cpp @@ -401,11 +401,14 @@ void RoutingManager::EvaluateOmrPrefix(OmrPrefixArray &aNewOmrPrefixes) continue; } - if (electedOmrPrefix == nullptr || onMeshPrefixConfig.mPreference > electedOmrPrefixPreference || - (onMeshPrefixConfig.mPreference == electedOmrPrefixPreference && prefix < *electedOmrPrefix)) + if (onMeshPrefixConfig.mPreferred) { - electedOmrPrefix = aNewOmrPrefixes.Back(); - electedOmrPrefixPreference = onMeshPrefixConfig.mPreference; + if (electedOmrPrefix == nullptr || onMeshPrefixConfig.mPreference > electedOmrPrefixPreference || + (onMeshPrefixConfig.mPreference == electedOmrPrefixPreference && prefix < *electedOmrPrefix)) + { + electedOmrPrefix = aNewOmrPrefixes.Back(); + electedOmrPrefixPreference = onMeshPrefixConfig.mPreference; + } } if (prefix == mLocalOmrPrefix) @@ -416,9 +419,9 @@ void RoutingManager::EvaluateOmrPrefix(OmrPrefixArray &aNewOmrPrefixes) // Decide if we need to add or remove our local OMR prefix. - if (aNewOmrPrefixes.IsEmpty()) + if (electedOmrPrefix == nullptr) { - LogInfo("EvaluateOmrPrefix: No valid OMR prefixes found in Thread network"); + LogInfo("EvaluateOmrPrefix: No preferred OMR prefixes found in Thread network"); if (PublishLocalOmrPrefix() == kErrorNone) { @@ -430,8 +433,6 @@ void RoutingManager::EvaluateOmrPrefix(OmrPrefixArray &aNewOmrPrefixes) } else { - OT_ASSERT(electedOmrPrefix != nullptr); - if (*electedOmrPrefix == mLocalOmrPrefix) { IgnoreError(PublishLocalOmrPrefix()); diff --git a/tests/scripts/thread-cert/border_router/test_manual_omr_prefix.py b/tests/scripts/thread-cert/border_router/test_manual_omr_prefix.py index e9035c69d..043a37128 100644 --- a/tests/scripts/thread-cert/border_router/test_manual_omr_prefix.py +++ b/tests/scripts/thread-cert/border_router/test_manual_omr_prefix.py @@ -31,6 +31,8 @@ import unittest import config import thread_cert +from pktverify.consts import ICMPV6_RA_OPT_TYPE_RIO +from pktverify.packet_verifier import PacketVerifier # Test description: # The purpose of this test case is to verify that BR can handle manually added OMR prefixes properly. @@ -89,6 +91,28 @@ class ManualOmrsPrefix(thread_cert.TestCase): self._test_manual_omr_prefix('2002::/64', 'pars', expect_withdraw=False) # Add a smaller but invalid OMR prefix (P_stable = 0). Verify BR_1 does not withdraw its OMR prefix. self._test_manual_omr_prefix('2003::/64', 'paro', expect_withdraw=False) + # Add a deprecating OMR prefix (P_preferred = 0). Verify BR_1 does not withdraw its OMR prefix. + self._test_manual_omr_prefix('2004::/64', 'aros', expect_withdraw=False) + + self.collect_ipaddrs() + self.collect_extra_vars(BR_1_OMR_PREFIX=br1.get_br_omr_prefix()) + + def verify(self, pv: PacketVerifier): + pkts = pv.pkts + pv.summary.show() + + BR_1_ETH = pv.vars['BR_1_ETH'] + + BR_1_OMR_PREFIX = pv.vars['BR_1_OMR_PREFIX'] + assert BR_1_OMR_PREFIX.endswith('::/64') + BR_1_OMR_PREFIX = BR_1_OMR_PREFIX[:-3] + + # Filter all ICMPv6 RA messages advertised by BR_1 + ra_pkts = pkts.filter_eth_src(BR_1_ETH).filter_icmpv6_nd_ra() + + # Verify BR_1 sends RA RIO for both BR OMR prefix and deprecating OMR prefix (i.e. 2004::/64) + ra_pkts.filter(lambda p: ICMPV6_RA_OPT_TYPE_RIO in p.icmpv6.opt.type and '2004::' in p.icmpv6.opt.prefix and + BR_1_OMR_PREFIX in p.icmpv6.opt.prefix).must_next() def _test_manual_omr_prefix(self, prefix, flags, expect_withdraw, prf='med'): br1 = self.nodes[BR_1] diff --git a/tests/scripts/thread-cert/pktverify/consts.py b/tests/scripts/thread-cert/pktverify/consts.py index 3946d28f3..888e9e7e5 100644 --- a/tests/scripts/thread-cert/pktverify/consts.py +++ b/tests/scripts/thread-cert/pktverify/consts.py @@ -397,6 +397,8 @@ ICMPV6_TYPE_PARAMETER_PROBLEM = 4 ICMPV6_TYPE_ECHO_REQUEST = 128 ICMPV6_TYPE_ECHO_REPLY = 129 +ICMPV6_RA_OPT_TYPE_RIO = 24 + THREAD_ALLOWED_ICMPV6_TYPES = [ ICMPV6_TYPE_DESTINATION_UNREACHABLE, ICMPV6_TYPE_PACKET_TO_BIG, diff --git a/tests/scripts/thread-cert/pktverify/layer_fields.py b/tests/scripts/thread-cert/pktverify/layer_fields.py index bd69d352d..eb17e557d 100644 --- a/tests/scripts/thread-cert/pktverify/layer_fields.py +++ b/tests/scripts/thread-cert/pktverify/layer_fields.py @@ -477,13 +477,7 @@ _LAYER_FIELDS = { 'icmpv6.opt.route_lifetime': _auto, 'icmpv6.opt.route_info.flag.route_preference': _auto, 'icmpv6.opt.route_info.flag.reserved': _auto, - 'icmpv6.opt.prefix.valid_lifetime': _auto, - 'icmpv6.opt.prefix.preferred_lifetime': _auto, - 'icmpv6.opt.prefix.length': _list(_auto), - 'icmpv6.opt.prefix.flag.reserved': _auto, - 'icmpv6.opt.prefix.flag.r': _auto, - 'icmpv6.opt.prefix.flag.l': _auto, - 'icmpv6.opt.prefix.flag.a': _auto, + 'icmpv6.opt.prefix': _list(_ipv6_addr), 'icmpv6.opt.length': _list(_auto), 'icmpv6.opt.reserved': _str, 'icmpv6.nd.ra.router_lifetime': _auto, From 56db0d3eff95d387b8c9c4abc61690e81eea71c7 Mon Sep 17 00:00:00 2001 From: Jonathan Hui Date: Mon, 23 May 2022 13:26:04 -0700 Subject: [PATCH 27/80] [routing-manager] remove vicarious router discovery feature (#7727) The vicarious router discovery feature depends on the ability to receive Router Advertisement messages sent in response to Router Solicitation messages from other hosts. However, this does not work since Router Advertisement messages are often sent unicast in response. --- src/core/border_router/routing_manager.cpp | 39 ------------------- src/core/border_router/routing_manager.hpp | 15 ------- src/core/config/border_router.h | 10 ----- .../config/openthread-core-config-check.h | 4 ++ ...uter_solicit.py => test_on_link_prefix.py} | 2 +- 5 files changed, 5 insertions(+), 65 deletions(-) rename tests/scripts/thread-cert/border_router/{test_vicarious_router_solicit.py => test_on_link_prefix.py} (99%) diff --git a/src/core/border_router/routing_manager.cpp b/src/core/border_router/routing_manager.cpp index f606bfc2b..c05edc0a1 100644 --- a/src/core/border_router/routing_manager.cpp +++ b/src/core/border_router/routing_manager.cpp @@ -73,9 +73,6 @@ RoutingManager::RoutingManager(Instance &aInstance) , mDiscoveredPrefixStaleTimer(aInstance, HandleDiscoveredPrefixStaleTimer) , mRouterAdvertisementCount(0) , mLastRouterAdvertisementSendTime(TimerMilli::GetNow() - kMinDelayBetweenRtrAdvs) -#if OPENTHREAD_CONFIG_BORDER_ROUTING_VICARIOUS_RS_ENABLE - , mVicariousRouterSolicitTimer(aInstance, HandleVicariousRouterSolicitTimer) -#endif , mRouterSolicitTimer(aInstance, HandleRouterSolicitTimer) , mRouterSolicitCount(0) , mRoutingPolicyTimer(aInstance, HandleRoutingPolicyTimer) @@ -292,9 +289,6 @@ void RoutingManager::Stop(void) mRouterAdvertisementCount = 0; -#if OPENTHREAD_CONFIG_BORDER_ROUTING_VICARIOUS_RS_ENABLE - mVicariousRouterSolicitTimer.Stop(); -#endif mRouterSolicitTimer.Stop(); mRouterSolicitCount = 0; @@ -787,10 +781,6 @@ void RoutingManager::StartRouterSolicitationDelay(void) OT_ASSERT(mRouterSolicitCount == 0); -#if OPENTHREAD_CONFIG_BORDER_ROUTING_VICARIOUS_RS_ENABLE - mVicariousRouterSolicitTimer.Stop(); -#endif - static_assert(kMaxRtrSolicitationDelay > 0, "invalid maximum Router Solicitation delay"); randomDelay = Random::NonCrypto::GetUint32InRange(0, Time::SecToMsec(kMaxRtrSolicitationDelay)); @@ -991,27 +981,6 @@ bool RoutingManager::IsValidOnLinkPrefix(const Ip6::Prefix &aOnLinkPrefix) return !aOnLinkPrefix.IsLinkLocal() && !aOnLinkPrefix.IsMulticast(); } -#if OPENTHREAD_CONFIG_BORDER_ROUTING_VICARIOUS_RS_ENABLE -void RoutingManager::HandleVicariousRouterSolicitTimer(Timer &aTimer) -{ - aTimer.Get().HandleVicariousRouterSolicitTimer(); -} - -void RoutingManager::HandleVicariousRouterSolicitTimer(void) -{ - LogInfo("Vicarious router solicitation time out"); - - for (const ExternalPrefix &prefix : mDiscoveredPrefixes) - { - if (prefix.mTimeLastUpdate <= mTimeVicariousRouterSolicitStart) - { - StartRouterSolicitationDelay(); - break; - } - } -} -#endif - void RoutingManager::HandleRouterSolicitTimer(Timer &aTimer) { aTimer.Get().HandleRouterSolicitTimer(); @@ -1118,14 +1087,6 @@ void RoutingManager::HandleRouterSolicit(const InfraIf::Icmp6Packet &aPacket, co LogInfo("Received Router Solicitation from %s on %s", aSrcAddress.ToString().AsCString(), mInfraIf.ToString().AsCString()); -#if OPENTHREAD_CONFIG_BORDER_ROUTING_VICARIOUS_RS_ENABLE - if (!mVicariousRouterSolicitTimer.IsRunning()) - { - mTimeVicariousRouterSolicitStart = TimerMilli::GetNow(); - mVicariousRouterSolicitTimer.Start(Time::SecToMsec(kVicariousSolicitationTime)); - } -#endif - // Schedule routing policy evaluation with random jitter to respond with Router Advertisement. StartRoutingPolicyEvaluationJitter(kRaReplyJitter); } diff --git a/src/core/border_router/routing_manager.hpp b/src/core/border_router/routing_manager.hpp index 02c68de5c..2ec9e6828 100644 --- a/src/core/border_router/routing_manager.hpp +++ b/src/core/border_router/routing_manager.hpp @@ -237,13 +237,6 @@ private: // The value is chosen in range of [`kMaxRtrAdvInterval` upper bound (1800s), `kDefaultOnLinkPrefixLifetime`]. static constexpr uint32_t kRtrAdvStaleTime = 1800; - // The VICARIOUS_SOLICIT_TIME in seconds. The Routing Manager will consider - // the discovered prefixes invalid if they are not refreshed after receiving - // a Router Solicitation message. - // The value is equal to Router Solicitation timeout. - static constexpr uint32_t kVicariousSolicitationTime = - kRtrSolicitationInterval * (kMaxRtrSolicitations - 1) + kMaxRtrSolicitationDelay; - static_assert(kMinRtrAdvInterval <= 3 * kMaxRtrAdvInterval / 4, "invalid RA intervals"); static_assert(kDefaultOmrPrefixLifetime >= kMaxRtrAdvInterval, "invalid default OMR prefix lifetime"); static_assert(kDefaultOnLinkPrefixLifetime >= kMaxRtrAdvInterval, "invalid default on-link prefix lifetime"); @@ -327,10 +320,6 @@ private: void SendRouterAdvertisement(const OmrPrefixArray &aNewOmrPrefixes, const Ip6::Prefix *aNewOnLinkPrefix); bool IsRouterSolicitationInProgress(void) const; -#if OPENTHREAD_CONFIG_BORDER_ROUTING_VICARIOUS_RS_ENABLE - static void HandleVicariousRouterSolicitTimer(Timer &aTimer); - void HandleVicariousRouterSolicitTimer(void); -#endif static void HandleRouterSolicitTimer(Timer &aTimer); void HandleRouterSolicitTimer(void); static void HandleDiscoveredPrefixInvalidTimer(Timer &aTimer); @@ -416,10 +405,6 @@ private: uint32_t mRouterAdvertisementCount; TimeMilli mLastRouterAdvertisementSendTime; -#if OPENTHREAD_CONFIG_BORDER_ROUTING_VICARIOUS_RS_ENABLE - TimerMilli mVicariousRouterSolicitTimer; - TimeMilli mTimeVicariousRouterSolicitStart; -#endif TimerMilli mRouterSolicitTimer; TimeMilli mTimeRouterSolicitStart; uint8_t mRouterSolicitCount; diff --git a/src/core/config/border_router.h b/src/core/config/border_router.h index 5e45b387a..f3959d50d 100644 --- a/src/core/config/border_router.h +++ b/src/core/config/border_router.h @@ -75,16 +75,6 @@ #define OPENTHREAD_CONFIG_BORDER_ROUTING_MAX_DISCOVERED_PREFIXES 8 #endif -/** - * @def OPENTHREAD_CONFIG_BORDER_ROUTING_VICARIOUS_RS_ENABLE - * - * Define to 1 to enable Border Routing Vicarious Router Solicitation. - * - */ -#ifndef OPENTHREAD_CONFIG_BORDER_ROUTING_VICARIOUS_RS_ENABLE -#define OPENTHREAD_CONFIG_BORDER_ROUTING_VICARIOUS_RS_ENABLE 1 -#endif - /** * @def OPENTHREAD_CONFIG_BORDER_ROUTING_NAT64_ENABLE * diff --git a/src/core/config/openthread-core-config-check.h b/src/core/config/openthread-core-config-check.h index ed8495aaa..1536ec2ff 100644 --- a/src/core/config/openthread-core-config-check.h +++ b/src/core/config/openthread-core-config-check.h @@ -623,4 +623,8 @@ #error "OPENTHREAD_CONFIG_PLATFORM_CSL_UNCERT was removed and no longer supported" #endif +#ifdef OPENTHREAD_CONFIG_BORDER_ROUTING_VICARIOUS_RS_ENABLE +#error "OPENTHREAD_CONFIG_BORDER_ROUTING_VICARIOUS_RS_ENABLE was removed and no longer supported" +#endif + #endif // OPENTHREAD_CORE_CONFIG_CHECK_H_ diff --git a/tests/scripts/thread-cert/border_router/test_vicarious_router_solicit.py b/tests/scripts/thread-cert/border_router/test_on_link_prefix.py similarity index 99% rename from tests/scripts/thread-cert/border_router/test_vicarious_router_solicit.py rename to tests/scripts/thread-cert/border_router/test_on_link_prefix.py index 065cdae1b..8d273f1aa 100755 --- a/tests/scripts/thread-cert/border_router/test_vicarious_router_solicit.py +++ b/tests/scripts/thread-cert/border_router/test_on_link_prefix.py @@ -34,7 +34,7 @@ import config import thread_cert # Test description: -# This test verifies Vicarious Router Solicitation. +# This test verifies on-link prefix configuration. # # Topology: # -------------(eth)---------------------------- From 58f56bbec2d638e68ebebacfc7b661893b513961 Mon Sep 17 00:00:00 2001 From: Abtin Keshavarzian Date: Mon, 23 May 2022 13:37:51 -0700 Subject: [PATCH 28/80] [srp-server] check allocated `UpdateMetadata` is not `nullptr` (#7717) --- src/core/net/srp_server.cpp | 30 +++++++++++++++++++++--------- src/core/net/srp_server.hpp | 1 + 2 files changed, 22 insertions(+), 9 deletions(-) diff --git a/src/core/net/srp_server.cpp b/src/core/net/srp_server.cpp index 9621ff9e3..ca012ab61 100644 --- a/src/core/net/srp_server.cpp +++ b/src/core/net/srp_server.cpp @@ -1192,20 +1192,32 @@ void Server::HandleUpdate(Host &aHost, const MessageMetadata &aMetadata) } exit: - if ((error == kErrorNone) && (mServiceUpdateHandler != nullptr)) + InformUpdateHandlerOrCommit(error, aHost, aMetadata); +} + +void Server::InformUpdateHandlerOrCommit(Error aError, Host &aHost, const MessageMetadata &aMetadata) +{ + if ((aError == kErrorNone) && (mServiceUpdateHandler != nullptr)) { UpdateMetadata *update = UpdateMetadata::Allocate(GetInstance(), aHost, aMetadata); - mOutstandingUpdates.Push(*update); - mOutstandingUpdatesTimer.FireAtIfEarlier(update->GetExpireTime()); + if (update != nullptr) + { + mOutstandingUpdates.Push(*update); + mOutstandingUpdatesTimer.FireAtIfEarlier(update->GetExpireTime()); - LogInfo("SRP update handler is notified (updatedId = %u)", update->GetId()); - mServiceUpdateHandler(update->GetId(), &aHost, kDefaultEventsHandlerTimeout, mServiceUpdateHandlerContext); - } - else - { - CommitSrpUpdate(error, aHost, aMetadata); + LogInfo("SRP update handler is notified (updatedId = %u)", update->GetId()); + mServiceUpdateHandler(update->GetId(), &aHost, kDefaultEventsHandlerTimeout, mServiceUpdateHandlerContext); + ExitNow(); + } + + aError = kErrorNoBufs; } + + CommitSrpUpdate(aError, aHost, aMetadata); + +exit: + return; } void Server::SendResponse(const Dns::UpdateHeader & aHeader, diff --git a/src/core/net/srp_server.hpp b/src/core/net/srp_server.hpp index 6289f3a2a..f31bd21b1 100644 --- a/src/core/net/srp_server.hpp +++ b/src/core/net/srp_server.hpp @@ -887,6 +887,7 @@ private: ServiceUpdateId AllocateId(void) { return mServiceUpdateId++; } + void InformUpdateHandlerOrCommit(Error aError, Host &aHost, const MessageMetadata &aMetadata); void CommitSrpUpdate(Error aError, Host &aHost, const MessageMetadata &aMessageMetadata); void CommitSrpUpdate(Error aError, UpdateMetadata &aUpdateMetadata); void CommitSrpUpdate(Error aError, From 5f2437939d26e54e8c566ca3fa23cc1723db41eb Mon Sep 17 00:00:00 2001 From: Abtin Keshavarzian Date: Mon, 23 May 2022 14:52:16 -0700 Subject: [PATCH 29/80] [routing-manager] simplify `ExternalPrefix` (#7723) This commit contains smaller enhancement in `RoutingManager` related to `ExternalPrefix` class (which represents a prefix discovered from processing Router Advertisements from infrastructure netif). This commit also uses `Array::Find()` methods when searching in the `mDiscoveredPrefixes` array. --- src/core/border_router/routing_manager.cpp | 193 ++++++++++++--------- src/core/border_router/routing_manager.hpp | 64 +++---- 2 files changed, 138 insertions(+), 119 deletions(-) diff --git a/src/core/border_router/routing_manager.cpp b/src/core/border_router/routing_manager.cpp index c05edc0a1..8c2dd58f9 100644 --- a/src/core/border_router/routing_manager.cpp +++ b/src/core/border_router/routing_manager.cpp @@ -560,14 +560,14 @@ const Ip6::Prefix *RoutingManager::EvaluateOnLinkPrefix(void) for (const ExternalPrefix &prefix : mDiscoveredPrefixes) { - if (!prefix.mIsOnLinkPrefix || prefix.IsDeprecated()) + if (!prefix.IsOnLinkPrefix() || prefix.IsDeprecated()) { continue; } - if (smallestOnLinkPrefix == nullptr || (prefix.mPrefix < *smallestOnLinkPrefix)) + if (smallestOnLinkPrefix == nullptr || (prefix.GetPrefix() < *smallestOnLinkPrefix)) { - smallestOnLinkPrefix = &prefix.mPrefix; + smallestOnLinkPrefix = &prefix.GetPrefix(); } } @@ -619,7 +619,7 @@ void RoutingManager::HandleOnLinkPrefixDeprecateTimer(void) for (const ExternalPrefix &prefix : mDiscoveredPrefixes) { - if (prefix.mIsOnLinkPrefix && prefix.mPrefix == mLocalOnLinkPrefix) + if (prefix.IsOnLinkPrefix() && prefix.GetPrefix() == mLocalOnLinkPrefix) { discoveredLocalOnLinkPrefix = true; break; @@ -1024,15 +1024,15 @@ void RoutingManager::HandleRouterSolicitTimer(void) // Invalidate/deprecate all OMR/on-link prefixes that are not refreshed during Router Solicitation. for (ExternalPrefix &prefix : mDiscoveredPrefixes) { - if (prefix.mTimeLastUpdate <= mTimeRouterSolicitStart) + if (prefix.GetLastUpdateTime() <= mTimeRouterSolicitStart) { - if (prefix.mIsOnLinkPrefix) + if (prefix.IsOnLinkPrefix()) { - prefix.mPreferredLifetime = 0; + prefix.ClearPreferredLifetime(); } else { - InvalidateDiscoveredPrefixes(&prefix.mPrefix, prefix.mIsOnLinkPrefix); + InvalidateDiscoveredPrefixes(&prefix.GetPrefix(), prefix.IsOnLinkPrefix()); } } } @@ -1091,22 +1091,6 @@ void RoutingManager::HandleRouterSolicit(const InfraIf::Icmp6Packet &aPacket, co StartRoutingPolicyEvaluationJitter(kRaReplyJitter); } -uint32_t RoutingManager::ExternalPrefix::GetPrefixExpireDelay(uint32_t aValidLifetime) -{ - uint32_t delay; - - if (aValidLifetime * static_cast(1000) > Timer::kMaxDelay) - { - delay = Timer::kMaxDelay; - } - else - { - delay = aValidLifetime * 1000; - } - - return delay; -} - void RoutingManager::HandleRouterAdvertisement(const InfraIf::Icmp6Packet &aPacket, const Ip6::Address &aSrcAddress) { OT_ASSERT(mIsRunning); @@ -1204,23 +1188,13 @@ bool RoutingManager::UpdateDiscoveredOnLinkPrefix(const RouterAdv::PrefixInfoOpt LogInfo("Discovered on-link prefix (%s, %u seconds) from %s", prefix.ToString().AsCString(), aPio.GetValidLifetime(), mInfraIf.ToString().AsCString()); - onLinkPrefix.mIsOnLinkPrefix = true; - onLinkPrefix.mPrefix = prefix; - onLinkPrefix.mValidLifetime = aPio.GetValidLifetime(); - onLinkPrefix.mPreferredLifetime = aPio.GetPreferredLifetime(); - onLinkPrefix.mTimeLastUpdate = TimerMilli::GetNow(); + onLinkPrefix.InitFrom(aPio); - for (ExternalPrefix &externalPrefix : mDiscoveredPrefixes) - { - if (externalPrefix == onLinkPrefix) - { - existingPrefix = &externalPrefix; - } - } + existingPrefix = mDiscoveredPrefixes.Find(onLinkPrefix); if (existingPrefix == nullptr) { - if (onLinkPrefix.mValidLifetime == 0) + if (onLinkPrefix.GetValidLifetime() == 0) { ExitNow(); } @@ -1240,34 +1214,11 @@ bool RoutingManager::UpdateDiscoveredOnLinkPrefix(const RouterAdv::PrefixInfoOpt } else { - constexpr uint32_t kTwoHoursInSeconds = 2 * 3600; - - // Per RFC 4862 section 5.5.3.e: - // 1. If the received Valid Lifetime is greater than 2 hours or - // greater than RemainingLifetime, set the valid lifetime of the - // corresponding address to the advertised Valid Lifetime. - // 2. If RemainingLifetime is less than or equal to 2 hours, ignore - // the Prefix Information option with regards to the valid - // lifetime, unless ... - // 3. Otherwise, reset the valid lifetime of the corresponding - // address to 2 hours. - - if (onLinkPrefix.mValidLifetime > kTwoHoursInSeconds || - onLinkPrefix.GetExpireTime() > existingPrefix->GetExpireTime()) - { - existingPrefix->mValidLifetime = onLinkPrefix.mValidLifetime; - } - else if (existingPrefix->GetExpireTime() > TimerMilli::GetNow() + TimeMilli::SecToMsec(kTwoHoursInSeconds)) - { - existingPrefix->mValidLifetime = kTwoHoursInSeconds; - } - // The on-link prefix routing policy may be affected when a // discovered on-link prefix becomes deprecated or preferred. needReevaluate = (onLinkPrefix.IsDeprecated() != existingPrefix->IsDeprecated()); - existingPrefix->mPreferredLifetime = onLinkPrefix.mPreferredLifetime; - existingPrefix->mTimeLastUpdate = onLinkPrefix.mTimeLastUpdate; + existingPrefix->AdoptValidAndPreferredLiftimesFrom(onLinkPrefix); } mDiscoveredPrefixInvalidTimer.FireAtIfEarlier(existingPrefix->GetExpireTime()); @@ -1318,30 +1269,20 @@ void RoutingManager::UpdateDiscoveredOmrPrefix(const RouterAdv::RouteInfoOption ExitNow(); } - omrPrefix.mIsOnLinkPrefix = false; - omrPrefix.mPrefix = prefix; - omrPrefix.mValidLifetime = aRio.GetRouteLifetime(); - omrPrefix.mRoutePreference = aRio.GetPreference(); - omrPrefix.mTimeLastUpdate = TimerMilli::GetNow(); + omrPrefix.InitFrom(aRio); - for (ExternalPrefix &externalPrefix : mDiscoveredPrefixes) - { - if (externalPrefix == omrPrefix) - { - existingPrefix = &externalPrefix; - } - } + existingPrefix = mDiscoveredPrefixes.Find(omrPrefix); if (existingPrefix == nullptr) { - if (omrPrefix.mValidLifetime == 0) + if (omrPrefix.GetValidLifetime() == 0) { ExitNow(); } if (!mDiscoveredPrefixes.IsFull()) { - SuccessOrExit(PublishExternalRoute(prefix, omrPrefix.mRoutePreference)); + SuccessOrExit(PublishExternalRoute(prefix, omrPrefix.GetRoutePreference())); existingPrefix = mDiscoveredPrefixes.PushBack(); } else @@ -1371,22 +1312,22 @@ void RoutingManager::InvalidateDiscoveredPrefixes(const Ip6::Prefix *aPrefix, bo for (const ExternalPrefix &prefix : mDiscoveredPrefixes) { bool isAdvertisedLocalOnLinkPrefix = - mIsAdvertisingLocalOnLinkPrefix && prefix.mIsOnLinkPrefix && mLocalOnLinkPrefix == prefix.mPrefix; + mIsAdvertisingLocalOnLinkPrefix && prefix.IsOnLinkPrefix() && mLocalOnLinkPrefix == prefix.GetPrefix(); if ( // Invalidate specified prefix - (aPrefix != nullptr && prefix.mPrefix == *aPrefix && prefix.mIsOnLinkPrefix == aIsOnLinkPrefix) || + (aPrefix != nullptr && prefix.GetPrefix() == *aPrefix && prefix.IsOnLinkPrefix() == aIsOnLinkPrefix) || // Invalidate expired prefix (prefix.GetExpireTime() <= now) || // Invalidate Local OMR prefixes - (!prefix.mIsOnLinkPrefix && - (mAdvertisedOmrPrefixes.Contains(prefix.mPrefix) || NetworkDataContainsOmrPrefix(prefix.mPrefix))) || + (!prefix.IsOnLinkPrefix() && (mAdvertisedOmrPrefixes.Contains(prefix.GetPrefix()) || + NetworkDataContainsOmrPrefix(prefix.GetPrefix()))) || // Remove local on-link prefix if the BR is advertising on-link prefix isAdvertisedLocalOnLinkPrefix) { if (!isAdvertisedLocalOnLinkPrefix) { - UnpublishExternalRoute(prefix.mPrefix); + UnpublishExternalRoute(prefix.GetPrefix()); } } else @@ -1395,7 +1336,7 @@ void RoutingManager::InvalidateDiscoveredPrefixes(const Ip6::Prefix *aPrefix, bo IgnoreError(remainingPrefixes.PushBack(prefix)); - if (prefix.mIsOnLinkPrefix) + if (prefix.IsOnLinkPrefix()) { ++remainingOnLinkPrefixNum; } @@ -1416,7 +1357,7 @@ void RoutingManager::InvalidateAllDiscoveredPrefixes(void) { for (ExternalPrefix &prefix : mDiscoveredPrefixes) { - prefix.mValidLifetime = 0; + prefix.ClearValidLifetime(); } InvalidateDiscoveredPrefixes(); @@ -1500,7 +1441,7 @@ void RoutingManager::ResetDiscoveredPrefixStaleTimer(void) { TimeMilli prefixStaleTime = externalPrefix.GetStaleTime(); - if (externalPrefix.mIsOnLinkPrefix) + if (externalPrefix.IsOnLinkPrefix()) { if (!externalPrefix.IsDeprecated()) { @@ -1536,6 +1477,92 @@ void RoutingManager::ResetDiscoveredPrefixStaleTimer(void) } } +//--------------------------------------------------------------------------------------------------------------------- +// ExtneralPrefix + +void RoutingManager::ExternalPrefix::InitFrom(const RouterAdv::PrefixInfoOption &aPio) +{ + Clear(); + aPio.GetPrefix(mPrefix); + mIsOnLinkPrefix = true; + mValidLifetime = aPio.GetValidLifetime(); + mPreferredLifetime = aPio.GetPreferredLifetime(); + mLastUpdateTime = TimerMilli::GetNow(); +} + +void RoutingManager::ExternalPrefix::InitFrom(const RouterAdv::RouteInfoOption &aRio) +{ + Clear(); + aRio.GetPrefix(mPrefix); + mIsOnLinkPrefix = false; + mValidLifetime = aRio.GetRouteLifetime(); + mRoutePreference = aRio.GetPreference(); + mLastUpdateTime = TimerMilli::GetNow(); +} + +bool RoutingManager::ExternalPrefix::operator==(const ExternalPrefix &aPrefix) const +{ + return mIsOnLinkPrefix == aPrefix.mIsOnLinkPrefix && (mPrefix == aPrefix.mPrefix); +} + +TimeMilli RoutingManager::ExternalPrefix::GetStaleTime(void) const +{ + uint32_t delay = OT_MIN(kRtrAdvStaleTime, mIsOnLinkPrefix ? mPreferredLifetime : mValidLifetime); + + return mLastUpdateTime + TimeMilli::SecToMsec(delay); +} + +bool RoutingManager::ExternalPrefix::IsDeprecated(void) const +{ + OT_ASSERT(mIsOnLinkPrefix); + + return mLastUpdateTime + TimeMilli::SecToMsec(mPreferredLifetime) <= TimerMilli::GetNow(); +} + +void RoutingManager::ExternalPrefix::AdoptValidAndPreferredLiftimesFrom(const ExternalPrefix &aPrefix) +{ + constexpr uint32_t kTwoHoursInSeconds = 2 * 3600; + + // Per RFC 4862 section 5.5.3.e: + // + // 1. If the received Valid Lifetime is greater than 2 hours or + // greater than RemainingLifetime, set the valid lifetime of the + // corresponding address to the advertised Valid Lifetime. + // 2. If RemainingLifetime is less than or equal to 2 hours, ignore + // the Prefix Information option with regards to the valid + // lifetime, unless ... + // 3. Otherwise, reset the valid lifetime of the corresponding + // address to 2 hours. + + if (aPrefix.mValidLifetime > kTwoHoursInSeconds || aPrefix.GetExpireTime() > GetExpireTime()) + { + mValidLifetime = aPrefix.mValidLifetime; + } + else if (GetExpireTime() > TimerMilli::GetNow() + TimeMilli::SecToMsec(kTwoHoursInSeconds)) + { + mValidLifetime = kTwoHoursInSeconds; + } + + mPreferredLifetime = aPrefix.GetPreferredLifetime(); + mLastUpdateTime = aPrefix.GetLastUpdateTime(); +} + +uint32_t RoutingManager::ExternalPrefix::GetPrefixExpireDelay(uint32_t aValidLifetime) +{ + uint32_t delay; + + if (aValidLifetime * static_cast(1000) > Timer::kMaxDelay) + { + delay = Timer::kMaxDelay; + } + else + { + delay = aValidLifetime * 1000; + } + + return delay; +} + } // namespace BorderRouter } // namespace ot diff --git a/src/core/border_router/routing_manager.hpp b/src/core/border_router/routing_manager.hpp index 2ec9e6828..be574d933 100644 --- a/src/core/border_router/routing_manager.hpp +++ b/src/core/border_router/routing_manager.hpp @@ -243,47 +243,39 @@ private: static_assert(kRtrAdvStaleTime >= 1800 && kRtrAdvStaleTime <= kDefaultOnLinkPrefixLifetime, "invalid RA STALE time"); - // This struct represents an external prefix which is - // discovered on the infrastructure interface. - struct ExternalPrefix : public Clearable, public Unequatable + // A prefix discovered from Router Advert msg from infra netif + class ExternalPrefix : private Clearable, public Unequatable { - Ip6::Prefix mPrefix; - uint32_t mValidLifetime; + public: + void InitFrom(const RouterAdv::PrefixInfoOption &aPio); + void InitFrom(const RouterAdv::RouteInfoOption &aRio); + bool IsOnLinkPrefix(void) const { return mIsOnLinkPrefix; } + const Ip6::Prefix &GetPrefix(void) const { return mPrefix; } + const TimeMilli & GetLastUpdateTime(void) const { return mLastUpdateTime; } + uint32_t GetValidLifetime(void) const { return mValidLifetime; } + void ClearValidLifetime(void) { mValidLifetime = 0; } + TimeMilli GetExpireTime(void) const { return mLastUpdateTime + GetPrefixExpireDelay(mValidLifetime); } + TimeMilli GetStaleTime(void) const; + bool operator==(const ExternalPrefix &aPrefix) const; - union - { - // Preferred Lifetime of on-link prefix, available - // only when `mIsOnLinkPrefix` is TRUE. - uint32_t mPreferredLifetime; + // Methods to use when `IsOnLinkPrefix()` + uint32_t GetPreferredLifetime(void) const { return mPreferredLifetime; } + void ClearPreferredLifetime(void) { mPreferredLifetime = 0; } + bool IsDeprecated(void) const; + void AdoptValidAndPreferredLiftimesFrom(const ExternalPrefix &Prefix); - // The preference of this route, available - // only when `mIsOnLinkPrefix` is FALSE. - RoutePreference mRoutePreference; - }; - TimeMilli mTimeLastUpdate; - bool mIsOnLinkPrefix; - - bool operator==(const ExternalPrefix &aPrefix) const - { - return mPrefix == aPrefix.mPrefix && mIsOnLinkPrefix == aPrefix.mIsOnLinkPrefix; - } - - bool IsDeprecated(void) const - { - OT_ASSERT(mIsOnLinkPrefix); - - return mTimeLastUpdate + TimeMilli::SecToMsec(mPreferredLifetime) <= TimerMilli::GetNow(); - } - - TimeMilli GetExpireTime(void) const { return mTimeLastUpdate + GetPrefixExpireDelay(mValidLifetime); } - TimeMilli GetStaleTime(void) const - { - uint32_t delay = OT_MIN(kRtrAdvStaleTime, mIsOnLinkPrefix ? mPreferredLifetime : mValidLifetime); - - return mTimeLastUpdate + TimeMilli::SecToMsec(delay); - } + // Method to use when `!IsOnlinkPrefix()` + RoutePreference GetRoutePreference(void) const { return mRoutePreference; } + private: static uint32_t GetPrefixExpireDelay(uint32_t aValidLifetime); + + Ip6::Prefix mPrefix; + TimeMilli mLastUpdateTime; + uint32_t mValidLifetime; + uint32_t mPreferredLifetime; // Applicable when prefix is on-link. + RoutePreference mRoutePreference; // Applicable when prefix is not on-link + bool mIsOnLinkPrefix; }; typedef Array OmrPrefixArray; From 349041bc61e899acdccefea455c3401c05e1318b Mon Sep 17 00:00:00 2001 From: Abtin Keshavarzian Date: Mon, 23 May 2022 16:53:54 -0700 Subject: [PATCH 30/80] [routing-manager] simplify `EvaluateOnLinkPrefix()` and sending RA (#7726) This commit changes `EvaluateOnLinkPrefix()` to directly update `mIsAdvertisingLocalOnLinkPrefix`. The `SendRouterAdvertisement()` is also simplified to directly use this boolean variable (instead of a pointer to prefix being passed as input parameter) to determine whether or not to include a PIO with the local on-link prefix. --- src/core/border_router/routing_manager.cpp | 69 ++++++++-------------- src/core/border_router/routing_manager.hpp | 4 +- 2 files changed, 28 insertions(+), 45 deletions(-) diff --git a/src/core/border_router/routing_manager.cpp b/src/core/border_router/routing_manager.cpp index 8c2dd58f9..9605a1643 100644 --- a/src/core/border_router/routing_manager.cpp +++ b/src/core/border_router/routing_manager.cpp @@ -276,10 +276,9 @@ void RoutingManager::Stop(void) } #endif // Use empty OMR & on-link prefixes to invalidate possible advertised prefixes. - SendRouterAdvertisement(OmrPrefixArray(), nullptr); + SendRouterAdvertisement(OmrPrefixArray()); mAdvertisedOmrPrefixes.Clear(); - mIsAdvertisingLocalOnLinkPrefix = false; mOnLinkPrefixDeprecateTimer.Stop(); InvalidateAllDiscoveredPrefixes(); @@ -549,14 +548,12 @@ exit: return; } -const Ip6::Prefix *RoutingManager::EvaluateOnLinkPrefix(void) +void RoutingManager::EvaluateOnLinkPrefix(void) { - const Ip6::Prefix *newOnLinkPrefix = nullptr; const Ip6::Prefix *smallestOnLinkPrefix = nullptr; // We don't evaluate on-link prefix if we are doing Router Solicitation. - VerifyOrExit(!IsRouterSolicitationInProgress(), - newOnLinkPrefix = (mIsAdvertisingLocalOnLinkPrefix ? &mLocalOnLinkPrefix : nullptr)); + VerifyOrExit(!IsRouterSolicitationInProgress()); for (const ExternalPrefix &prefix : mDiscoveredPrefixes) { @@ -574,10 +571,18 @@ const Ip6::Prefix *RoutingManager::EvaluateOnLinkPrefix(void) // We start advertising our local on-link prefix if there is no existing one. if (smallestOnLinkPrefix == nullptr) { - if (mIsAdvertisingLocalOnLinkPrefix || + if (!mIsAdvertisingLocalOnLinkPrefix && (PublishExternalRoute(mLocalOnLinkPrefix, NetworkData::kRoutePreferenceMedium) == kErrorNone)) { - newOnLinkPrefix = &mLocalOnLinkPrefix; + mIsAdvertisingLocalOnLinkPrefix = true; + LogInfo("Start advertising on-link prefix %s on %s", mLocalOnLinkPrefix.ToString().AsCString(), + mInfraIf.ToString().AsCString()); + + // Call `InvalidateDiscoveredPrefixes()` after setting + // `mIsAdvertisingLocalOnLinkPrefix` to remove on-link + // prefix in case it was discovered and included in + // `mDiscoveredPrefixes` list earlier. + InvalidateDiscoveredPrefixes(); } mOnLinkPrefixDeprecateTimer.Stop(); @@ -588,11 +593,7 @@ const Ip6::Prefix *RoutingManager::EvaluateOnLinkPrefix(void) // the same smallest on-link prefix and the application-specific prefix is not used. else if (mIsAdvertisingLocalOnLinkPrefix) { - if (mLocalOnLinkPrefix < *smallestOnLinkPrefix) - { - newOnLinkPrefix = &mLocalOnLinkPrefix; - } - else + if (!(mLocalOnLinkPrefix < *smallestOnLinkPrefix)) { LogInfo("EvaluateOnLinkPrefix: There is already smaller on-link prefix %s on %s", smallestOnLinkPrefix->ToString().AsCString(), mInfraIf.ToString().AsCString()); @@ -601,7 +602,7 @@ const Ip6::Prefix *RoutingManager::EvaluateOnLinkPrefix(void) } exit: - return newOnLinkPrefix; + return; } void RoutingManager::HandleOnLinkPrefixDeprecateTimer(Timer &aTimer) @@ -636,6 +637,8 @@ void RoutingManager::DeprecateOnLinkPrefix(void) { OT_ASSERT(mIsAdvertisingLocalOnLinkPrefix); + mIsAdvertisingLocalOnLinkPrefix = false; + LogInfo("Deprecate local on-link prefix %s", mLocalOnLinkPrefix.ToString().AsCString()); mOnLinkPrefixDeprecateTimer.StartAt(mTimeAdvertisedOnLinkPrefix, TimeMilli::SecToMsec(kDefaultOnLinkPrefixLifetime)); @@ -699,20 +702,19 @@ void RoutingManager::EvaluateRoutingPolicy(void) { OT_ASSERT(mIsRunning); - const Ip6::Prefix *newOnLinkPrefix = nullptr; - OmrPrefixArray newOmrPrefixes; + OmrPrefixArray newOmrPrefixes; LogInfo("Evaluating routing policy"); // 0. Evaluate on-link, OMR and NAT64 prefixes. - newOnLinkPrefix = EvaluateOnLinkPrefix(); + EvaluateOnLinkPrefix(); EvaluateOmrPrefix(newOmrPrefixes); #if OPENTHREAD_CONFIG_BORDER_ROUTING_NAT64_ENABLE EvaluateNat64Prefix(); #endif // 1. Send Router Advertisement message if necessary. - SendRouterAdvertisement(newOmrPrefixes, newOnLinkPrefix); + SendRouterAdvertisement(newOmrPrefixes); if (newOmrPrefixes.IsEmpty()) { @@ -737,18 +739,8 @@ void RoutingManager::EvaluateRoutingPolicy(void) StartRoutingPolicyEvaluationDelay(Time::SecToMsec(nextSendDelay)); } - // 3. Update advertised on-link & OMR prefixes information. - { - bool wasAdvertisingLocalOnLinkPrefix = mIsAdvertisingLocalOnLinkPrefix; - - mIsAdvertisingLocalOnLinkPrefix = (newOnLinkPrefix == &mLocalOnLinkPrefix); - if (!wasAdvertisingLocalOnLinkPrefix && mIsAdvertisingLocalOnLinkPrefix) - { - InvalidateDiscoveredPrefixes(); - } - - mAdvertisedOmrPrefixes = newOmrPrefixes; - } + // 3. Update OMR prefixes information. + mAdvertisedOmrPrefixes = newOmrPrefixes; } void RoutingManager::StartRoutingPolicyEvaluationJitter(uint32_t aJitterMilli) @@ -814,9 +806,7 @@ Error RoutingManager::SendRouterSolicitation(void) // This method sends Router Advertisement messages to advertise on-link prefix and route for OMR prefix. // @param[in] aNewOmrPrefixes An array of the new OMR prefixes to be advertised. // Empty array means we should stop advertising OMR prefixes. -// @param[in] aOnLinkPrefix A pointer to the new on-link prefix to be advertised. -// `nullptr` means we should stop advertising on-link prefix. -void RoutingManager::SendRouterAdvertisement(const OmrPrefixArray &aNewOmrPrefixes, const Ip6::Prefix *aNewOnLinkPrefix) +void RoutingManager::SendRouterAdvertisement(const OmrPrefixArray &aNewOmrPrefixes) { uint8_t buffer[kMaxRouterAdvMessageLength]; uint16_t bufferLength = 0; @@ -825,11 +815,10 @@ void RoutingManager::SendRouterAdvertisement(const OmrPrefixArray &aNewOmrPrefix memcpy(buffer, &mRouterAdvMessage, sizeof(mRouterAdvMessage)); bufferLength += sizeof(mRouterAdvMessage); - if (aNewOnLinkPrefix != nullptr) + if (mIsAdvertisingLocalOnLinkPrefix) { RouterAdv::PrefixInfoOption *pio; - OT_ASSERT(aNewOnLinkPrefix == &mLocalOnLinkPrefix); OT_ASSERT(bufferLength + sizeof(RouterAdv::PrefixInfoOption) <= sizeof(buffer)); pio = reinterpret_cast(buffer + bufferLength); @@ -839,18 +828,12 @@ void RoutingManager::SendRouterAdvertisement(const OmrPrefixArray &aNewOmrPrefix pio->SetAutoAddrConfigFlag(); pio->SetValidLifetime(kDefaultOnLinkPrefixLifetime); pio->SetPreferredLifetime(kDefaultOnLinkPrefixLifetime); - pio->SetPrefix(*aNewOnLinkPrefix); + pio->SetPrefix(mLocalOnLinkPrefix); bufferLength += pio->GetSize(); - if (!mIsAdvertisingLocalOnLinkPrefix) - { - LogInfo("Start advertising new on-link prefix %s on %s", aNewOnLinkPrefix->ToString().AsCString(), - mInfraIf.ToString().AsCString()); - } - LogInfo("Send on-link prefix %s in PIO (preferred lifetime = %u seconds, valid lifetime = %u seconds)", - aNewOnLinkPrefix->ToString().AsCString(), pio->GetPreferredLifetime(), pio->GetValidLifetime()); + mLocalOnLinkPrefix.ToString().AsCString(), pio->GetPreferredLifetime(), pio->GetValidLifetime()); mTimeAdvertisedOnLinkPrefix = TimerMilli::GetNow(); } diff --git a/src/core/border_router/routing_manager.hpp b/src/core/border_router/routing_manager.hpp index be574d933..299c3ed51 100644 --- a/src/core/border_router/routing_manager.hpp +++ b/src/core/border_router/routing_manager.hpp @@ -291,7 +291,7 @@ private: void GenerateOmrPrefix(void); void GenerateOnLinkPrefix(void); - const Ip6::Prefix *EvaluateOnLinkPrefix(void); + void EvaluateOnLinkPrefix(void); #if OPENTHREAD_CONFIG_BORDER_ROUTING_NAT64_ENABLE void GenerateNat64Prefix(void); @@ -309,7 +309,7 @@ private: void UnpublishExternalRoute(const Ip6::Prefix &aPrefix); void StartRouterSolicitationDelay(void); Error SendRouterSolicitation(void); - void SendRouterAdvertisement(const OmrPrefixArray &aNewOmrPrefixes, const Ip6::Prefix *aNewOnLinkPrefix); + void SendRouterAdvertisement(const OmrPrefixArray &aNewOmrPrefixes); bool IsRouterSolicitationInProgress(void) const; static void HandleRouterSolicitTimer(Timer &aTimer); From 67c2c04f0e615956d39208df0a6acee08a48a154 Mon Sep 17 00:00:00 2001 From: Zhanglong Xia Date: Wed, 25 May 2022 01:47:30 +0800 Subject: [PATCH 31/80] [log] add region code log (#7740) This commit adds log to record the region code to help developers know which region code is currently in use from log. --- src/lib/spinel/radio_spinel_impl.hpp | 17 ++++++++++++++++- src/posix/platform/radio_url.cpp | 3 ++- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/src/lib/spinel/radio_spinel_impl.hpp b/src/lib/spinel/radio_spinel_impl.hpp index c6c02e288..176e79419 100644 --- a/src/lib/spinel/radio_spinel_impl.hpp +++ b/src/lib/spinel/radio_spinel_impl.hpp @@ -2409,7 +2409,22 @@ exit: template otError RadioSpinel::SetRadioRegion(uint16_t aRegionCode) { - return Set(SPINEL_PROP_PHY_REGION_CODE, SPINEL_DATATYPE_UINT16_S, aRegionCode); + otError error; + + error = Set(SPINEL_PROP_PHY_REGION_CODE, SPINEL_DATATYPE_UINT16_S, aRegionCode); + + if (error == OT_ERROR_NONE) + { + otLogNotePlat("Set region code \"%c%c\" successfully", static_cast(aRegionCode >> 8), + static_cast(aRegionCode)); + } + else + { + otLogWarnPlat("Failed to set region code \"%c%c\": %s", static_cast(aRegionCode >> 8), + static_cast(aRegionCode), otThreadErrorToString(error)); + } + + return error; } template diff --git a/src/posix/platform/radio_url.cpp b/src/posix/platform/radio_url.cpp index 54e9be0a2..c126663b9 100644 --- a/src/posix/platform/radio_url.cpp +++ b/src/posix/platform/radio_url.cpp @@ -89,7 +89,8 @@ const char *otSysGetRadioUrlHelpString(void) #endif return "RadioURL:\n" OT_RADIO_URL_HELP_BUS OT_RADIO_URL_HELP_MAX_POWER_TABLE - " region[=region-code] Set the radio's region code.\n" + " region[=region-code] Set the radio's region code. The region code must be an\n" + " ISO 3166 alpha-2 code.\n" " cca-threshold[=dbm] Set the radio's CCA ED threshold in dBm measured at antenna connector.\n" " enable-coex[=1|0] If not specified, RCP coex operates with its default configuration.\n" " Disable coex with 0, and enable it with other values.\n" From 8210a02400c145eb862b65e32040ed0415ba36c2 Mon Sep 17 00:00:00 2001 From: whd <7058128+superwhd@users.noreply.github.com> Date: Wed, 25 May 2022 12:14:58 +0800 Subject: [PATCH 32/80] [dnssd-server] add `otDnssdGetCounters` for telemetry (#7708) This commit implements `otDnssdGetCounters` to report the statistics of the DNS-SD server. --- include/openthread/dnssd_server.h | 26 ++++++++++++++++ include/openthread/instance.h | 2 +- src/core/api/dns_server_api.cpp | 5 ++++ src/core/net/dnssd_server.cpp | 50 +++++++++++++++++++++++++++---- src/core/net/dnssd_server.hpp | 27 +++++++++++++++-- 5 files changed, 100 insertions(+), 10 deletions(-) diff --git a/include/openthread/dnssd_server.h b/include/openthread/dnssd_server.h index abcac362e..73666981a 100644 --- a/include/openthread/dnssd_server.h +++ b/include/openthread/dnssd_server.h @@ -147,6 +147,22 @@ typedef enum OT_DNSSD_QUERY_TYPE_RESOLVE_HOST = 3, ///< Service type resolve hostname. } otDnssdQueryType; +/** + * This structure contains the counters of DNS-SD server. + * + */ +typedef struct otDnssdCounters +{ + uint32_t mSuccessResponse; ///< The number of successful responses + uint32_t mServerFailureResponse; ///< The number of server failure responses + uint32_t mFormatErrorResponse; ///< The number of format error responses + uint32_t mNameErrorResponse; ///< The number of name error responses + uint32_t mNotImplementedResponse; ///< The number of 'not implemented' responses + uint32_t mOtherResponse; ///< The number of other responses + + uint32_t mResolvedBySrp; ///< The number of queries completely resolved by the local SRP server +} otDnssdCounters; + /** * This function sets DNS-SD server query callbacks. * @@ -219,6 +235,16 @@ const otDnssdQuery *otDnssdGetNextQuery(otInstance *aInstance, const otDnssdQuer */ otDnssdQueryType otDnssdGetQueryTypeAndName(const otDnssdQuery *aQuery, char (*aNameOutput)[OT_DNS_MAX_NAME_SIZE]); +/** + * This function returns the counters of the DNS-SD server. + * + * @param[in] aInstance The OpenThread instance structure. + * + * @returns A pointer to the counters of the DNS-SD server. + * + */ +const otDnssdCounters *otDnssdGetCounters(otInstance *aInstance); + /** * @} * diff --git a/include/openthread/instance.h b/include/openthread/instance.h index c946217c5..1148de43d 100644 --- a/include/openthread/instance.h +++ b/include/openthread/instance.h @@ -53,7 +53,7 @@ extern "C" { * @note This number versions both OpenThread platform and user APIs. * */ -#define OPENTHREAD_API_VERSION (211) +#define OPENTHREAD_API_VERSION (212) /** * @addtogroup api-instance diff --git a/src/core/api/dns_server_api.cpp b/src/core/api/dns_server_api.cpp index b9e020cf6..5b094487d 100644 --- a/src/core/api/dns_server_api.cpp +++ b/src/core/api/dns_server_api.cpp @@ -81,4 +81,9 @@ otDnssdQueryType otDnssdGetQueryTypeAndName(const otDnssdQuery *aQuery, char (*a return MapEnum(Dns::ServiceDiscovery::Server::GetQueryTypeAndName(aQuery, *aNameOutput)); } +const otDnssdCounters *otDnssdGetCounters(otInstance *aInstance) +{ + return &AsCoreType(aInstance).Get().GetCounters(); +} + #endif // OPENTHREAD_CONFIG_DNSSD_SERVER_ENABLE diff --git a/src/core/net/dnssd_server.cpp b/src/core/net/dnssd_server.cpp index 40249ce7b..c996c153a 100644 --- a/src/core/net/dnssd_server.cpp +++ b/src/core/net/dnssd_server.cpp @@ -65,6 +65,7 @@ Server::Server(Instance &aInstance) , mQueryUnsubscribe(nullptr) , mTimer(aInstance, Server::HandleTimer) { + mCounters.Clear(); } Error Server::Start(void) @@ -173,11 +174,19 @@ void Server::ProcessQuery(const Header &aRequestHeader, Message &aRequestMessage #endif // Resolve the question using query callbacks if SRP server failed to resolve the questions. - if (responseHeader.GetAnswerCount() == 0 && - kErrorNone == ResolveByQueryCallbacks(responseHeader, *responseMessage, compressInfo, aMessageInfo)) + if (responseHeader.GetAnswerCount() == 0) { - resolveByQueryCallbacks = true; + if (kErrorNone == ResolveByQueryCallbacks(responseHeader, *responseMessage, compressInfo, aMessageInfo)) + { + resolveByQueryCallbacks = true; + } } +#if OPENTHREAD_CONFIG_SRP_SERVER_ENABLE + else + { + ++mCounters.mResolvedBySrp; + } +#endif exit: if (error == kErrorNone && !resolveByQueryCallbacks) @@ -220,6 +229,8 @@ void Server::SendResponse(Header aHeader, { LogInfo("send DNS-SD reply: %s, RCODE=%d", ErrorToString(error), aResponseCode); } + + UpdateResponseCounters(aResponseCode); } Header::Response Server::AddQuestions(const Header & aRequestHeader, @@ -842,7 +853,7 @@ Server::QueryTransaction *Server::NewQuery(const Header & aResponseHead continue; } - query.Init(aResponseHeader, aResponseMessage, aCompressInfo, aMessageInfo); + query.Init(aResponseHeader, aResponseMessage, aCompressInfo, aMessageInfo, GetInstance()); ExitNow(newQuery = &query); } @@ -1194,10 +1205,12 @@ void Server::FinalizeQuery(QueryTransaction &aQuery, Header::Response aResponseC void Server::QueryTransaction::Init(const Header & aResponseHeader, Message & aResponseMessage, const NameCompressInfo &aCompressInfo, - const Ip6::MessageInfo &aMessageInfo) + const Ip6::MessageInfo &aMessageInfo, + Instance & aInstance) { OT_ASSERT(mResponseMessage == nullptr); + InstanceLocatorInit::Init(aInstance); mResponseHeader = aResponseHeader; mResponseMessage = &aResponseMessage; mCompressInfo = aCompressInfo; @@ -1209,10 +1222,35 @@ void Server::QueryTransaction::Finalize(Header::Response aResponseMessage, Ip6:: { OT_ASSERT(mResponseMessage != nullptr); - SendResponse(mResponseHeader, aResponseMessage, *mResponseMessage, mMessageInfo, aSocket); + Get().SendResponse(mResponseHeader, aResponseMessage, *mResponseMessage, mMessageInfo, aSocket); mResponseMessage = nullptr; } +void Server::UpdateResponseCounters(Header::Response aResponseCode) +{ + switch (aResponseCode) + { + case UpdateHeader::kResponseSuccess: + ++mCounters.mSuccessResponse; + break; + case UpdateHeader::kResponseServerFailure: + ++mCounters.mServerFailureResponse; + break; + case UpdateHeader::kResponseFormatError: + ++mCounters.mFormatErrorResponse; + break; + case UpdateHeader::kResponseNameError: + ++mCounters.mNameErrorResponse; + break; + case UpdateHeader::kResponseNotImplemented: + ++mCounters.mNotImplementedResponse; + break; + default: + ++mCounters.mOtherResponse; + break; + } +} + } // namespace ServiceDiscovery } // namespace Dns } // namespace ot diff --git a/src/core/net/dnssd_server.hpp b/src/core/net/dnssd_server.hpp index 2bab3b07e..de16c424e 100644 --- a/src/core/net/dnssd_server.hpp +++ b/src/core/net/dnssd_server.hpp @@ -67,6 +67,14 @@ class Server : public InstanceLocator, private NonCopyable friend class Srp::Server; public: + /** + * This class contains the counters of the DNS-SD server. + * + */ + class Counters : public otDnssdCounters, public Clearable + { + }; + /** * This enumeration specifies a DNS-SD query type. * @@ -155,6 +163,14 @@ public: */ static DnsQueryType GetQueryTypeAndName(const otDnssdQuery *aQuery, char (&aName)[Name::kMaxNameSize]); + /** + * This method returns the counters of the DNS-SD server. + * + * @returns A reference to the `Counters` instance. + * + */ + const Counters &GetCounters(void) const { return mCounters; }; + private: class NameCompressInfo : public Clearable { @@ -273,7 +289,7 @@ private: * This class contains the compress information for a dns packet. * */ - class QueryTransaction + class QueryTransaction : public InstanceLocatorInit { public: explicit QueryTransaction(void) @@ -284,7 +300,8 @@ private: void Init(const Header & aResponseHeader, Message & aResponseMessage, const NameCompressInfo &aCompressInfo, - const Ip6::MessageInfo &aMessageInfo); + const Ip6::MessageInfo &aMessageInfo, + Instance & aInstance); bool IsValid(void) const { return mResponseMessage != nullptr; } const Ip6::MessageInfo &GetMessageInfo(void) const { return mMessageInfo; } const Header & GetResponseHeader(void) const { return mResponseHeader; } @@ -347,7 +364,7 @@ private: static void IncResourceRecordCount(Header &aHeader, bool aAdditional); static Error FindNameComponents(const char *aName, const char *aDomain, NameComponentsOffsetInfo &aInfo); static Error FindPreviousLabel(const char *aName, uint8_t &aStart, uint8_t &aStop); - static void SendResponse(Header aHeader, + void SendResponse(Header aHeader, Header::Response aResponseCode, Message & aMessage, const Ip6::MessageInfo &aMessageInfo, @@ -392,6 +409,8 @@ private: void HandleTimer(void); void ResetTimer(void); + void UpdateResponseCounters(Header::Response aResponseCode); + static const char kDnssdProtocolUdp[]; static const char kDnssdProtocolTcp[]; static const char kDnssdSubTypeLabel[]; @@ -403,6 +422,8 @@ private: otDnssdQuerySubscribeCallback mQuerySubscribe; otDnssdQueryUnsubscribeCallback mQueryUnsubscribe; TimerMilli mTimer; + + Counters mCounters; }; } // namespace ServiceDiscovery From 57bfa92d46111098a1dbabb5a63eb01c8b172ff1 Mon Sep 17 00:00:00 2001 From: Eduardo Montoya Date: Wed, 25 May 2022 20:15:59 +0200 Subject: [PATCH 33/80] [srp-client] prefer the numerically lowest server address (#7737) --- src/core/net/srp_client.cpp | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/src/core/net/srp_client.cpp b/src/core/net/srp_client.cpp index f9fad2e4b..e148d659a 100644 --- a/src/core/net/srp_client.cpp +++ b/src/core/net/srp_client.cpp @@ -1783,7 +1783,6 @@ Error Client::SelectUnicastEntry(DnsSrpUnicast::Origin aOrigin, DnsSrpUnicast::I Error error = kErrorNotFound; DnsSrpUnicast::Info unicastInfo; NetworkData::Service::Manager::Iterator iterator; - uint16_t numServers = 0; #if OPENTHREAD_CONFIG_SRP_CLIENT_SAVE_SELECTED_SERVER_ENABLE Settings::SrpClientInfo savedInfo; bool hasSavedServerInfo = false; @@ -1820,16 +1819,10 @@ Error Client::SelectUnicastEntry(DnsSrpUnicast::Origin aOrigin, DnsSrpUnicast::I ExitNow(); } #endif - numServers++; - // Choose a server randomly (with uniform distribution) from - // the list of servers. As we iterate through server entries, - // with probability `1/numServers`, we choose to switch the - // current selected server with the new entry. This approach - // results in a uniform/same probability of selection among - // all server entries. + // Prefer the numerically lowest server address - if ((numServers == 1) || (Random::NonCrypto::GetUint16InRange(0, numServers) == 0)) + if ((error == kErrorNotFound) || (unicastInfo.mSockAddr.GetAddress() < aInfo.mSockAddr.GetAddress())) { aInfo = unicastInfo; error = kErrorNone; From 61ecd9053158ef61b3afca75a8606e237f8cac8f Mon Sep 17 00:00:00 2001 From: Abtin Keshavarzian Date: Wed, 25 May 2022 11:23:09 -0700 Subject: [PATCH 34/80] [routing-manager] simplify invalidation of specific prefix (#7747) This commit simplifies the mechanism to invalidate/remove a specific prefix entry in `mDiscoveredPrefixes` list. Instead of passing the prefix to invalidate as an input to `InvalidateDiscoveredPrefixes()` method, we directly set the valid lifetime to zero on the entry in the list before calling the `InvalidateDiscoveredPrefixes()` to then remove all expired entries. This change addresses an issue in `HandleRouterSolicitTimer()` where while iterating over the `mDiscoveredPrefixes` list we could call `InvalidateDiscoveredPrefixes()` which then removed an entry from the list (thus changing the list). --- src/core/border_router/routing_manager.cpp | 26 +++++++++++----------- src/core/border_router/routing_manager.hpp | 2 +- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/src/core/border_router/routing_manager.cpp b/src/core/border_router/routing_manager.cpp index 9605a1643..de636a02b 100644 --- a/src/core/border_router/routing_manager.cpp +++ b/src/core/border_router/routing_manager.cpp @@ -1015,11 +1015,13 @@ void RoutingManager::HandleRouterSolicitTimer(void) } else { - InvalidateDiscoveredPrefixes(&prefix.GetPrefix(), prefix.IsOnLinkPrefix()); + prefix.ClearValidLifetime(); } } } + InvalidateDiscoveredPrefixes(); + // Invalidate the learned RA message if it is not refreshed during Router Solicitation. if (mTimeRouterAdvMessageLastUpdate <= mTimeRouterSolicitStart) { @@ -1246,23 +1248,23 @@ void RoutingManager::UpdateDiscoveredOmrPrefix(const RouterAdv::RouteInfoOption LogInfo("Discovered OMR prefix (%s, %u seconds) from %s", prefix.ToString().AsCString(), aRio.GetRouteLifetime(), mInfraIf.ToString().AsCString()); - if (aRio.GetRouteLifetime() == 0) - { - InvalidateDiscoveredPrefixes(&prefix, /* aIsOnLinkPrefix */ false); - ExitNow(); - } - omrPrefix.InitFrom(aRio); existingPrefix = mDiscoveredPrefixes.Find(omrPrefix); - if (existingPrefix == nullptr) + if (omrPrefix.GetValidLifetime() == 0) { - if (omrPrefix.GetValidLifetime() == 0) + if (existingPrefix != nullptr) { - ExitNow(); + existingPrefix->ClearValidLifetime(); + InvalidateDiscoveredPrefixes(); } + ExitNow(); + } + + if (existingPrefix == nullptr) + { if (!mDiscoveredPrefixes.IsFull()) { SuccessOrExit(PublishExternalRoute(prefix, omrPrefix.GetRoutePreference())); @@ -1284,7 +1286,7 @@ exit: return; } -void RoutingManager::InvalidateDiscoveredPrefixes(const Ip6::Prefix *aPrefix, bool aIsOnLinkPrefix) +void RoutingManager::InvalidateDiscoveredPrefixes(void) { TimeMilli now = TimerMilli::GetNow(); uint8_t remainingOnLinkPrefixNum = 0; @@ -1298,8 +1300,6 @@ void RoutingManager::InvalidateDiscoveredPrefixes(const Ip6::Prefix *aPrefix, bo mIsAdvertisingLocalOnLinkPrefix && prefix.IsOnLinkPrefix() && mLocalOnLinkPrefix == prefix.GetPrefix(); if ( - // Invalidate specified prefix - (aPrefix != nullptr && prefix.GetPrefix() == *aPrefix && prefix.IsOnLinkPrefix() == aIsOnLinkPrefix) || // Invalidate expired prefix (prefix.GetExpireTime() <= now) || // Invalidate Local OMR prefixes diff --git a/src/core/border_router/routing_manager.hpp b/src/core/border_router/routing_manager.hpp index 299c3ed51..b4b501460 100644 --- a/src/core/border_router/routing_manager.hpp +++ b/src/core/border_router/routing_manager.hpp @@ -327,7 +327,7 @@ private: void HandleRouterAdvertisement(const InfraIf::Icmp6Packet &aPacket, const Ip6::Address &aSrcAddress); bool UpdateDiscoveredOnLinkPrefix(const RouterAdv::PrefixInfoOption &aPio); void UpdateDiscoveredOmrPrefix(const RouterAdv::RouteInfoOption &aRio); - void InvalidateDiscoveredPrefixes(const Ip6::Prefix *aPrefix = nullptr, bool aIsOnLinkPrefix = true); + void InvalidateDiscoveredPrefixes(void); void InvalidateAllDiscoveredPrefixes(void); bool NetworkDataContainsOmrPrefix(const Ip6::Prefix &aPrefix) const; bool UpdateRouterAdvMessage(const RouterAdv::RouterAdvMessage *aRouterAdvMessage); From d266f90fbf0905d1e82eb707f596bb778cddcbbe Mon Sep 17 00:00:00 2001 From: Simon Lin Date: Thu, 26 May 2022 23:57:42 +0800 Subject: [PATCH 35/80] [tests] `mcast6.py` to receive UDP messages without `JOIN_GROUP` (#7757) `mcast6.py` is used by GitHub Actions CI to send or receive UDP messages in multicast groups. This commit adds a `-u` argument to allow `mcast6.py` to receive UDP messages without joining a multicast group. --- tests/scripts/thread-cert/mcast6.py | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/tests/scripts/thread-cert/mcast6.py b/tests/scripts/thread-cert/mcast6.py index 27548bc21..b090fd95f 100755 --- a/tests/scripts/thread-cert/mcast6.py +++ b/tests/scripts/thread-cert/mcast6.py @@ -68,13 +68,18 @@ def main(): if args[0] == '-s': is_sender = True args.pop(0) + elif args[0] == '-u': + is_multicast_receiver = False + args.pop(0) + else: + is_multicast_receiver = True ifname, group = args if is_sender: sender(ifname, group) else: - receiver(ifname, group) + receiver(ifname, group, is_multicast_receiver=is_multicast_receiver) def sender(ifname, group): @@ -94,7 +99,7 @@ def sender(ifname, group): time.sleep(1) -def receiver(ifname, group): +def receiver(ifname, group, is_multicast_receiver=True): # Look up multicast group address in name server and find out IP version addrinfo = socket.getaddrinfo(group, None)[0] assert addrinfo[0] == socket.AF_INET6 @@ -110,11 +115,12 @@ def receiver(ifname, group): # Bind it to the port s.bind(('', MYPORT)) - group_bin = socket.inet_pton(addrinfo[0], addrinfo[4][0]) - # Join group - interface_index = if_nametoindex(ifname) - mreq = group_bin + struct.pack('@I', interface_index) - s.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_JOIN_GROUP, mreq) + if is_multicast_receiver: + group_bin = socket.inet_pton(addrinfo[0], addrinfo[4][0]) + # Join group + interface_index = if_nametoindex(ifname) + mreq = group_bin + struct.pack('@I', interface_index) + s.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_JOIN_GROUP, mreq) # Loop, printing any data we receive while True: From cbb51b01ceca1cd99f72d8ed6dbfc81566d4185a Mon Sep 17 00:00:00 2001 From: Jonathan Hui Date: Thu, 26 May 2022 09:07:41 -0700 Subject: [PATCH 36/80] [neighbor] change `IsThreadVersion1p2()` to include higher versions (#7755) --- src/core/thread/link_metrics.cpp | 8 ++++---- src/core/thread/topology.hpp | 12 ++++++------ 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/core/thread/link_metrics.cpp b/src/core/thread/link_metrics.cpp index c4265f605..c9c917bdd 100644 --- a/src/core/thread/link_metrics.cpp +++ b/src/core/thread/link_metrics.cpp @@ -117,7 +117,7 @@ Error LinkMetrics::Query(const Ip6::Address &aDestination, uint8_t aSeriesId, co Neighbor * neighbor = GetNeighborFromLinkLocalAddr(aDestination); VerifyOrExit(neighbor != nullptr, error = kErrorUnknownNeighbor); - VerifyOrExit(neighbor->IsThreadVersion1p2(), error = kErrorNotCapable); + VerifyOrExit(neighbor->IsThreadVersion1p2OrHigher(), error = kErrorNotCapable); if (aMetrics != nullptr) { @@ -150,7 +150,7 @@ Error LinkMetrics::SendMgmtRequestForwardTrackingSeries(const Ip6::Address & Neighbor * neighbor = GetNeighborFromLinkLocalAddr(aDestination); VerifyOrExit(neighbor != nullptr, error = kErrorUnknownNeighbor); - VerifyOrExit(neighbor->IsThreadVersion1p2(), error = kErrorNotCapable); + VerifyOrExit(neighbor->IsThreadVersion1p2OrHigher(), error = kErrorNotCapable); // Directly transform `aMetrics` into TypeIdFlags and put them into `subTlvs` if (aMetrics != nullptr) @@ -187,7 +187,7 @@ Error LinkMetrics::SendMgmtRequestEnhAckProbing(const Ip6::Address &aDestination Neighbor * neighbor = GetNeighborFromLinkLocalAddr(aDestination); VerifyOrExit(neighbor != nullptr, error = kErrorUnknownNeighbor); - VerifyOrExit(neighbor->IsThreadVersion1p2(), error = kErrorNotCapable); + VerifyOrExit(neighbor->IsThreadVersion1p2OrHigher(), error = kErrorNotCapable); if (aEnhAckFlags == kEnhAckClear) { @@ -227,7 +227,7 @@ Error LinkMetrics::SendLinkProbe(const Ip6::Address &aDestination, uint8_t aSeri Neighbor *neighbor = GetNeighborFromLinkLocalAddr(aDestination); VerifyOrExit(neighbor != nullptr, error = kErrorUnknownNeighbor); - VerifyOrExit(neighbor->IsThreadVersion1p2(), error = kErrorNotCapable); + VerifyOrExit(neighbor->IsThreadVersion1p2OrHigher(), error = kErrorNotCapable); VerifyOrExit(aLength <= LinkMetrics::kLinkProbeMaxLen && aSeriesId != kQueryIdSingleProbe && aSeriesId != kSeriesIdAllSeries, diff --git a/src/core/thread/topology.hpp b/src/core/thread/topology.hpp index 5d476825b..778356564 100644 --- a/src/core/thread/topology.hpp +++ b/src/core/thread/topology.hpp @@ -552,20 +552,20 @@ public: #endif // OPENTHREAD_CONFIG_MULTI_RADIO /** - * This method indicates whether or not it is a valid Thread 1.1 neighbor. + * This method indicates whether or not it is Thread 1.1. * - * @returns TRUE if it is a valid Thread 1.1 neighbor, FALSE otherwise. + * @returns TRUE if neighbors is Thread 1.1, FALSE otherwise. * */ bool IsThreadVersion1p1(void) const { return mState != kStateInvalid && mVersion == OT_THREAD_VERSION_1_1; } /** - * This method indicates whether or not it is a valid Thread 1.2 neighbor. + * This method indicates whether or not neighbor is Thread 1.2 or higher.. * - * @returns TRUE if it is a valid Thread 1.2 neighbor, FALSE otherwise. + * @returns TRUE if neighbor is Thread 1.2 or higher, FALSE otherwise. * */ - bool IsThreadVersion1p2(void) const { return mState != kStateInvalid && mVersion == OT_THREAD_VERSION_1_2; } + bool IsThreadVersion1p2OrHigher(void) const { return mState != kStateInvalid && mVersion >= OT_THREAD_VERSION_1_2; } /** * This method indicates whether Thread version supports CSL. @@ -573,7 +573,7 @@ public: * @returns TRUE if CSL is supported, FALSE otherwise. * */ - bool IsThreadVersionCslCapable(void) const { return IsThreadVersion1p2() && !IsRxOnWhenIdle(); } + bool IsThreadVersionCslCapable(void) const { return IsThreadVersion1p2OrHigher() && !IsRxOnWhenIdle(); } /** * This method indicates whether Enhanced Keep-Alive is supported or not. From f75d03f6cbd77b3d9ca0a630934114f12560b3fc Mon Sep 17 00:00:00 2001 From: Jonathan Hui Date: Thu, 26 May 2022 09:16:49 -0700 Subject: [PATCH 37/80] [mle] delay sending multicast Link Request (#7745) A device sends a multicast Link Request message after becoming a router to quickly and efficiently establish links with neighboring routers. However, sending a Link Request too quickly can fail in cases where the neighboring router(s) have not yet received the updated Router ID set from the Leader. This commit delays sending the Link Request to allow time for neighboring routers to receive the updated Router ID from the Leader. --- src/core/thread/mle_router.cpp | 26 ++++++- src/core/thread/mle_router.hpp | 2 + src/core/thread/mle_types.hpp | 1 + .../thread-cert/Cert_5_2_04_REEDUpgrade.py | 70 ++++++++++--------- .../thread-cert/v1_2_test_parent_selection.py | 4 +- tools/otci/tests/test_otci.py | 4 +- 6 files changed, 66 insertions(+), 41 deletions(-) diff --git a/src/core/thread/mle_router.cpp b/src/core/thread/mle_router.cpp index 86335e05f..ccd167687 100644 --- a/src/core/thread/mle_router.cpp +++ b/src/core/thread/mle_router.cpp @@ -85,6 +85,7 @@ MleRouter::MleRouter(Instance &aInstance) , mPreviousPartitionIdTimeout(0) , mRouterSelectionJitter(kRouterSelectionJitter) , mRouterSelectionJitterTimeout(0) + , mLinkRequestDelay(0) , mParentPriority(kParentPriorityUnspecified) #if OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE , mBackboneRouterRegistrationDelay(0) @@ -196,6 +197,7 @@ Error MleRouter::BecomeRouter(ThreadStatusTlv::Status aStatus) Get().SetRxOnWhenIdle(true); mRouterSelectionJitterTimeout = 0; + mLinkRequestDelay = 0; switch (mRole) { @@ -293,7 +295,7 @@ void MleRouter::HandleDetachStart(void) void MleRouter::HandleChildStart(AttachMode aMode) { - // reset `rejected` flag whenever REED becomes child. + mLinkRequestDelay = 0; mAddressSolicitRejected = false; mRouterSelectionJitterTimeout = 1 + Random::NonCrypto::GetUint8InRange(0, mRouterSelectionJitter); @@ -496,6 +498,11 @@ void MleRouter::SendAdvertisement(void) // children to detach. VerifyOrExit(!mAddressSolicitPending); + // Suppress MLE Advertisements before sending multicast Link Request. + // + // Before sending the multicast Link Request message, no links have been established to neighboring routers. + VerifyOrExit(mLinkRequestDelay == 0); + VerifyOrExit((message = NewMleMessage(kCommandAdvertisement)) != nullptr, error = kErrorNoBufs); SuccessOrExit(error = message->AppendSourceAddressTlv()); SuccessOrExit(error = message->AppendLeaderDataTlv()); @@ -535,6 +542,8 @@ Error MleRouter::SendLinkRequest(Neighbor *aNeighbor) TxMessage * message; Ip6::Address destination; + VerifyOrExit(mLinkRequestDelay == 0 && mChallengeTimeout == 0); + destination.Clear(); VerifyOrExit((message = NewMleMessage(kCommandLinkRequest)) != nullptr, error = kErrorNoBufs); @@ -1422,6 +1431,12 @@ Error MleRouter::HandleAdvertisement(RxInfo &aRxInfo) ExitNow(); case kRoleRouter: + if (mLinkRequestDelay > 0 && route.IsRouterIdSet(mRouterId)) + { + mLinkRequestDelay = 0; + IgnoreError(SendLinkRequest(nullptr)); + } + router = mRouterTable.GetRouter(routerId); VerifyOrExit(router != nullptr); @@ -1755,6 +1770,11 @@ void MleRouter::HandleTimeTick(void) VerifyOrExit(IsFullThreadDevice(), Get().UnregisterReceiver(TimeTicker::kMleRouter)); + if (mLinkRequestDelay > 0 && --mLinkRequestDelay == 0) + { + IgnoreError(SendLinkRequest(nullptr)); + } + if (mChallengeTimeout > 0) { mChallengeTimeout--; @@ -3783,13 +3803,13 @@ void MleRouter::HandleAddressSolicitResponse(Coap::Message * aMessage, leader->SetNextHop(RouterIdFromRloc16(mParent.GetRloc16())); } - IgnoreError(SendLinkRequest(nullptr)); - for (Child &child : Get().Iterate(Child::kInStateChildIdRequest)) { IgnoreError(SendChildIdResponse(child)); } + mLinkRequestDelay = kMulticastLinkRequestDelay; + exit: // Send announce after received address solicit reply if needed InformPreviousChannel(); diff --git a/src/core/thread/mle_router.hpp b/src/core/thread/mle_router.hpp index fc9434a4b..0ae422ed2 100644 --- a/src/core/thread/mle_router.hpp +++ b/src/core/thread/mle_router.hpp @@ -695,6 +695,8 @@ private: uint8_t mRouterSelectionJitter; ///< The variable to save the assigned jitter value. uint8_t mRouterSelectionJitterTimeout; ///< The Timeout prior to request/release Router ID. + uint8_t mLinkRequestDelay; + int8_t mParentPriority; ///< The assigned parent priority value, -2 means not assigned. #if OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE uint8_t mBackboneRouterRegistrationDelay; ///< Delay before registering Backbone Router service. diff --git a/src/core/thread/mle_types.hpp b/src/core/thread/mle_types.hpp index 29fef937f..ad987dbff 100644 --- a/src/core/thread/mle_types.hpp +++ b/src/core/thread/mle_types.hpp @@ -100,6 +100,7 @@ constexpr uint32_t kMaxResponseDelay = 1000; ///< Max response del constexpr uint32_t kMaxChildIdRequestTimeout = 5000; ///< Max delay to rx a Child ID Request (in msec) constexpr uint32_t kMaxChildUpdateResponseTimeout = 2000; ///< Max delay to rx a Child Update Response (in msec) constexpr uint32_t kMaxLinkRequestTimeout = 2000; ///< Max delay to rx a Link Accept +constexpr uint8_t kMulticastLinkRequestDelay = 5; ///< Max delay for sending a mcast Link Request (in sec) constexpr uint32_t kMinTimeoutKeepAlive = (((kMaxChildKeepAliveAttempts + 1) * kUnicastRetransmissionDelay) / 1000); constexpr uint32_t kMinPollPeriod = OPENTHREAD_CONFIG_MAC_MINIMUM_POLL_PERIOD; diff --git a/tests/scripts/thread-cert/Cert_5_2_04_REEDUpgrade.py b/tests/scripts/thread-cert/Cert_5_2_04_REEDUpgrade.py index e5c9b1449..e97bb72ce 100755 --- a/tests/scripts/thread-cert/Cert_5_2_04_REEDUpgrade.py +++ b/tests/scripts/thread-cert/Cert_5_2_04_REEDUpgrade.py @@ -187,7 +187,7 @@ class Cert_5_2_4_REEDUpgrade(thread_cert.TestCase): self.simulator.go(REED_ADVERTISEMENT_INTERVAL + REED_ADVERTISEMENT_MAX_JITTER) self.nodes[MED].start() - self.simulator.go(5) + self.simulator.go(config.ROUTER_STARTUP_DELAY) self.collect_ipaddrs() mleid = self.nodes[LEADER].get_ip6_address(config.ADDRESS_TYPE.ML_EID) @@ -352,43 +352,45 @@ class Cert_5_2_4_REEDUpgrade(thread_cert.TestCase): # - TLV Request TLV: Link Margin # - Version TLV - pkts.filter_wpan_src64(REED).\ - filter_LLARMA().\ - filter_mle_cmd(MLE_LINK_REQUEST).\ - filter(lambda p: { - CHALLENGE_TLV, - LEADER_DATA_TLV, - SOURCE_ADDRESS_TLV, - VERSION_TLV, - TLV_REQUEST_TLV, - LINK_MARGIN_TLV - } <= set(p.mle.tlv.type) - ).\ - must_next() + with pkts.save_index(): + pkts.filter_wpan_src64(REED).\ + filter_LLARMA().\ + filter_mle_cmd(MLE_LINK_REQUEST).\ + filter(lambda p: { + CHALLENGE_TLV, + LEADER_DATA_TLV, + SOURCE_ADDRESS_TLV, + VERSION_TLV, + TLV_REQUEST_TLV, + LINK_MARGIN_TLV + } <= set(p.mle.tlv.type) + ).\ + must_next() # Step 11: The REED MLE Child ID Response MUST be properly # formatted with MED_1’s new 16-bit address. - pkts.filter_wpan_src64(REED).\ - filter_wpan_dst64(MED).\ - filter_mle_cmd(MLE_CHILD_ID_RESPONSE).\ - filter(lambda p: { - ADDRESS16_TLV, - LEADER_DATA_TLV, - NETWORK_DATA_TLV, - SOURCE_ADDRESS_TLV, - ROUTE64_TLV - } <= set(p.mle.tlv.type) or\ - { - ADDRESS16_TLV, - LEADER_DATA_TLV, - NETWORK_DATA_TLV, - SOURCE_ADDRESS_TLV - } <= set(p.mle.tlv.type) and\ - p.mle.tlv.source_addr != REED_RLOC16 and\ - p.mle.tlv.addr16 != MED_RLOC16 - ).\ - must_next() + with pkts.save_index(): + pkts.filter_wpan_src64(REED).\ + filter_wpan_dst64(MED).\ + filter_mle_cmd(MLE_CHILD_ID_RESPONSE).\ + filter(lambda p: { + ADDRESS16_TLV, + LEADER_DATA_TLV, + NETWORK_DATA_TLV, + SOURCE_ADDRESS_TLV, + ROUTE64_TLV + } <= set(p.mle.tlv.type) or\ + { + ADDRESS16_TLV, + LEADER_DATA_TLV, + NETWORK_DATA_TLV, + SOURCE_ADDRESS_TLV + } <= set(p.mle.tlv.type) and\ + p.mle.tlv.source_addr != REED_RLOC16 and\ + p.mle.tlv.addr16 != MED_RLOC16 + ).\ + must_next() # Step 12: The Leader MUST respond with an ICMPv6 Echo Reply diff --git a/tests/scripts/thread-cert/v1_2_test_parent_selection.py b/tests/scripts/thread-cert/v1_2_test_parent_selection.py index 432f984ba..e94571886 100755 --- a/tests/scripts/thread-cert/v1_2_test_parent_selection.py +++ b/tests/scripts/thread-cert/v1_2_test_parent_selection.py @@ -158,7 +158,7 @@ class TestParentSelection(thread_cert.TestCase): self.nodes[REED_1_2].set_link_quality(self.nodes[ROUTER_1_2].get_addr64(), 2) self.nodes[ROUTER_1_2].set_router_selection_jitter(1) self.nodes[ROUTER_1_2].start() - self.simulator.go(5) + self.simulator.go(config.ROUTER_STARTUP_DELAY) self.assertEqual(self.nodes[ROUTER_1_2].get_state(), 'router') # Check Parent Response @@ -220,7 +220,7 @@ class TestParentSelection(thread_cert.TestCase): # MED_1_1 would attach to LEADER_1_2 self.nodes[REED_1_1].set_state('router') - self.simulator.go(5) + self.simulator.go(config.ROUTER_STARTUP_DELAY) self.assertEqual(self.nodes[REED_1_1].get_state(), 'router') # Flush relative message queues diff --git a/tools/otci/tests/test_otci.py b/tools/otci/tests/test_otci.py index c1eacefb5..286e3363c 100644 --- a/tools/otci/tests/test_otci.py +++ b/tools/otci/tests/test_otci.py @@ -533,7 +533,7 @@ class TestOTCI(unittest.TestCase): node2.joiner_start("TEST123") node2.wait(10, expect_line="Join success") node2.thread_start() - node2.wait(5) + node2.wait(10) assert node2.get_state() == "router" def _test_otci_multi_nodes(self, leader, commissioner, child1, child2): @@ -587,7 +587,7 @@ class TestOTCI(unittest.TestCase): commissioner.set_network_key(TEST_NETWORKKEY) commissioner.thread_start() - commissioner.wait(5) + commissioner.wait(10) self.assertEqual('router', commissioner.get_state()) From 3c4caed8a42e08dc1796d4d7c12e383c8a2527c2 Mon Sep 17 00:00:00 2001 From: Abtin Keshavarzian Date: Thu, 26 May 2022 09:21:13 -0700 Subject: [PATCH 38/80] [routing-manager] remove invalidated prefixes from array in place (#7751) This commit updates `InvalidateDiscoveredPrefixes()` to remove invalidated entries from the array directly. This is done by replacing the invalidated prefix entry in array with the last entry in list and popping the last entry. This helps simplify the code and avoid the need to create a separate copy of the list. This commit also makes other smaller enhancements in this method: Tracking the `nextExpireTime` to avoid multiple calls to `FireAtIfEarlier()` from the same method (avoid re-arranging the full timer linked-list). Also using a boolean `containsOnLinkPrefix` to track whether we have seen any on-link prefix in the discovered prefix list (instead of keeping track of the number of such entries). --- src/core/border_router/routing_manager.cpp | 53 +++++++++++++--------- 1 file changed, 31 insertions(+), 22 deletions(-) diff --git a/src/core/border_router/routing_manager.cpp b/src/core/border_router/routing_manager.cpp index de636a02b..b0712c50c 100644 --- a/src/core/border_router/routing_manager.cpp +++ b/src/core/border_router/routing_manager.cpp @@ -1288,50 +1288,59 @@ exit: void RoutingManager::InvalidateDiscoveredPrefixes(void) { - TimeMilli now = TimerMilli::GetNow(); - uint8_t remainingOnLinkPrefixNum = 0; - ExternalPrefixArray remainingPrefixes; + TimeMilli now = TimerMilli::GetNow(); + TimeMilli nextExpireTime = now.GetDistantFuture(); + bool containsOnLinkPrefix = false; mDiscoveredPrefixInvalidTimer.Stop(); - for (const ExternalPrefix &prefix : mDiscoveredPrefixes) + for (ExternalPrefixArray::IndexType index = 0; index < mDiscoveredPrefixes.GetLength();) { - bool isAdvertisedLocalOnLinkPrefix = + ExternalPrefix &prefix = mDiscoveredPrefixes[index]; + bool isAdvertisedLocalOnLinkPrefix = mIsAdvertisingLocalOnLinkPrefix && prefix.IsOnLinkPrefix() && mLocalOnLinkPrefix == prefix.GetPrefix(); - if ( - // Invalidate expired prefix - (prefix.GetExpireTime() <= now) || - // Invalidate Local OMR prefixes + // We invalidate expired prefixes, or local OMR prefixes + // (either in `mAdvertisedOmrPrefixes` or in Thread Network + // Data), or if the prefix matches the local on-link prefix + // (when local on-link prefix is being advertised). + + if ((prefix.GetExpireTime() <= now) || (!prefix.IsOnLinkPrefix() && (mAdvertisedOmrPrefixes.Contains(prefix.GetPrefix()) || NetworkDataContainsOmrPrefix(prefix.GetPrefix()))) || - // Remove local on-link prefix if the BR is advertising on-link prefix isAdvertisedLocalOnLinkPrefix) { if (!isAdvertisedLocalOnLinkPrefix) { UnpublishExternalRoute(prefix.GetPrefix()); } + + // Remove the prefix from the array by replacing it with + // last entry in the array (we copy the popped last entry + // into `prefix` entry at current `index`). Also in this + // case, the `index` is not incremented. + + prefix = *mDiscoveredPrefixes.PopBack(); } else { - mDiscoveredPrefixInvalidTimer.FireAtIfEarlier(prefix.GetExpireTime()); + nextExpireTime = OT_MIN(nextExpireTime, prefix.GetExpireTime()); + containsOnLinkPrefix |= prefix.IsOnLinkPrefix(); - IgnoreError(remainingPrefixes.PushBack(prefix)); - - if (prefix.IsOnLinkPrefix()) - { - ++remainingOnLinkPrefixNum; - } + index++; } } - mDiscoveredPrefixes = remainingPrefixes; - - if (remainingOnLinkPrefixNum == 0 && !mIsAdvertisingLocalOnLinkPrefix) + if (nextExpireTime != now.GetDistantFuture()) { - // There are no valid on-link prefixes on infra link now, start Router Solicitation - // To discover more on-link prefixes or timeout to advertise my local on-link prefix. + mDiscoveredPrefixInvalidTimer.FireAt(nextExpireTime); + } + + if (!containsOnLinkPrefix && !mIsAdvertisingLocalOnLinkPrefix) + { + // There are no valid on-link prefixes on infra link now, start + // Router Solicitation to discover more on-link prefixes or + // time out to advertise the local on-link prefix. StartRouterSolicitationDelay(); } } From e8b9b874f51b74ce6339f3f963897aee0eb0879f Mon Sep 17 00:00:00 2001 From: hemanth-silabs <50145824+hemanth-silabs@users.noreply.github.com> Date: Thu, 26 May 2022 22:02:07 +0100 Subject: [PATCH 39/80] [mac] re-add API to receive Thread-specific Beacon payload (#7736) Some of the current implementations of thread stack use active scans to find out the joining network name and extended panids. These details are then used as part of commissioning process. So at the very minimum we will need processing the incoming beacons to extract these information. --- include/openthread/instance.h | 2 +- include/openthread/link.h | 4 ++ script/check-simulation-build-autotools | 1 + src/core/config/mac.h | 11 ++++ src/core/mac/mac.cpp | 22 +++++++ src/core/mac/mac_frame.hpp | 87 +++++++++++++++++++++++++ 6 files changed, 126 insertions(+), 1 deletion(-) diff --git a/include/openthread/instance.h b/include/openthread/instance.h index 1148de43d..6bd2e7148 100644 --- a/include/openthread/instance.h +++ b/include/openthread/instance.h @@ -53,7 +53,7 @@ extern "C" { * @note This number versions both OpenThread platform and user APIs. * */ -#define OPENTHREAD_API_VERSION (212) +#define OPENTHREAD_API_VERSION (213) /** * @addtogroup api-instance diff --git a/include/openthread/link.h b/include/openthread/link.h index 7c77e964d..0a7280cee 100644 --- a/include/openthread/link.h +++ b/include/openthread/link.h @@ -391,6 +391,10 @@ typedef struct otActiveScanResult unsigned int mVersion : 4; ///< Version bool mIsNative : 1; ///< Native Commissioner flag bool mDiscover : 1; ///< Result from MLE Discovery + + // Applicable/Required only when beacon payload parsing feature + // (`OPENTHREAD_CONFIG_MAC_BEACON_PAYLOAD_PARSING_ENABLE`) is enabled. + bool mIsJoinable : 1; ///< Joining Permitted flag } otActiveScanResult; /** diff --git a/script/check-simulation-build-autotools b/script/check-simulation-build-autotools index 6b16957ac..c08a266f5 100755 --- a/script/check-simulation-build-autotools +++ b/script/check-simulation-build-autotools @@ -89,6 +89,7 @@ build_all_features() "-DOPENTHREAD_CONFIG_TMF_NETDATA_SERVICE_ENABLE=1" "-DOPENTHREAD_CONFIG_TMF_NETWORK_DIAG_MTD_ENABLE=1" "-DOPENTHREAD_CONFIG_UDP_FORWARD_ENABLE=1" + "-DOPENTHREAD_CONFIG_MAC_BEACON_PAYLOAD_PARSING_ENABLE=1" ) local options_1_2=( diff --git a/src/core/config/mac.h b/src/core/config/mac.h index 6572dd0e8..0af268449 100644 --- a/src/core/config/mac.h +++ b/src/core/config/mac.h @@ -499,4 +499,15 @@ #define OPENTHREAD_CONFIG_MAC_SCAN_DURATION 300 #endif +/** + * @def OPENTHREAD_CONFIG_MAC_BEACON_PAYLOAD_PARSING_ENABLE + * + * This setting configures if the beacon payload parsing needs to be enabled in MAC. This is optional and is disabled by + * default because Thread 1.2.1 has removed support for beacon payloads. + * + */ +#ifndef OPENTHREAD_CONFIG_MAC_BEACON_PAYLOAD_PARSING_ENABLE +#define OPENTHREAD_CONFIG_MAC_BEACON_PAYLOAD_PARSING_ENABLE 0 +#endif + #endif // CONFIG_MAC_H_ diff --git a/src/core/mac/mac.cpp b/src/core/mac/mac.cpp index faaea8094..b42300d83 100644 --- a/src/core/mac/mac.cpp +++ b/src/core/mac/mac.cpp @@ -210,6 +210,11 @@ Error Mac::ConvertBeaconToActiveScanResult(const RxFrame *aBeaconFrame, ActiveSc { Error error = kErrorNone; Address address; +#if OPENTHREAD_CONFIG_MAC_BEACON_PAYLOAD_PARSING_ENABLE + const BeaconPayload *beaconPayload = nullptr; + const Beacon * beacon = nullptr; + uint16_t payloadLength; +#endif memset(&aResult, 0, sizeof(ActiveScanResult)); @@ -229,6 +234,23 @@ Error Mac::ConvertBeaconToActiveScanResult(const RxFrame *aBeaconFrame, ActiveSc aResult.mRssi = aBeaconFrame->GetRssi(); aResult.mLqi = aBeaconFrame->GetLqi(); +#if OPENTHREAD_CONFIG_MAC_BEACON_PAYLOAD_PARSING_ENABLE + payloadLength = aBeaconFrame->GetPayloadLength(); + + beacon = reinterpret_cast(aBeaconFrame->GetPayload()); + beaconPayload = reinterpret_cast(beacon->GetPayload()); + + if ((payloadLength >= (sizeof(*beacon) + sizeof(*beaconPayload))) && beacon->IsValid() && beaconPayload->IsValid()) + { + aResult.mVersion = beaconPayload->GetProtocolVersion(); + aResult.mIsJoinable = beaconPayload->IsJoiningPermitted(); + aResult.mIsNative = beaconPayload->IsNative(); + IgnoreError(AsCoreType(&aResult.mNetworkName).Set(beaconPayload->GetNetworkName())); + VerifyOrExit(IsValidUtf8String(aResult.mNetworkName.m8), error = kErrorParse); + aResult.mExtendedPanId = beaconPayload->GetExtendedPanId(); + } +#endif + LogBeacon("Received"); exit: diff --git a/src/core/mac/mac_frame.hpp b/src/core/mac/mac_frame.hpp index 1c1a1a504..10de014ed 100644 --- a/src/core/mac/mac_frame.hpp +++ b/src/core/mac/mac_frame.hpp @@ -1482,6 +1482,93 @@ private: uint8_t mPendingAddressSpec; } OT_TOOL_PACKED_END; +#if OPENTHREAD_CONFIG_MAC_BEACON_PAYLOAD_PARSING_ENABLE +/** + * This class implements IEEE 802.15.4 Beacon Payload generation and parsing. + * + */ +OT_TOOL_PACKED_BEGIN +class BeaconPayload +{ +public: + static constexpr uint8_t kProtocolId = 3; ///< Thread Protocol ID. + static constexpr uint8_t kVersionOffset = 4; ///< Version field bit offset. + static constexpr uint8_t kNativeFlag = 1 << 3; ///< Native Commissioner flag. + static constexpr uint8_t kJoiningFlag = 1 << 0; ///< Joining Permitted flag. + + /** + * This constant specified the maximum number of chars in Network Name (excludes null char). + * + */ + static constexpr uint8_t kMaxSize = OT_NETWORK_NAME_MAX_SIZE; + + /** + * This method indicates whether or not the beacon appears to be a valid Thread Beacon Payload. + * + * @retval TRUE If the beacon appears to be a valid Thread Beacon Payload. + * @retval FALSE If the beacon does not appear to be a valid Thread Beacon Payload. + * + */ + bool IsValid(void) const { return (mProtocolId == kProtocolId); } + + /** + * This method returns the Protocol ID value. + * + * @returns the Protocol ID value. + * + */ + uint8_t GetProtocolId(void) const { return mProtocolId; } + + /** + * This method returns the Protocol Version value. + * + * @returns The Protocol Version value. + * + */ + uint8_t GetProtocolVersion(void) const { return mFlags >> kVersionOffset; } + + /** + * This method indicates whether or not the Native Commissioner flag is set. + * + * @retval TRUE If the Native Commissioner flag is set. + * @retval FALSE If the Native Commissioner flag is not set. + * + */ + bool IsNative(void) const { return (mFlags & kNativeFlag) != 0; } + + /** + * This method indicates whether or not the Joining Permitted flag is set. + * + * @retval TRUE If the Joining Permitted flag is set. + * @retval FALSE If the Joining Permitted flag is not set. + * + */ + bool IsJoiningPermitted(void) const { return (mFlags & kJoiningFlag) != 0; } + + /** + * This method gets the Network Name field. + * + * @returns The Network Name field as `NameData`. + * + */ + const char *GetNetworkName(void) const { return mNetworkName; } + + /** + * This method returns the Extended PAN ID field. + * + * @returns The Extended PAN ID field. + * + */ + const otExtendedPanId &GetExtendedPanId(void) const { return mExtendedPanId; } + +private: + uint8_t mProtocolId; + uint8_t mFlags; + char mNetworkName[kMaxSize]; + otExtendedPanId mExtendedPanId; +} OT_TOOL_PACKED_END; +#endif // OPENTHREAD_CONFIG_MAC_BEACON_PAYLOAD_PARSING_ENABLE + /** * This class implements CSL IE data structure. * From 1b4a866feabcd02c76c9244d749d21d1ffe7bc76 Mon Sep 17 00:00:00 2001 From: Jonathan Hui Date: Fri, 27 May 2022 10:46:35 -0700 Subject: [PATCH 40/80] [tests] disable some checks in Cert_9_2_09 (#7771) Disable steps 32, 33, and 34 until a solution is found. Depending on timing, there may be one MLE Data Request/Response exchange for both Active and Pending Operational Datasets or individual MLE Data Request/Response exchange for each Active and Pending Operational Dataset separately. --- .../Cert_9_2_09_PendingPartition.py | 119 ++++++++++-------- 1 file changed, 64 insertions(+), 55 deletions(-) diff --git a/tests/scripts/thread-cert/Cert_9_2_09_PendingPartition.py b/tests/scripts/thread-cert/Cert_9_2_09_PendingPartition.py index 209669cc3..621384f17 100755 --- a/tests/scripts/thread-cert/Cert_9_2_09_PendingPartition.py +++ b/tests/scripts/thread-cert/Cert_9_2_09_PendingPartition.py @@ -643,6 +643,15 @@ class Cert_9_2_09_PendingPartition(thread_cert.TestCase): ).\ must_next() + # + # Disable steps 32, 33, and 34 until a solution is + # found. Depending on timing, there may be one MLE Data + # Request/Response exchange for both Active and Pending + # Operational Datasets or individual MLE Data Request/Response + # exchange for each Active and Pending Operational Dataset + # separately. + # + # Step 32: Leader sends a MLE Data Response to Commissioner including the following TLVs: # - Source Address TLV # - Leader Data TLV @@ -656,30 +665,30 @@ class Cert_9_2_09_PendingPartition(thread_cert.TestCase): # - Channel TLV : ‘Primary’ # - PAN ID TLV : 0xABCD # - Network Name TLV : 'TEST' - with pkts.save_index(): - pkts.filter_mle_cmd(MLE_DATA_RESPONSE).\ - filter_wpan_src64(LEADER).\ - filter_wpan_dst64(COMMISSIONER).\ - filter(lambda p: { - SOURCE_ADDRESS_TLV, - LEADER_DATA_TLV, - ACTIVE_TIMESTAMP_TLV, - PENDING_TIMESTAMP_TLV, - PENDING_OPERATION_DATASET_TLV - } <= set(p.mle.tlv.type) and\ - p.thread_nwd.tlv.stable == [0] and\ - NWD_COMMISSIONING_DATA_TLV in p.thread_nwd.tlv.type and\ - NM_COMMISSIONER_SESSION_ID_TLV in p.thread_meshcop.tlv.type and\ - NM_BORDER_AGENT_LOCATOR_TLV in p.thread_meshcop.tlv.type and\ - p.mle.tlv.active_tstamp == ROUTER2_ACTIVE_TIMESTAMP and\ - p.mle.tlv.pending_tstamp == ROUTER2_PENDING_TIMESTAMP and\ - p.thread_meshcop.tlv.net_name == [ROUTER2_NET_NAME, ROUTER2_NET_NAME] and\ - p.thread_meshcop.tlv.delay_timer < ROUTER2_DELAY_TIMER and\ - p.thread_meshcop.tlv.active_tstamp == ROUTER2_PENDING_ACTIVE_TIMESTAMP and\ - p.thread_meshcop.tlv.channel == [CHANNEL_INIT, CHANNEL_FINAL] and\ - p.thread_meshcop.tlv.pan_id == [PANID_INIT, PANID_FINAL] - ).\ - must_next() + #with pkts.save_index(): + # pkts.filter_mle_cmd(MLE_DATA_RESPONSE).\ + # filter_wpan_src64(LEADER).\ + # filter_wpan_dst64(COMMISSIONER).\ + # filter(lambda p: { + # SOURCE_ADDRESS_TLV, + # LEADER_DATA_TLV, + # ACTIVE_TIMESTAMP_TLV, + # PENDING_TIMESTAMP_TLV, + # PENDING_OPERATION_DATASET_TLV + # } <= set(p.mle.tlv.type) and\ + # p.thread_nwd.tlv.stable == [0] and\ + # NWD_COMMISSIONING_DATA_TLV in p.thread_nwd.tlv.type and\ + # NM_COMMISSIONER_SESSION_ID_TLV in p.thread_meshcop.tlv.type and\ + # NM_BORDER_AGENT_LOCATOR_TLV in p.thread_meshcop.tlv.type and\ + # p.mle.tlv.active_tstamp == ROUTER2_ACTIVE_TIMESTAMP and\ + # p.mle.tlv.pending_tstamp == ROUTER2_PENDING_TIMESTAMP and\ + # p.thread_meshcop.tlv.net_name == [ROUTER2_NET_NAME, ROUTER2_NET_NAME] and\ + # p.thread_meshcop.tlv.delay_timer < ROUTER2_DELAY_TIMER and\ + # p.thread_meshcop.tlv.active_tstamp == ROUTER2_PENDING_ACTIVE_TIMESTAMP and\ + # p.thread_meshcop.tlv.channel == [CHANNEL_INIT, CHANNEL_FINAL] and\ + # p.thread_meshcop.tlv.pan_id == [PANID_INIT, PANID_FINAL] + # ).\ + # must_next() # Step 33: Router_1 MUST send a unicast MLE Data Request to the Leader, including the # following TLVs: @@ -687,19 +696,19 @@ class Cert_9_2_09_PendingPartition(thread_cert.TestCase): # - Network Data TLV # - Active Timestamp TLV (10s) # - Pending Timestamp TLV (30s) - pkts.filter_wpan_src64(ROUTER_1).\ - filter_wpan_dst64(LEADER).\ - filter_mle_cmd(MLE_DATA_REQUEST).\ - filter(lambda p: { - TLV_REQUEST_TLV, - NETWORK_DATA_TLV, - ACTIVE_TIMESTAMP_TLV - } <= set(p.mle.tlv.type) and\ - p.mle.tlv.active_tstamp == TIMESTAMP_INIT and\ - p.mle.tlv.pending_tstamp == COMM_PENDING_TIMESTAMP and\ - p.thread_meshcop.tlv.type is nullField - ).\ - must_next() + #pkts.filter_wpan_src64(ROUTER_1).\ + # filter_wpan_dst64(LEADER).\ + # filter_mle_cmd(MLE_DATA_REQUEST).\ + # filter(lambda p: { + # TLV_REQUEST_TLV, + # NETWORK_DATA_TLV, + # ACTIVE_TIMESTAMP_TLV + # } <= set(p.mle.tlv.type) and\ + # p.mle.tlv.active_tstamp == TIMESTAMP_INIT and\ + # p.mle.tlv.pending_tstamp == COMM_PENDING_TIMESTAMP and\ + # p.thread_meshcop.tlv.type is nullField + # ).\ + # must_next() # Step 34: Leader sends a MLE Data Response to Router_1 including the following TLVs: # - Source Address TLV @@ -713,24 +722,24 @@ class Cert_9_2_09_PendingPartition(thread_cert.TestCase): # - Delay Timer TLV <~ 200s> # - Channel TLV : ‘Primary’ # - PAN ID TLV : 0xABCD - pkts.filter_mle_cmd(MLE_DATA_RESPONSE).\ - filter_wpan_src64(LEADER).\ - filter_wpan_dst64(ROUTER_1).\ - filter(lambda p: { - SOURCE_ADDRESS_TLV, - LEADER_DATA_TLV, - ACTIVE_TIMESTAMP_TLV, - PENDING_TIMESTAMP_TLV, - PENDING_OPERATION_DATASET_TLV - } <= set(p.mle.tlv.type) and\ - p.mle.tlv.active_tstamp == ROUTER2_ACTIVE_TIMESTAMP and\ - p.mle.tlv.pending_tstamp == ROUTER2_PENDING_TIMESTAMP and\ - p.thread_meshcop.tlv.delay_timer < ROUTER2_DELAY_TIMER and\ - p.thread_meshcop.tlv.active_tstamp == ROUTER2_PENDING_ACTIVE_TIMESTAMP and\ - p.thread_meshcop.tlv.channel == [CHANNEL_INIT, CHANNEL_FINAL] and\ - p.thread_meshcop.tlv.pan_id == [PANID_INIT, PANID_FINAL] - ).\ - must_next() + #pkts.filter_mle_cmd(MLE_DATA_RESPONSE).\ + # filter_wpan_src64(LEADER).\ + # filter_wpan_dst64(ROUTER_1).\ + # filter(lambda p: { + # SOURCE_ADDRESS_TLV, + # LEADER_DATA_TLV, + # ACTIVE_TIMESTAMP_TLV, + # PENDING_TIMESTAMP_TLV, + # PENDING_OPERATION_DATASET_TLV + # } <= set(p.mle.tlv.type) and\ + # p.mle.tlv.active_tstamp == ROUTER2_ACTIVE_TIMESTAMP and\ + # p.mle.tlv.pending_tstamp == ROUTER2_PENDING_TIMESTAMP and\ + # p.thread_meshcop.tlv.delay_timer < ROUTER2_DELAY_TIMER and\ + # p.thread_meshcop.tlv.active_tstamp == ROUTER2_PENDING_ACTIVE_TIMESTAMP and\ + # p.thread_meshcop.tlv.channel == [CHANNEL_INIT, CHANNEL_FINAL] and\ + # p.thread_meshcop.tlv.pan_id == [PANID_INIT, PANID_FINAL] + # ).\ + # must_next() # Step 36: The DUT MUST respond with an ICMPv6 Echo Reply _pkt = pkts.filter_ping_request().\ From 62f5a3907cf603b95748f549bfe660b353f1c3af Mon Sep 17 00:00:00 2001 From: Jonathan Hui Date: Fri, 27 May 2022 13:13:14 -0700 Subject: [PATCH 41/80] [mle-router] fix uninitialized pointer in `SendLinkRequest()` (#7770) --- src/core/thread/mle_router.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/thread/mle_router.cpp b/src/core/thread/mle_router.cpp index ccd167687..69c10b58c 100644 --- a/src/core/thread/mle_router.cpp +++ b/src/core/thread/mle_router.cpp @@ -539,7 +539,7 @@ Error MleRouter::SendLinkRequest(Neighbor *aNeighbor) static const uint8_t routerTlvs[] = {Tlv::kLinkMargin}; static const uint8_t validNeighborTlvs[] = {Tlv::kLinkMargin, Tlv::kRoute}; Error error = kErrorNone; - TxMessage * message; + TxMessage * message = nullptr; Ip6::Address destination; VerifyOrExit(mLinkRequestDelay == 0 && mChallengeTimeout == 0); From 81a948b4d8923924e53ae2dd28a0262c9fde0ed3 Mon Sep 17 00:00:00 2001 From: Jonathan Hui Date: Fri, 27 May 2022 13:15:58 -0700 Subject: [PATCH 42/80] [mle] evaluate neighbor links before upgrade to router (#7765) OpenThread only etablishes links when the link margin is above a certain threshold, to help ensure that links in the routing topology are stable. However, if a device has no existing links to neighboring routers above threshold, then upgrading to a router is useless and disruptive. This commit adds a check to ensure there is at least one neighbor above the link margin threshold before upgrading to a router. --- src/core/thread/mle_router.cpp | 37 ++++++- src/core/thread/mle_router.hpp | 1 + tests/scripts/thread-cert/Makefile.am | 2 + .../thread-cert/test_router_upgrade.py | 99 +++++++++++++++++++ 4 files changed, 138 insertions(+), 1 deletion(-) create mode 100755 tests/scripts/thread-cert/test_router_upgrade.py diff --git a/src/core/thread/mle_router.cpp b/src/core/thread/mle_router.cpp index 69c10b58c..813f1a2a1 100644 --- a/src/core/thread/mle_router.cpp +++ b/src/core/thread/mle_router.cpp @@ -1764,6 +1764,41 @@ exit: LogProcessError(kTypeParentRequest, error); } +bool MleRouter::HasNeighborWithGoodLinkQuality(void) const +{ + bool haveNeighbor = true; + uint8_t linkMargin; + + linkMargin = + LinkQualityInfo::ConvertRssToLinkMargin(Get().GetNoiseFloor(), mParent.GetLinkInfo().GetLastRss()); + + if (linkMargin >= OPENTHREAD_CONFIG_MLE_LINK_REQUEST_MARGIN_MIN) + { + ExitNow(); + } + + for (Router &router : Get().Iterate()) + { + if (!router.IsStateValid()) + { + continue; + } + + linkMargin = + LinkQualityInfo::ConvertRssToLinkMargin(Get().GetNoiseFloor(), router.GetLinkInfo().GetLastRss()); + + if (linkMargin >= OPENTHREAD_CONFIG_MLE_LINK_REQUEST_MARGIN_MIN) + { + ExitNow(); + } + } + + haveNeighbor = false; + +exit: + return haveNeighbor; +} + void MleRouter::HandleTimeTick(void) { bool routerStateUpdate = false; @@ -1833,7 +1868,7 @@ void MleRouter::HandleTimeTick(void) case kRoleChild: if (routerStateUpdate) { - if (mRouterTable.GetActiveRouterCount() < mRouterUpgradeThreshold) + if (mRouterTable.GetActiveRouterCount() < mRouterUpgradeThreshold && HasNeighborWithGoodLinkQuality()) { // upgrade to Router IgnoreError(BecomeRouter(ThreadStatusTlv::kTooFewRouters)); diff --git a/src/core/thread/mle_router.hpp b/src/core/thread/mle_router.hpp index 0ae422ed2..3ccd231fb 100644 --- a/src/core/thread/mle_router.hpp +++ b/src/core/thread/mle_router.hpp @@ -633,6 +633,7 @@ private: Error UpdateChildAddresses(const Message &aMessage, uint16_t aOffset, Child &aChild); void UpdateRoutes(const RouteTlv &aRoute, uint8_t aRouterId); bool UpdateLinkQualityOut(const RouteTlv &aRoute, Router &aNeighbor, bool &aResetAdvInterval); + bool HasNeighborWithGoodLinkQuality(void) const; static void HandleAddressSolicitResponse(void * aContext, otMessage * aMessage, diff --git a/tests/scripts/thread-cert/Makefile.am b/tests/scripts/thread-cert/Makefile.am index 1b517e190..4b115e902 100644 --- a/tests/scripts/thread-cert/Makefile.am +++ b/tests/scripts/thread-cert/Makefile.am @@ -184,6 +184,7 @@ EXTRA_DIST = \ test_reset.py \ test_route_table.py \ test_router_reattach.py \ + test_router_upgrade.py \ test_service.py \ test_set_mliid.py \ test_srp_auto_start_mode.py \ @@ -258,6 +259,7 @@ check_SCRIPTS = \ test_reset.py \ test_route_table.py \ test_router_reattach.py \ + test_router_upgrade.py \ test_service.py \ test_srp_auto_start_mode.py \ test_srp_client_remove_host.py \ diff --git a/tests/scripts/thread-cert/test_router_upgrade.py b/tests/scripts/thread-cert/test_router_upgrade.py new file mode 100755 index 000000000..bb1374fea --- /dev/null +++ b/tests/scripts/thread-cert/test_router_upgrade.py @@ -0,0 +1,99 @@ +#!/usr/bin/env python3 +# +# Copyright (c) 2022, The OpenThread Authors. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. Neither the name of the copyright holder nor the +# names of its contributors may be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. +# + +import unittest + +import config +import mle +import network_layer +import thread_cert + +LEADER = 1 +REED = 2 + +RSSI_LOW = -95 +RSSI_HIGH = -45 + +ROUTER_UPGRADE_DELAY = 150 + +# Test Purpose and Description: +# ----------------------------- +# The purpose of this test case is to show that a REED does not +# upgrade to router if it does not have a neighbor with link margin +# above threshold. +# +# Test Topology: +# ------------- +# Leader +# | +# REED +# +# DUT Types: +# ---------- +# Leader +# REED + + +class TestRouterUpgrade(thread_cert.TestCase): + USE_MESSAGE_FACTORY = False + + TOPOLOGY = { + LEADER: { + 'name': 'LEADER', + 'mode': 'rdn', + 'allowlist': [REED] + }, + REED: { + 'name': 'REED', + 'mode': 'rdn', + 'allowlist': [LEADER] + }, + } + + def test(self): + self.nodes[LEADER].start() + self.simulator.go(5) + self.assertEqual(self.nodes[LEADER].get_state(), 'leader') + + self.nodes[REED].add_allowlist(self.nodes[LEADER].get_addr64(), rssi=RSSI_LOW) + self.nodes[REED].enable_allowlist() + + self.nodes[REED].start() + self.simulator.go(ROUTER_UPGRADE_DELAY) + self.assertEqual(self.nodes[REED].get_state(), 'child') + + self.nodes[REED].add_allowlist(self.nodes[LEADER].get_addr64(), rssi=RSSI_HIGH) + self.nodes[REED].enable_allowlist() + + self.simulator.go(ROUTER_UPGRADE_DELAY) + self.assertEqual(self.nodes[REED].get_state(), 'router') + + +if __name__ == '__main__': + unittest.main() From bfb60204fcb27dc874685b63049be249ac7ddb08 Mon Sep 17 00:00:00 2001 From: Jonathan Hui Date: Fri, 27 May 2022 13:16:23 -0700 Subject: [PATCH 43/80] [ndproxy] fix typo `Attampts` -> `Attempts` (#7752) --- src/core/backbone_router/ndproxy_table.cpp | 4 ++-- src/core/backbone_router/ndproxy_table.hpp | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/core/backbone_router/ndproxy_table.cpp b/src/core/backbone_router/ndproxy_table.cpp index 9d6595897..89c044225 100644 --- a/src/core/backbone_router/ndproxy_table.cpp +++ b/src/core/backbone_router/ndproxy_table.cpp @@ -251,7 +251,7 @@ void NdProxyTable::HandleTimer(void) for (NdProxy &proxy : Iterate(kFilterDadInProcess)) { - if (proxy.IsDadAttamptsComplete()) + if (proxy.IsDadAttemptsComplete()) { proxy.mDadFlag = false; NotifyDuaRegistrationOnBackboneLink(proxy, /* aIsRenew */ false); @@ -262,7 +262,7 @@ void NdProxyTable::HandleTimer(void) if (Get().SendBackboneQuery(GetDua(proxy)) == kErrorNone) { - proxy.IncreaseDadAttampts(); + proxy.IncreaseDadAttempts(); } } } diff --git a/src/core/backbone_router/ndproxy_table.hpp b/src/core/backbone_router/ndproxy_table.hpp index b4cf71605..097a6d3a3 100644 --- a/src/core/backbone_router/ndproxy_table.hpp +++ b/src/core/backbone_router/ndproxy_table.hpp @@ -111,8 +111,8 @@ public: uint32_t aTimeSinceLastTransaction); void Update(uint16_t aRloc16, uint32_t aTimeSinceLastTransaction); - void IncreaseDadAttampts(void) { mDadAttempts++; } - bool IsDadAttamptsComplete() const { return mDadAttempts == Mle::kDuaDadRepeats; } + void IncreaseDadAttempts(void) { mDadAttempts++; } + bool IsDadAttemptsComplete() const { return mDadAttempts == Mle::kDuaDadRepeats; } Ip6::InterfaceIdentifier mAddressIid; Ip6::InterfaceIdentifier mMeshLocalIid; From 40484c94687163e60e44e3f23855b49e7dd911df Mon Sep 17 00:00:00 2001 From: Abtin Keshavarzian Date: Mon, 30 May 2022 22:06:00 -0700 Subject: [PATCH 44/80] [routing-manager] directly remove on-link prefix from discovered list on advert start (#7764) This commit changes `EvaluteOnLinkPrefix()` so that when we decide to start advertising the local on-link prefix, we go through the list of discovered prefixes to check if the local prefix was previously discovered and included in the list, and remove it from the list (without unpublishing it from Thread Network Data). This change allows us to simplify `InvalidateDiscoveredPrefixes()` which was earlier called and was in charge of removing the local prefix. This helps align the logic that when we are advertising the on-link prefix, we do not allow it to be added in the discovered prefix list from `UpdateDiscoveredOnLinkPrefix()`. --- src/core/border_router/routing_manager.cpp | 39 +++++++++++++--------- 1 file changed, 23 insertions(+), 16 deletions(-) diff --git a/src/core/border_router/routing_manager.cpp b/src/core/border_router/routing_manager.cpp index b0712c50c..6581da52e 100644 --- a/src/core/border_router/routing_manager.cpp +++ b/src/core/border_router/routing_manager.cpp @@ -578,11 +578,25 @@ void RoutingManager::EvaluateOnLinkPrefix(void) LogInfo("Start advertising on-link prefix %s on %s", mLocalOnLinkPrefix.ToString().AsCString(), mInfraIf.ToString().AsCString()); - // Call `InvalidateDiscoveredPrefixes()` after setting - // `mIsAdvertisingLocalOnLinkPrefix` to remove on-link - // prefix in case it was discovered and included in - // `mDiscoveredPrefixes` list earlier. - InvalidateDiscoveredPrefixes(); + // We go through `mDiscoveredPrefixes` list to check if the + // local on-link prefix was previously discovered and + // included in the list and if so we remove it from list. + // + // Note that `UpdateDiscoveredOnLinkPrefix()` will also + // check and not add local on-link prefix in the discovered + // prefix list while we are advertising the local on-link + // prefix. + + for (ExternalPrefix &prefix : mDiscoveredPrefixes) + { + if (prefix.IsOnLinkPrefix() && mLocalOnLinkPrefix == prefix.GetPrefix()) + { + // To remove the prefix from the list, we copy the + // popped last entry into `prefix` entry. + prefix = *mDiscoveredPrefixes.PopBack(); + break; + } + } } mOnLinkPrefixDeprecateTimer.Stop(); @@ -1297,23 +1311,16 @@ void RoutingManager::InvalidateDiscoveredPrefixes(void) for (ExternalPrefixArray::IndexType index = 0; index < mDiscoveredPrefixes.GetLength();) { ExternalPrefix &prefix = mDiscoveredPrefixes[index]; - bool isAdvertisedLocalOnLinkPrefix = - mIsAdvertisingLocalOnLinkPrefix && prefix.IsOnLinkPrefix() && mLocalOnLinkPrefix == prefix.GetPrefix(); // We invalidate expired prefixes, or local OMR prefixes // (either in `mAdvertisedOmrPrefixes` or in Thread Network - // Data), or if the prefix matches the local on-link prefix - // (when local on-link prefix is being advertised). + // Data). if ((prefix.GetExpireTime() <= now) || - (!prefix.IsOnLinkPrefix() && (mAdvertisedOmrPrefixes.Contains(prefix.GetPrefix()) || - NetworkDataContainsOmrPrefix(prefix.GetPrefix()))) || - isAdvertisedLocalOnLinkPrefix) + (!prefix.IsOnLinkPrefix() && + (mAdvertisedOmrPrefixes.Contains(prefix.GetPrefix()) || NetworkDataContainsOmrPrefix(prefix.GetPrefix())))) { - if (!isAdvertisedLocalOnLinkPrefix) - { - UnpublishExternalRoute(prefix.GetPrefix()); - } + UnpublishExternalRoute(prefix.GetPrefix()); // Remove the prefix from the array by replacing it with // last entry in the array (we copy the popped last entry From 2870c1c4dc1655ff7105483cda85d8067d2c3e60 Mon Sep 17 00:00:00 2001 From: Yakun Xu Date: Wed, 1 Jun 2022 08:30:41 +0800 Subject: [PATCH 45/80] [border-agent] log the listening port (#7768) --- src/core/meshcop/border_agent.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/meshcop/border_agent.cpp b/src/core/meshcop/border_agent.cpp index 1fb3984fe..e10a79da5 100644 --- a/src/core/meshcop/border_agent.cpp +++ b/src/core/meshcop/border_agent.cpp @@ -588,7 +588,7 @@ void BorderAgent::Start(void) mState = kStateStarted; mUdpProxyPort = 0; - LogInfo("Border Agent start listening on port %d", kBorderAgentUdpPort); + LogInfo("Border Agent start listening on port %u", GetUdpPort()); exit: if (error != kErrorNone) From 11eb75348462b7541b64a2278deffddff0b10d1f Mon Sep 17 00:00:00 2001 From: Abtin Keshavarzian Date: Tue, 31 May 2022 20:09:07 -0700 Subject: [PATCH 46/80] [settings] remove unused/deprecated definitions (#7762) This commit removes deprecated `Settings` definitions in particular the OMR prefix, on-link prefix and NAT64 prefix. --- include/openthread/instance.h | 2 +- include/openthread/platform/settings.h | 14 ++++++--- src/core/border_router/routing_manager.cpp | 2 -- src/core/common/settings.cpp | 15 +++------ src/core/common/settings.hpp | 36 +--------------------- 5 files changed, 15 insertions(+), 54 deletions(-) diff --git a/include/openthread/instance.h b/include/openthread/instance.h index 6bd2e7148..769b59e21 100644 --- a/include/openthread/instance.h +++ b/include/openthread/instance.h @@ -53,7 +53,7 @@ extern "C" { * @note This number versions both OpenThread platform and user APIs. * */ -#define OPENTHREAD_API_VERSION (213) +#define OPENTHREAD_API_VERSION (214) /** * @addtogroup api-instance diff --git a/include/openthread/platform/settings.h b/include/openthread/platform/settings.h index a5d609965..dd6c41f84 100644 --- a/include/openthread/platform/settings.h +++ b/include/openthread/platform/settings.h @@ -55,7 +55,8 @@ extern "C" { * This enumeration defines the keys of settings. * * Note: When adding a new settings key, if the settings corresponding to the key contains security sensitive - * information, the developer MUST add the key to the array `kSensitiveKeys`. + * information, the developer MUST add the key to the array `aSensitiveKeys` which is passed in + * `otPlatSettingsInit()`. * */ enum @@ -65,17 +66,20 @@ enum OT_SETTINGS_KEY_NETWORK_INFO = 0x0003, ///< Thread network information. OT_SETTINGS_KEY_PARENT_INFO = 0x0004, ///< Parent information. OT_SETTINGS_KEY_CHILD_INFO = 0x0005, ///< Child information. - OT_SETTINGS_KEY_RESERVED = 0x0006, ///< Reserved (previously auto-start). OT_SETTINGS_KEY_SLAAC_IID_SECRET_KEY = 0x0007, ///< SLAAC key to generate semantically opaque IID. OT_SETTINGS_KEY_DAD_INFO = 0x0008, ///< Duplicate Address Detection (DAD) information. - OT_SETTINGS_KEY_LEGACY_OMR_PREFIX = 0x0009, ///< Reserved. Legacy Off-mesh routable (OMR) prefix. - OT_SETTINGS_KEY_ON_LINK_PREFIX = 0x000a, ///< On-link prefix for infrastructure link. OT_SETTINGS_KEY_SRP_ECDSA_KEY = 0x000b, ///< SRP client ECDSA public/private key pair. OT_SETTINGS_KEY_SRP_CLIENT_INFO = 0x000c, ///< The SRP client info (selected SRP server address). OT_SETTINGS_KEY_SRP_SERVER_INFO = 0x000d, ///< The SRP server info (UDP port). - OT_SETTINGS_KEY_LEGACY_NAT64_PREFIX = 0x000e, ///< Reserved. Legacy NAT64 prefix. OT_SETTINGS_KEY_BR_ULA_PREFIX = 0x000f, ///< BR ULA prefix. + // Deprecated and reserved key values: + // + // 0x0006 previously auto-start. + // 0x0009 previously OMR prefix. + // 0x000a previously on-link prefix. + // 0x000e previously NAT64 prefix. + // Keys in range 0x8000-0xffff are reserved for vendor-specific use. OT_SETTINGS_KEY_VENDOR_RESERVED_MIN = 0x8000, OT_SETTINGS_KEY_VENDOR_RESERVED_MAX = 0xffff, diff --git a/src/core/border_router/routing_manager.cpp b/src/core/border_router/routing_manager.cpp index 6581da52e..1ea7059f6 100644 --- a/src/core/border_router/routing_manager.cpp +++ b/src/core/border_router/routing_manager.cpp @@ -195,8 +195,6 @@ exit: void RoutingManager::GenerateOmrPrefix(void) { - IgnoreError(Get().Delete()); - mLocalOmrPrefix = mBrUlaPrefix; mLocalOmrPrefix.SetSubnetId(kOmrPrefixSubnetId); mLocalOmrPrefix.SetLength(kOmrPrefixLength); diff --git a/src/core/common/settings.cpp b/src/core/common/settings.cpp index c69b32261..67f76e205 100644 --- a/src/core/common/settings.cpp +++ b/src/core/common/settings.cpp @@ -146,15 +146,15 @@ const char *SettingsBase::KeyToString(Key aKey) "NetworkInfo", // (3) kKeyNetworkInfo "ParentInfo", // (4) kKeyParentInfo "ChildInfo", // (5) kKeyChildInfo - "", // (6) kKeyReserved + "", // (6) Removed (previously auto-start). "SlaacIidSecretKey", // (7) kKeySlaacIidSecretKey "DadInfo", // (8) kKeyDadInfo - "LegacyOmrPrefix", // (9) kKeyLegacyOmrPrefix - "OnLinkPrefix", // (10) kKeyOnLinkPrefix + "", // (9) Removed (previously OMR prefix). + "", // (10) Removed (previously on-link prefix). "SrpEcdsaKey", // (11) kKeySrpEcdsaKey "SrpClientInfo", // (12) kKeySrpClientInfo "SrpServerInfo", // (13) kKeySrpServerInfo - "LegacyNat64Prefix", // (14) kKeyLegacyNat64Prefix + "", // (14) Removed (previously NAT64 prefix) "BrUlaPrefix", // (15) kKeyBrUlaPrefix }; @@ -163,15 +163,11 @@ const char *SettingsBase::KeyToString(Key aKey) static_assert(3 == kKeyNetworkInfo, "kKeyNetworkInfo value is incorrect"); static_assert(4 == kKeyParentInfo, "kKeyParentInfo value is incorrect"); static_assert(5 == kKeyChildInfo, "kKeyChildInfo value is incorrect"); - static_assert(6 == kKeyReserved, "kKeyReserved value is incorrect"); static_assert(7 == kKeySlaacIidSecretKey, "kKeySlaacIidSecretKey value is incorrect"); static_assert(8 == kKeyDadInfo, "kKeyDadInfo value is incorrect"); - static_assert(9 == kKeyLegacyOmrPrefix, "kKeyLegacyOmrPrefix value is incorrect"); - static_assert(10 == kKeyOnLinkPrefix, "kKeyOnLinkPrefix value is incorrect"); static_assert(11 == kKeySrpEcdsaKey, "kKeySrpEcdsaKey value is incorrect"); static_assert(12 == kKeySrpClientInfo, "kKeySrpClientInfo value is incorrect"); static_assert(13 == kKeySrpServerInfo, "kKeySrpServerInfo value is incorrect"); - static_assert(14 == kKeyLegacyNat64Prefix, "kKeyLegacyNat64Prefix value is incorrect"); static_assert(15 == kKeyBrUlaPrefix, "kKeyBrUlaPrefix value is incorrect"); static_assert(kLastKey == kKeyBrUlaPrefix, "kLastKey is not valid"); @@ -435,9 +431,6 @@ void Settings::Log(Action aAction, Error aError, Key aKey, const void *aValue) #if OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE case kKeyBrUlaPrefix: - case kKeyLegacyOmrPrefix: - case kKeyOnLinkPrefix: - case kKeyLegacyNat64Prefix: LogPrefix(aAction, aKey, *reinterpret_cast(aValue)); break; #endif diff --git a/src/core/common/settings.hpp b/src/core/common/settings.hpp index 1758feba2..6db4a02f2 100644 --- a/src/core/common/settings.hpp +++ b/src/core/common/settings.hpp @@ -112,15 +112,11 @@ public: kKeyNetworkInfo = OT_SETTINGS_KEY_NETWORK_INFO, kKeyParentInfo = OT_SETTINGS_KEY_PARENT_INFO, kKeyChildInfo = OT_SETTINGS_KEY_CHILD_INFO, - kKeyReserved = OT_SETTINGS_KEY_RESERVED, kKeySlaacIidSecretKey = OT_SETTINGS_KEY_SLAAC_IID_SECRET_KEY, kKeyDadInfo = OT_SETTINGS_KEY_DAD_INFO, - kKeyLegacyOmrPrefix = OT_SETTINGS_KEY_LEGACY_OMR_PREFIX, - kKeyOnLinkPrefix = OT_SETTINGS_KEY_ON_LINK_PREFIX, kKeySrpEcdsaKey = OT_SETTINGS_KEY_SRP_ECDSA_KEY, kKeySrpClientInfo = OT_SETTINGS_KEY_SRP_CLIENT_INFO, kKeySrpServerInfo = OT_SETTINGS_KEY_SRP_SERVER_INFO, - kKeyLegacyNat64Prefix = OT_SETTINGS_KEY_LEGACY_NAT64_PREFIX, kKeyBrUlaPrefix = OT_SETTINGS_KEY_BR_ULA_PREFIX, }; @@ -583,37 +579,7 @@ public: private: BrUlaPrefix(void) = default; }; - - /** - * This class defines constants and types for legacy OMR prefix settings. - * - */ - class LegacyOmrPrefix - { - public: - static constexpr Key kKey = kKeyLegacyOmrPrefix; ///< The associated key. - - typedef Ip6::Prefix ValueType; ///< The associated value type. - - private: - LegacyOmrPrefix(void) = default; - }; - - /** - * This class defines constants and types for on-link prefix settings. - * - */ - class OnLinkPrefix - { - public: - static constexpr Key kKey = kKeyOnLinkPrefix; ///< The associated key. - - typedef Ip6::Prefix ValueType; ///< The associated value type. - - private: - OnLinkPrefix(void) = default; - }; -#endif // OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE +#endif #if OPENTHREAD_CONFIG_SRP_CLIENT_ENABLE /** From 33cc75ed3b7510ab93b2d563eafb5661a43f22d4 Mon Sep 17 00:00:00 2001 From: Jonathan Hui Date: Tue, 31 May 2022 22:01:27 -0700 Subject: [PATCH 47/80] [srp-server] implement TTL processing (#7738) --- include/openthread/instance.h | 2 +- include/openthread/srp_server.h | 44 +++++++ src/cli/cli_srp_server.cpp | 25 ++++ src/cli/cli_srp_server.hpp | 2 + src/core/api/srp_server_api.cpp | 15 +++ src/core/net/srp_server.cpp | 79 ++++++++++- src/core/net/srp_server.hpp | 77 ++++++++++- tests/scripts/thread-cert/Makefile.am | 2 + tests/scripts/thread-cert/node.py | 9 +- tests/scripts/thread-cert/test_srp_ttl.py | 151 ++++++++++++++++++++++ tests/toranj/cli/cli.py | 5 +- tools/otci/otci/otci.py | 2 +- 12 files changed, 399 insertions(+), 14 deletions(-) create mode 100755 tests/scripts/thread-cert/test_srp_ttl.py diff --git a/include/openthread/instance.h b/include/openthread/instance.h index 769b59e21..877e3267a 100644 --- a/include/openthread/instance.h +++ b/include/openthread/instance.h @@ -53,7 +53,7 @@ extern "C" { * @note This number versions both OpenThread platform and user APIs. * */ -#define OPENTHREAD_API_VERSION (214) +#define OPENTHREAD_API_VERSION (215) /** * @addtogroup api-instance diff --git a/include/openthread/srp_server.h b/include/openthread/srp_server.h index c9fd220e8..fba71749b 100644 --- a/include/openthread/srp_server.h +++ b/include/openthread/srp_server.h @@ -155,6 +155,16 @@ typedef enum otSrpServerAddressMode OT_SRP_SERVER_ADDRESS_MODE_ANYCAST = 1, ///< Anycast address mode. } otSrpServerAddressMode; +/** + * This structure includes SRP server TTL configurations. + * + */ +typedef struct otSrpServerTtlConfig +{ + uint32_t mMinTtl; ///< The minimum TTL in seconds. + uint32_t mMaxTtl; ///< The maximum TTL in seconds. +} otSrpServerTtlConfig; + /** * This structure includes SRP server LEASE and KEY-LEASE configurations. * @@ -298,6 +308,30 @@ otError otSrpServerSetAnycastModeSequenceNumber(otInstance *aInstance, uint8_t a */ void otSrpServerSetEnabled(otInstance *aInstance, bool aEnabled); +/** + * This function returns SRP server TTL configuration. + * + * @param[in] aInstance A pointer to an OpenThread instance. + * @param[out] aTtlConfig A pointer to an `otSrpServerTtlConfig` instance. + * + */ +void otSrpServerGetTtlConfig(otInstance *aInstance, otSrpServerTtlConfig *aTtlConfig); + +/** + * This function sets SRP server TTL configuration. + * + * The granted TTL will always be no greater than the max lease interval configured via `otSrpServerSetLeaseConfig()`, + * regardless of the minimum and maximum TTL configuration. + * + * @param[in] aInstance A pointer to an OpenThread instance. + * @param[in] aTtlConfig A pointer to an `otSrpServerTtlConfig` instance. + * + * @retval OT_ERROR_NONE Successfully set the TTL configuration. + * @retval OT_ERROR_INVALID_ARGS The TTL configuration is not valid. + * + */ +otError otSrpServerSetTtlConfig(otInstance *aInstance, const otSrpServerTtlConfig *aTtlConfig); + /** * This function returns SRP server LEASE and KEY-LEASE configurations. * @@ -617,6 +651,16 @@ uint16_t otSrpServerServiceGetWeight(const otSrpServerService *aService); */ uint16_t otSrpServerServiceGetPriority(const otSrpServerService *aService); +/** + * This function returns the TTL of the service instance. + * + * @param[in] aService A pointer to the SRP service. + * + * @returns The TTL of the service instance.. + * + */ +uint32_t otSrpServerServiceGetTtl(const otSrpServerService *aService); + /** * This function returns the TXT record data of the service instance. * diff --git a/src/cli/cli_srp_server.cpp b/src/cli/cli_srp_server.cpp index 74930419b..e13b8eca6 100644 --- a/src/cli/cli_srp_server.cpp +++ b/src/cli/cli_srp_server.cpp @@ -149,6 +149,30 @@ otError SrpServer::ProcessDisable(Arg aArgs[]) return OT_ERROR_NONE; } +otError SrpServer::ProcessTtl(Arg aArgs[]) +{ + otError error = OT_ERROR_NONE; + otSrpServerTtlConfig ttlConfig; + + if (aArgs[0].IsEmpty()) + { + otSrpServerGetTtlConfig(GetInstancePtr(), &ttlConfig); + OutputLine("min ttl: %u", ttlConfig.mMinTtl); + OutputLine("max ttl: %u", ttlConfig.mMaxTtl); + } + else + { + SuccessOrExit(error = aArgs[0].ParseAsUint32(ttlConfig.mMinTtl)); + SuccessOrExit(error = aArgs[1].ParseAsUint32(ttlConfig.mMaxTtl)); + VerifyOrExit(aArgs[2].IsEmpty(), error = OT_ERROR_INVALID_ARGS); + + error = otSrpServerSetTtlConfig(GetInstancePtr(), &ttlConfig); + } + +exit: + return error; +} + otError SrpServer::ProcessLease(Arg aArgs[]) { otError error = OT_ERROR_NONE; @@ -289,6 +313,7 @@ otError SrpServer::ProcessService(Arg aArgs[]) OutputLine(kIndentSize, "port: %hu", otSrpServerServiceGetPort(service)); OutputLine(kIndentSize, "priority: %hu", otSrpServerServiceGetPriority(service)); OutputLine(kIndentSize, "weight: %hu", otSrpServerServiceGetWeight(service)); + OutputLine(kIndentSize, "ttl: %hu", otSrpServerServiceGetTtl(service)); txtData = otSrpServerServiceGetTxtData(service, &txtDataLength); OutputFormat(kIndentSize, "TXT: "); diff --git a/src/cli/cli_srp_server.hpp b/src/cli/cli_srp_server.hpp index 03e5293df..363f59118 100644 --- a/src/cli/cli_srp_server.hpp +++ b/src/cli/cli_srp_server.hpp @@ -90,6 +90,7 @@ private: otError ProcessHost(Arg aArgs[]); otError ProcessService(Arg aArgs[]); otError ProcessSeqNum(Arg aArgs[]); + otError ProcessTtl(Arg aArgs[]); otError ProcessHelp(Arg aArgs[]); void OutputHostAddresses(const otSrpServerHost *aHost); @@ -100,6 +101,7 @@ private: {"help", &SrpServer::ProcessHelp}, {"host", &SrpServer::ProcessHost}, {"lease", &SrpServer::ProcessLease}, {"seqnum", &SrpServer::ProcessSeqNum}, {"service", &SrpServer::ProcessService}, {"state", &SrpServer::ProcessState}, + {"ttl", &SrpServer::ProcessTtl}, }; static_assert(BinarySearch::IsSorted(sCommands), "Command Table is not sorted"); diff --git a/src/core/api/srp_server_api.cpp b/src/core/api/srp_server_api.cpp index 950f25e3b..f9d4367e4 100644 --- a/src/core/api/srp_server_api.cpp +++ b/src/core/api/srp_server_api.cpp @@ -87,6 +87,16 @@ void otSrpServerSetEnabled(otInstance *aInstance, bool aEnabled) AsCoreType(aInstance).Get().SetEnabled(aEnabled); } +void otSrpServerGetTtlConfig(otInstance *aInstance, otSrpServerTtlConfig *aTtlConfig) +{ + AsCoreType(aInstance).Get().GetTtlConfig(AsCoreType(aTtlConfig)); +} + +otError otSrpServerSetTtlConfig(otInstance *aInstance, const otSrpServerTtlConfig *aTtlConfig) +{ + return AsCoreType(aInstance).Get().SetTtlConfig(AsCoreType(aTtlConfig)); +} + void otSrpServerGetLeaseConfig(otInstance *aInstance, otSrpServerLeaseConfig *aLeaseConfig) { AsCoreType(aInstance).Get().GetLeaseConfig(AsCoreType(aLeaseConfig)); @@ -204,6 +214,11 @@ uint16_t otSrpServerServiceGetPriority(const otSrpServerService *aService) return AsCoreType(aService).GetPriority(); } +uint32_t otSrpServerServiceGetTtl(const otSrpServerService *aService) +{ + return AsCoreType(aService).GetTtl(); +} + const uint8_t *otSrpServerServiceGetTxtData(const otSrpServerService *aService, uint16_t *aDataLength) { *aDataLength = AsCoreType(aService).GetTxtDataLength(); diff --git a/src/core/net/srp_server.cpp b/src/core/net/srp_server.cpp index ca012ab61..dd18a4070 100644 --- a/src/core/net/srp_server.cpp +++ b/src/core/net/srp_server.cpp @@ -168,6 +168,30 @@ exit: return; } +Server::TtlConfig::TtlConfig(void) +{ + mMinTtl = kDefaultMinTtl; + mMaxTtl = kDefaultMaxTtl; +} + +Error Server::SetTtlConfig(const TtlConfig &aTtlConfig) +{ + Error error = kErrorNone; + + VerifyOrExit(aTtlConfig.IsValid(), error = kErrorInvalidArgs); + mTtlConfig = aTtlConfig; + +exit: + return error; +} + +uint32_t Server::TtlConfig::GrantTtl(uint32_t aLease, uint32_t aTtl) const +{ + OT_ASSERT(mMinTtl <= mMaxTtl); + + return OT_MAX(mMinTtl, OT_MIN(OT_MIN(mMaxTtl, aLease), aTtl)); +} + Server::LeaseConfig::LeaseConfig(void) { mMinLease = kDefaultMinLease; @@ -266,7 +290,6 @@ void Server::AddHost(Host &aHost) OT_ASSERT(mHosts.FindMatching(aHost.GetFullName()) == nullptr); IgnoreError(mHosts.Add(aHost)); } - void Server::RemoveHost(Host *aHost, RetainName aRetainName, NotifyMode aNotifyServiceHandler) { VerifyOrExit(aHost != nullptr); @@ -374,20 +397,21 @@ void Server::HandleServiceUpdateResult(UpdateMetadata *aUpdate, Error aError) void Server::CommitSrpUpdate(Error aError, Host &aHost, const MessageMetadata &aMessageMetadata) { CommitSrpUpdate(aError, aHost, aMessageMetadata.mDnsHeader, aMessageMetadata.mMessageInfo, - aMessageMetadata.mLeaseConfig); + aMessageMetadata.mTtlConfig, aMessageMetadata.mLeaseConfig); } void Server::CommitSrpUpdate(Error aError, UpdateMetadata &aUpdateMetadata) { CommitSrpUpdate(aError, aUpdateMetadata.GetHost(), aUpdateMetadata.GetDnsHeader(), aUpdateMetadata.IsDirectRxFromClient() ? &aUpdateMetadata.GetMessageInfo() : nullptr, - aUpdateMetadata.GetLeaseConfig()); + aUpdateMetadata.GetTtlConfig(), aUpdateMetadata.GetLeaseConfig()); } void Server::CommitSrpUpdate(Error aError, Host & aHost, const Dns::UpdateHeader &aDnsHeader, const Ip6::MessageInfo * aMessageInfo, + const TtlConfig & aTtlConfig, const LeaseConfig & aLeaseConfig) { Host * existingHost; @@ -395,6 +419,7 @@ void Server::CommitSrpUpdate(Error aError, uint32_t hostKeyLease; uint32_t grantedLease; uint32_t grantedKeyLease; + uint32_t grantedTtl; bool shouldFreeHost = true; SuccessOrExit(aError); @@ -403,14 +428,17 @@ void Server::CommitSrpUpdate(Error aError, hostKeyLease = aHost.GetKeyLease(); grantedLease = aLeaseConfig.GrantLease(hostLease); grantedKeyLease = aLeaseConfig.GrantKeyLease(hostKeyLease); + grantedTtl = aTtlConfig.GrantTtl(grantedLease, aHost.GetTtl()); aHost.SetLease(grantedLease); aHost.SetKeyLease(grantedKeyLease); + aHost.SetTtl(grantedTtl); for (Service &service : aHost.mServices) { service.mDescription->mLease = grantedLease; service.mDescription->mKeyLease = grantedKeyLease; + service.mDescription->mTtl = grantedTtl; } existingHost = mHosts.FindMatching(aHost.GetFullName()); @@ -802,6 +830,8 @@ Error Server::ProcessHostDescriptionInstruction(Host & aHost, VerifyOrExit(record.GetClass() == aMetadata.mDnsZone.GetClass(), error = kErrorFailed); + SuccessOrExit(error = aHost.ProcessTtl(record.GetTtl())); + SuccessOrExit(error = aHost.SetFullName(name)); SuccessOrExit(error = aMessage.Read(offset, aaaaRecord)); @@ -816,6 +846,9 @@ Error Server::ProcessHostDescriptionInstruction(Host & aHost, Dns::Ecdsa256KeyRecord keyRecord; VerifyOrExit(record.GetClass() == aMetadata.mDnsZone.GetClass(), error = kErrorFailed); + + SuccessOrExit(error = aHost.ProcessTtl(record.GetTtl())); + SuccessOrExit(error = aMessage.Read(offset, keyRecord)); VerifyOrExit(keyRecord.IsValid(), error = kErrorParse); @@ -908,6 +941,11 @@ Error Server::ProcessServiceDiscoveryInstructions(Host & aHost, // This RR is a "Delete an RR from an RRset" update when the CLASS is NONE. service->mIsDeleted = (ptrRecord.GetClass() == Dns::ResourceRecord::kClassNone); + + if (!service->mIsDeleted) + { + SuccessOrExit(error = aHost.ProcessTtl(ptrRecord.GetTtl())); + } } exit: @@ -959,6 +997,9 @@ Error Server::ProcessServiceDescriptionInstructions(Host & aHost, uint16_t hostNameLength = sizeof(hostName); VerifyOrExit(record.GetClass() == aMetadata.mDnsZone.GetClass(), error = kErrorFailed); + + SuccessOrExit(error = aHost.ProcessTtl(record.GetTtl())); + SuccessOrExit(error = aMessage.Read(offset, srvRecord)); offset += sizeof(srvRecord); @@ -971,6 +1012,7 @@ Error Server::ProcessServiceDescriptionInstructions(Host & aHost, // Make sure that this is the first SRV RR for this service description VerifyOrExit(desc->mPort == 0, error = kErrorFailed); + desc->mTtl = srvRecord.GetTtl(); desc->mPriority = srvRecord.GetPriority(); desc->mWeight = srvRecord.GetWeight(); desc->mPort = srvRecord.GetPort(); @@ -980,6 +1022,8 @@ Error Server::ProcessServiceDescriptionInstructions(Host & aHost, { VerifyOrExit(record.GetClass() == aMetadata.mDnsZone.GetClass(), error = kErrorFailed); + SuccessOrExit(error = aHost.ProcessTtl(record.GetTtl())); + desc = aHost.FindServiceDescription(name); VerifyOrExit(desc != nullptr, error = kErrorFailed); @@ -1324,11 +1368,12 @@ void Server::HandleUdpReceive(Message &aMessage, const Ip6::MessageInfo &aMessag Error Server::ProcessMessage(Message &aMessage, const Ip6::MessageInfo &aMessageInfo) { - return ProcessMessage(aMessage, TimerMilli::GetNow(), mLeaseConfig, &aMessageInfo); + return ProcessMessage(aMessage, TimerMilli::GetNow(), mTtlConfig, mLeaseConfig, &aMessageInfo); } Error Server::ProcessMessage(Message & aMessage, TimeMilli aRxTime, + const TtlConfig & aTtlConfig, const LeaseConfig & aLeaseConfig, const Ip6::MessageInfo *aMessageInfo) { @@ -1337,6 +1382,7 @@ Error Server::ProcessMessage(Message & aMessage, metadata.mOffset = aMessage.GetOffset(); metadata.mRxTime = aRxTime; + metadata.mTtlConfig = aTtlConfig; metadata.mLeaseConfig = aLeaseConfig; metadata.mMessageInfo = aMessageInfo; @@ -1680,6 +1726,7 @@ Error Server::Service::Description::Init(const char *aInstanceName, Host &aHost) mHost = &aHost; mPriority = 0; mWeight = 0; + mTtl = 0; mPort = 0; mLease = 0; mKeyLease = 0; @@ -1708,6 +1755,7 @@ void Server::Service::Description::TakeResourcesFrom(Description &aDescription) mWeight = aDescription.mWeight; mPort = aDescription.mPort; + mTtl = aDescription.mTtl; mLease = aDescription.mLease; mKeyLease = aDescription.mKeyLease; mUpdateTime = TimerMilli::GetNow(); @@ -1736,6 +1784,7 @@ exit: Server::Host::Host(Instance &aInstance, TimeMilli aUpdateTime) : InstanceLocator(aInstance) , mNext(nullptr) + , mTtl(0) , mLease(0) , mKeyLease(0) , mUpdateTime(aUpdateTime) @@ -1804,6 +1853,26 @@ void Server::Host::GetLeaseInfo(LeaseInfo &aLeaseInfo) const aLeaseInfo.mRemainingKeyLease = (now <= keyExpireTime) ? (keyExpireTime - now) : 0; } +Error Server::Host::ProcessTtl(uint32_t aTtl) +{ + // This method processes the TTL value received in a resource record. + // + // If no TTL value is stored, this method wil set the stored value to @p aTtl and return `kErrorNone`. + // If a TTL value is stored and @p aTtl equals the stored value, this method returns `kErrorNone`. + // Otherwise, this method returns `kErrorRejected`. + + Error error = kErrorRejected; + + VerifyOrExit(aTtl && (mTtl == 0 || mTtl == aTtl)); + + mTtl = aTtl; + + error = kErrorNone; + +exit: + return error; +} + const Server::Service *Server::Host::FindNextService(const Service *aPrevService, Service::Flags aFlags, const char * aServiceName, @@ -1915,6 +1984,7 @@ Error Server::Host::MergeServicesAndResourcesFrom(Host &aHost) mAddresses.TakeFrom(static_cast &&>(aHost.mAddresses)); mKeyRecord = aHost.mKeyRecord; + mTtl = aHost.mTtl; mLease = aHost.mLease; mKeyLease = aHost.mKeyLease; mUpdateTime = TimerMilli::GetNow(); @@ -2028,6 +2098,7 @@ Server::UpdateMetadata::UpdateMetadata(Instance &aInstance, Host &aHost, const M , mExpireTime(TimerMilli::GetNow() + kDefaultEventsHandlerTimeout) , mDnsHeader(aMessageMetadata.mDnsHeader) , mId(Get().AllocateId()) + , mTtlConfig(aMessageMetadata.mTtlConfig) , mLeaseConfig(aMessageMetadata.mLeaseConfig) , mHost(aHost) , mIsDirectRxFromClient(aMessageMetadata.IsDirectRxFromClient()) diff --git a/src/core/net/srp_server.hpp b/src/core/net/srp_server.hpp index f31bd21b1..6fcc1d818 100644 --- a/src/core/net/srp_server.hpp +++ b/src/core/net/srp_server.hpp @@ -259,6 +259,14 @@ public: */ Error GetServiceSubTypeLabel(char *aLabel, uint8_t aMaxSize) const; + /** + * This method returns the TTL of the service instance. + * + * @returns The TTL of the service instance. + * + */ + uint32_t GetTtl(void) const { return mDescription->mTtl; } + /** * This method returns the port of the service instance. * @@ -390,6 +398,7 @@ public: uint16_t mPriority; uint16_t mWeight; uint16_t mPort; + uint32_t mTtl; // The TTL in seconds. uint32_t mLease; // The LEASE time in seconds. uint32_t mKeyLease; // The KEY-LEASE time in seconds. TimeMilli mUpdateTime; @@ -467,6 +476,14 @@ public: return mAddresses.AsCArray(); } + /** + * This method returns the TTL of the host. + * + * @returns The TTL of the host. + * + */ + uint32_t GetTtl(void) const { return mTtl; } + /** * This method returns the LEASE time of the host. * @@ -555,10 +572,13 @@ public: Host(Instance &aInstance, TimeMilli aUpdateTime); ~Host(void); - Error SetFullName(const char *aFullName); - void SetKeyRecord(Dns::Ecdsa256KeyRecord &aKeyRecord); - void SetLease(uint32_t aLease) { mLease = aLease; } - void SetKeyLease(uint32_t aKeyLease) { mKeyLease = aKeyLease; } + Error SetFullName(const char *aFullName); + void SetKeyRecord(Dns::Ecdsa256KeyRecord &aKeyRecord); + void SetTtl(uint32_t aTtl) { mTtl = aTtl; } + void SetLease(uint32_t aLease) { mLease = aLease; } + void SetKeyLease(uint32_t aKeyLease) { mKeyLease = aKeyLease; } + Error ProcessTtl(uint32_t aTtl); + LinkedList &GetServices(void) { return mServices; } Service * AddNewService(const char *aServiceName, const char *aInstanceName, @@ -582,12 +602,33 @@ public: // TODO(wgtdkp): there is no necessary to save the entire resource // record, saving only the ECDSA-256 public key should be enough. Dns::Ecdsa256KeyRecord mKeyRecord; + uint32_t mTtl; // The TTL in seconds. uint32_t mLease; // The LEASE time in seconds. uint32_t mKeyLease; // The KEY-LEASE time in seconds. TimeMilli mUpdateTime; LinkedList mServices; }; + /** + * This class handles TTL configuration. + * + */ + class TtlConfig : public otSrpServerTtlConfig + { + friend class Server; + + public: + /** + * This constructor initializes to default TTL configuration. + * + */ + TtlConfig(void); + + private: + bool IsValid(void) const { return mMinTtl <= mMaxTtl; } + uint32_t GrantTtl(uint32_t aLease, uint32_t aTtl) const; + }; + /** * This class handles LEASE and KEY-LEASE configurations. * @@ -760,6 +801,25 @@ public: */ void SetEnabled(bool aEnabled); + /** + * This method returns the TTL configuration. + * + * @param[out] aTtlConfig A reference to the `TtlConfig` instance. + * + */ + void GetTtlConfig(TtlConfig &aTtlConfig) const { aTtlConfig = mTtlConfig; } + + /** + * This method sets the TTL configuration. + * + * @param[in] aTtlConfig A reference to the `TtlConfig` instance. + * + * @retval kErrorNone Successfully set the TTL configuration + * @retval kErrorInvalidArgs The TTL range is not valid. + * + */ + Error SetTtlConfig(const TtlConfig &aTtlConfig); + /** * This method returns the LEASE and KEY-LEASE configurations. * @@ -814,6 +874,8 @@ public: private: static constexpr uint16_t kUdpPayloadSize = Ip6::kMaxDatagramLength - sizeof(Ip6::Udp::Header); + static constexpr uint32_t kDefaultMinTtl = 60u; // 1 min (in seconds). + static constexpr uint32_t kDefaultMaxTtl = 3600u * 2; // 2 hours (in seconds). static constexpr uint32_t kDefaultMinLease = 60u * 30; // 30 min (in seconds). static constexpr uint32_t kDefaultMaxLease = 3600u * 2; // 2 hours (in seconds). static constexpr uint32_t kDefaultMinKeyLease = 3600u * 24; // 1 day (in seconds). @@ -836,6 +898,7 @@ private: Dns::Zone mDnsZone; uint16_t mOffset; TimeMilli mRxTime; + TtlConfig mTtlConfig; LeaseConfig mLeaseConfig; const Ip6::MessageInfo *mMessageInfo; // Set to `nullptr` when from SRPL. }; @@ -853,6 +916,7 @@ private: TimeMilli GetExpireTime(void) const { return mExpireTime; } const Dns::UpdateHeader &GetDnsHeader(void) const { return mDnsHeader; } ServiceUpdateId GetId(void) const { return mId; } + const TtlConfig & GetTtlConfig(void) const { return mTtlConfig; } const LeaseConfig & GetLeaseConfig(void) const { return mLeaseConfig; } Host & GetHost(void) { return mHost; } const Ip6::MessageInfo & GetMessageInfo(void) const { return mMessageInfo; } @@ -866,6 +930,7 @@ private: TimeMilli mExpireTime; Dns::UpdateHeader mDnsHeader; ServiceUpdateId mId; // The ID of this service update transaction. + TtlConfig mTtlConfig; // TTL config to use when processing the message. LeaseConfig mLeaseConfig; // Lease config to use when processing the message. Host & mHost; // The `UpdateMetadata` has no ownership of this host. Ip6::MessageInfo mMessageInfo; // Valid when `mIsDirectRxFromClient` is true. @@ -894,10 +959,12 @@ private: Host & aHost, const Dns::UpdateHeader &aDnsHeader, const Ip6::MessageInfo * aMessageInfo, + const TtlConfig & aTtlConfig, const LeaseConfig & aLeaseConfig); Error ProcessMessage(Message &aMessage, const Ip6::MessageInfo &aMessageInfo); Error ProcessMessage(Message & aMessage, TimeMilli aRxTime, + const TtlConfig & aTtlConfig, const LeaseConfig & aLeaseConfig, const Ip6::MessageInfo *aMessageInfo); void ProcessDnsUpdate(Message &aMessage, MessageMetadata &aMetadata); @@ -951,6 +1018,7 @@ private: Heap::String mDomain; + TtlConfig mTtlConfig; LeaseConfig mLeaseConfig; LinkedList mHosts; @@ -971,6 +1039,7 @@ private: } // namespace Srp +DefineCoreType(otSrpServerTtlConfig, Srp::Server::TtlConfig); DefineCoreType(otSrpServerLeaseConfig, Srp::Server::LeaseConfig); DefineCoreType(otSrpServerHost, Srp::Server::Host); DefineCoreType(otSrpServerService, Srp::Server::Service); diff --git a/tests/scripts/thread-cert/Makefile.am b/tests/scripts/thread-cert/Makefile.am index 4b115e902..326869093 100644 --- a/tests/scripts/thread-cert/Makefile.am +++ b/tests/scripts/thread-cert/Makefile.am @@ -196,6 +196,7 @@ EXTRA_DIST = \ test_srp_server_anycast_mode.py \ test_srp_server_reboot_port.py \ test_srp_sub_type.py \ + test_srp_ttl.py \ test_zero_len_external_route.py \ thread_cert.py \ tlvs_parsing.py \ @@ -270,6 +271,7 @@ check_SCRIPTS = \ test_srp_server_anycast_mode.py \ test_srp_server_reboot_port.py \ test_srp_sub_type.py \ + test_srp_ttl.py \ test_zero_len_external_route.py \ Cert_5_1_01_RouterAttach.py \ Cert_5_1_02_ChildAddressTimeout.py \ diff --git a/tests/scripts/thread-cert/node.py b/tests/scripts/thread-cert/node.py index add478fa5..f2cf62b98 100755 --- a/tests/scripts/thread-cert/node.py +++ b/tests/scripts/thread-cert/node.py @@ -929,6 +929,10 @@ class NodeImpl: self.send_command(f'srp server lease {min_lease} {max_lease} {min_key_lease} {max_key_lease}') self._expect_done() + def srp_server_set_ttl_range(self, min_ttl, max_ttl): + self.send_command(f'srp server ttl {min_ttl} {max_ttl}') + self._expect_done() + def srp_server_get_hosts(self): """Returns the host list on the SRP server as a list of property dictionary. @@ -989,6 +993,7 @@ class NodeImpl: 'port': '12345', 'priority': '0', 'weight': '0', + 'ttl': '7200', 'TXT': ['abc=010203'], 'host_fullname': 'my-host.default.service.arpa.', 'host': 'my-host', @@ -1015,8 +1020,8 @@ class NodeImpl: service_list.append(service) continue - # 'subtypes', port', 'priority', 'weight' - for i in range(0, 4): + # 'subtypes', port', 'priority', 'weight', 'ttl' + for i in range(0, 5): key_value = lines.pop(0).strip().split(':') service[key_value[0].strip()] = key_value[1].strip() diff --git a/tests/scripts/thread-cert/test_srp_ttl.py b/tests/scripts/thread-cert/test_srp_ttl.py new file mode 100755 index 000000000..5ea55ef5c --- /dev/null +++ b/tests/scripts/thread-cert/test_srp_ttl.py @@ -0,0 +1,151 @@ +#!/usr/bin/env python3 +# +# Copyright (c) 2022, The OpenThread Authors. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. Neither the name of the copyright holder nor the +# names of its contributors may be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. +# + +import ipaddress +import unittest + +import command +import config +import thread_cert + +# Test description: +# This test verifies the SRP server and client properly handle SRP host +# and service instance TTLs. +# +# Topology: +# LEADER (SRP server) +# | +# | +# ROUTER (SRP client) +# + +SERVER = 1 +CLIENT = 2 +KEY_LEASE = 240 # Seconds + + +class SrpTtl(thread_cert.TestCase): + USE_MESSAGE_FACTORY = False + SUPPORT_NCP = False + + TOPOLOGY = { + SERVER: { + 'name': 'SRP_SERVER', + 'mode': 'rdn', + }, + CLIENT: { + 'name': 'SRP_CLIENT', + 'mode': 'rdn', + }, + } + + def test(self): + server = self.nodes[SERVER] + client = self.nodes[CLIENT] + + # + # Start the server and client devices. + # + + server.srp_server_set_enabled(True) + server.srp_server_set_lease_range(120, 240, KEY_LEASE, KEY_LEASE) + server.start() + self.simulator.go(5) + self.assertEqual(server.get_state(), 'leader') + self.simulator.go(5) + + client.srp_server_set_enabled(False) + client.start() + self.simulator.go(config.ROUTER_STARTUP_DELAY) + self.assertEqual(client.get_state(), 'router') + + client.srp_client_set_host_name('my-host') + client.srp_client_set_host_address('2001::1') + client.srp_client_start(server.get_addrs()[0], client.get_srp_server_port()) + client.srp_client_add_service('my-service', '_ipps._tcp', 12345) + self.simulator.go(2) + + # + # CLIENT_TTL < TTL_MIN < LEASE_MAX ==> TTL_MIN + # + + client.srp_client_set_ttl(100) + server.srp_server_set_ttl_range(120, 240) + server.srp_server_set_lease_range(120, 240, KEY_LEASE, KEY_LEASE) + self.simulator.go(KEY_LEASE) + self.check_ttl(120) + + # + # TTL_MIN < CLIENT_TTL < TTL_MAX < LEASE_MAX ==> CLIENT_TTL + # + + client.srp_client_set_ttl(100) + server.srp_server_set_ttl_range(60, 120) + server.srp_server_set_lease_range(120, 240, KEY_LEASE, KEY_LEASE) + self.simulator.go(KEY_LEASE) + self.check_ttl(100) + + # + # TTL_MAX < LEASE_MAX < CLIENT_TTL ==> TTL_MAX + # + + client.srp_client_set_ttl(240) + server.srp_server_set_ttl_range(60, 120) + server.srp_server_set_lease_range(120, 240, KEY_LEASE, KEY_LEASE) + self.simulator.go(KEY_LEASE) + self.check_ttl(120) + + # + # LEASE_MAX < TTL_MAX < CLIENT_TTL ==> LEASE_MAX + # + + client.srp_client_set_ttl(240) + server.srp_server_set_ttl_range(60, 120) + server.srp_server_set_lease_range(30, 60, KEY_LEASE, KEY_LEASE) + self.simulator.go(KEY_LEASE) + self.check_ttl(60) + + def check_ttl(self, ttl): + """Check that we have properly registered host and service instance. + """ + + server = self.nodes[SERVER] + + server_services = server.srp_server_get_services() + print(server_services) + self.assertEqual(len(server_services), 1) + server_service = server_services[0] + + # Verify that the server accepted the SRP registration and stored + # the same service resources. + self.assertEqual(int(server_service['ttl']), ttl) + + +if __name__ == '__main__': + unittest.main() diff --git a/tests/toranj/cli/cli.py b/tests/toranj/cli/cli.py index 184271b09..5e24bedec 100644 --- a/tests/toranj/cli/cli.py +++ b/tests/toranj/cli/cli.py @@ -458,6 +458,7 @@ class Node(object): 'port': '12345', 'priority': '0', 'weight': '0', + 'ttl': '7200', 'TXT': ['abc=010203'], 'host_fullname': 'my-host.default.service.arpa.', 'host': 'my-host', @@ -478,8 +479,8 @@ class Node(object): if service['deleted'] == 'true': service_list.append(service) continue - # 'subtypes', port', 'priority', 'weight' - for i in range(0, 4): + # 'subtypes', port', 'priority', 'weight', 'ttl' + for i in range(0, 5): key_value = outputs.pop(0).strip().split(':') service[key_value[0].strip()] = key_value[1].strip() txt_entries = outputs.pop(0).strip().split('[')[1].strip(' ]').split(',') diff --git a/tools/otci/otci/otci.py b/tools/otci/otci/otci.py index 710400e0b..da6ef6135 100644 --- a/tools/otci/otci/otci.py +++ b/tools/otci/otci/otci.py @@ -976,7 +976,7 @@ class OTCI(object): info['addresses'] = list(map(Ip6Addr, v.split(', '))) elif k == 'subtypes': info[k] = list() if v == '(null)' else list(v.split(',')) - elif k in ('port', 'weight', 'priority'): + elif k in ('port', 'weight', 'priority', 'ttl'): info[k] = int(v) elif k in ('host',): info[k] = v From ed665e95a73e40db486f0d52a3dbd2f86243de5d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Duda?= Date: Wed, 1 Jun 2022 22:47:13 +0200 Subject: [PATCH 48/80] [platform] update documentation of otPlatSettings API (#7774) Signed-off-by: Lukasz Duda --- include/openthread/instance.h | 2 +- include/openthread/platform/settings.h | 167 ++++++++++++++----------- 2 files changed, 97 insertions(+), 72 deletions(-) diff --git a/include/openthread/instance.h b/include/openthread/instance.h index 877e3267a..ae935f154 100644 --- a/include/openthread/instance.h +++ b/include/openthread/instance.h @@ -53,7 +53,7 @@ extern "C" { * @note This number versions both OpenThread platform and user APIs. * */ -#define OPENTHREAD_API_VERSION (215) +#define OPENTHREAD_API_VERSION (216) /** * @addtogroup api-instance diff --git a/include/openthread/platform/settings.h b/include/openthread/platform/settings.h index dd6c41f84..2a4892baa 100644 --- a/include/openthread/platform/settings.h +++ b/include/openthread/platform/settings.h @@ -108,80 +108,100 @@ void otPlatSettingsInit(otInstance *aInstance, const uint16_t *aSensitiveKeys, u */ void otPlatSettingsDeinit(otInstance *aInstance); -/// Fetches the value of a setting -/** This function fetches the value of the setting identified - * by aKey and write it to the memory pointed to by aValue. - * It then writes the length to the integer pointed to by - * aValueLength. The initial value of aValueLength is the - * maximum number of bytes to be written to aValue. +/** + * Fetches the value of a setting. * - * This function can be used to check for the existence of - * a key without fetching the value by setting aValue and - * aValueLength to NULL. You can also check the length of - * the setting without fetching it by setting only aValue - * to NULL. + * This function fetches the value of the setting identified + * by @p aKey and write it to the memory pointed to by aValue. + * It then writes the length to the integer pointed to by + * @p aValueLength. The initial value of @p aValueLength is the + * maximum number of bytes to be written to @p aValue. * - * Note that the underlying storage implementation is not - * required to maintain the order of settings with multiple - * values. The order of such values MAY change after ANY - * write operation to the store. + * This function can be used to check for the existence of + * a key without fetching the value by setting @p aValue and + * @p aValueLength to NULL. You can also check the length of + * the setting without fetching it by setting only aValue + * to NULL. * - * @param[in] aInstance The OpenThread instance structure. - * @param[in] aKey The key associated with the requested setting. - * @param[in] aIndex The index of the specific item to get. - * @param[out] aValue A pointer to where the value of the setting should be written. May be set to NULL if - * just testing for the presence or length of a setting. - * @param[in,out] aValueLength A pointer to the length of the value. When called, this pointer should point to an - * integer containing the maximum value size that can be written to aValue. At return, - * the actual length of the setting is written. This may be set to NULL if performing - * a presence check. + * Note that the underlying storage implementation is not + * required to maintain the order of settings with multiple + * values. The order of such values MAY change after ANY + * write operation to the store. * - * @retval OT_ERROR_NONE The given setting was found and fetched successfully. - * @retval OT_ERROR_NOT_FOUND The given setting was not found in the setting store. - * @retval OT_ERROR_NOT_IMPLEMENTED This function is not implemented on this platform. + * @param[in] aInstance The OpenThread instance structure. + * @param[in] aKey The key associated with the requested setting. + * @param[in] aIndex The index of the specific item to get. + * @param[out] aValue A pointer to where the value of the setting should be written. May be set to NULL if + * just testing for the presence or length of a setting. + * @param[in,out] aValueLength A pointer to the length of the value. When called, this pointer should point to an + * integer containing the maximum value size that can be written to @p aValue. At return, + * the actual length of the setting is written. This may be set to NULL if performing + * a presence check. + * + * @retval OT_ERROR_NONE The given setting was found and fetched successfully. + * @retval OT_ERROR_NOT_FOUND The given setting was not found in the setting store. + * @retval OT_ERROR_NOT_IMPLEMENTED This function is not implemented on this platform. */ otError otPlatSettingsGet(otInstance *aInstance, uint16_t aKey, int aIndex, uint8_t *aValue, uint16_t *aValueLength); -/// Sets or replaces the value of a setting -/** This function sets or replaces the value of a setting - * identified by aKey. If there was more than one - * value previously associated with aKey, then they are - * all deleted and replaced with this single entry. +/** + * Sets or replaces the value of a setting. * - * Calling this function successfully may cause unrelated - * settings with multiple values to be reordered. + * This function sets or replaces the value of a setting + * identified by @p aKey. * - * @param[in] aInstance The OpenThread instance structure. - * @param[in] aKey The key associated with the setting to change. - * @param[in] aValue A pointer to where the new value of the setting should be read from. MUST NOT be NULL if - * aValueLength is non-zero. - * @param[in] aValueLength The length of the data pointed to by aValue. May be zero. + * Calling this function successfully may cause unrelated + * settings with multiple values to be reordered. * - * @retval OT_ERROR_NONE The given setting was changed or staged. - * @retval OT_ERROR_NOT_IMPLEMENTED This function is not implemented on this platform. - * @retval OT_ERROR_NO_BUFS No space remaining to store the given setting. + * OpenThread stack guarantees to use `otPlatSettingsSet()` + * method for a @p aKey that was either previously set using + * `otPlatSettingsSet()` (i.e., contains a single value) or + * is empty and/or fully deleted (contains no value). + * + * Platform layer can rely and use this fact for optimizing + * its implementation. + * + * @param[in] aInstance The OpenThread instance structure. + * @param[in] aKey The key associated with the setting to change. + * @param[in] aValue A pointer to where the new value of the setting should be read from. MUST NOT be NULL if + * @p aValueLength is non-zero. + * @param[in] aValueLength The length of the data pointed to by aValue. May be zero. + * + * @retval OT_ERROR_NONE The given setting was changed or staged. + * @retval OT_ERROR_NOT_IMPLEMENTED This function is not implemented on this platform. + * @retval OT_ERROR_NO_BUFS No space remaining to store the given setting. */ otError otPlatSettingsSet(otInstance *aInstance, uint16_t aKey, const uint8_t *aValue, uint16_t aValueLength); -/// Adds a value to a setting -/** This function adds the value to a setting - * identified by aKey, without replacing any existing - * values. +/** + * Adds a value to a setting. * - * Note that the underlying implementation is not required - * to maintain the order of the items associated with a - * specific key. The added value may be added to the end, - * the beginning, or even somewhere in the middle. The order - * of any pre-existing values may also change. + * This function adds the value to a setting + * identified by @p aKey, without replacing any existing + * values. * - * Calling this function successfully may cause unrelated - * settings with multiple values to be reordered. + * Note that the underlying implementation is not required + * to maintain the order of the items associated with a + * specific key. The added value may be added to the end, + * the beginning, or even somewhere in the middle. The order + * of any pre-existing values may also change. + * + * Calling this function successfully may cause unrelated + * settings with multiple values to be reordered. + * + * OpenThread stack guarantees to use `otPlatSettingsAdd()` + * method for a @p aKey that was either previously managed by + * `otPlatSettingsAdd()` (i.e., contains one or more items) or + * is empty and/or fully deleted (contains no value). + * + * Platform layer can rely and use this fact for optimizing + * its implementation. * * @param[in] aInstance The OpenThread instance structure. * @param[in] aKey The key associated with the setting to change. * @param[in] aValue A pointer to where the new value of the setting should be read from. MUST NOT be NULL - * if aValueLength is non-zero. - * @param[in] aValueLength The length of the data pointed to by aValue. May be zero. + * if @p aValueLength is non-zero. + * @param[in] aValueLength The length of the data pointed to by @p aValue. May be zero. * * @retval OT_ERROR_NONE The given setting was added or staged to be added. * @retval OT_ERROR_NOT_IMPLEMENTED This function is not implemented on this platform. @@ -189,29 +209,34 @@ otError otPlatSettingsSet(otInstance *aInstance, uint16_t aKey, const uint8_t *a */ otError otPlatSettingsAdd(otInstance *aInstance, uint16_t aKey, const uint8_t *aValue, uint16_t aValueLength); -/// Removes a setting from the setting store -/** This function deletes a specific value from the - * setting identified by aKey from the settings store. +/** + * Removes a setting from the setting store. * - * Note that the underlying implementation is not required - * to maintain the order of the items associated with a - * specific key. + * This function deletes a specific value from the + * setting identified by aKey from the settings store. * - * @param[in] aInstance The OpenThread instance structure. - * @param[in] aKey The key associated with the requested setting. - * @param[in] aIndex The index of the value to be removed. If set to -1, all values for this aKey will be removed. + * Note that the underlying implementation is not required + * to maintain the order of the items associated with a + * specific key. * - * @retval OT_ERROR_NONE The given key and index was found and removed successfully. - * @retval OT_ERROR_NOT_FOUND The given key or index was not found in the setting store. - * @retval OT_ERROR_NOT_IMPLEMENTED This function is not implemented on this platform. + * @param[in] aInstance The OpenThread instance structure. + * @param[in] aKey The key associated with the requested setting. + * @param[in] aIndex The index of the value to be removed. If set to -1, all values for this @p aKey will be + * removed. + * + * @retval OT_ERROR_NONE The given key and index was found and removed successfully. + * @retval OT_ERROR_NOT_FOUND The given key or index was not found in the setting store. + * @retval OT_ERROR_NOT_IMPLEMENTED This function is not implemented on this platform. */ otError otPlatSettingsDelete(otInstance *aInstance, uint16_t aKey, int aIndex); -/// Removes all settings from the setting store -/** This function deletes all settings from the settings - * store, resetting it to its initial factory state. +/** + * Removes all settings from the setting store. * - * @param[in] aInstance The OpenThread instance structure. + * This function deletes all settings from the settings + * store, resetting it to its initial factory state. + * + * @param[in] aInstance The OpenThread instance structure. */ void otPlatSettingsWipe(otInstance *aInstance); From 8f92d2dc81243f62d6cd609956cdb0120273272d Mon Sep 17 00:00:00 2001 From: hemanth-silabs <50145824+hemanth-silabs@users.noreply.github.com> Date: Thu, 2 Jun 2022 00:50:01 +0100 Subject: [PATCH 49/80] [mac] enable Beacon Payload in outgoing beacons (#7767) This commit enables beacon payloads on outgoing beacons to prevent backward compatibility issues with existing devices in the market. --- script/check-simulation-build-autotools | 1 + src/core/config/mac.h | 11 ++++ src/core/mac/mac.cpp | 28 ++++++++++ src/core/mac/mac_frame.hpp | 73 +++++++++++++++++++++---- 4 files changed, 103 insertions(+), 10 deletions(-) diff --git a/script/check-simulation-build-autotools b/script/check-simulation-build-autotools index c08a266f5..086f2688c 100755 --- a/script/check-simulation-build-autotools +++ b/script/check-simulation-build-autotools @@ -90,6 +90,7 @@ build_all_features() "-DOPENTHREAD_CONFIG_TMF_NETWORK_DIAG_MTD_ENABLE=1" "-DOPENTHREAD_CONFIG_UDP_FORWARD_ENABLE=1" "-DOPENTHREAD_CONFIG_MAC_BEACON_PAYLOAD_PARSING_ENABLE=1" + "-DOPENTHREAD_CONFIG_MAC_OUTGOING_BEACON_PAYLOAD_ENABLE=1" ) local options_1_2=( diff --git a/src/core/config/mac.h b/src/core/config/mac.h index 0af268449..d44ff112c 100644 --- a/src/core/config/mac.h +++ b/src/core/config/mac.h @@ -510,4 +510,15 @@ #define OPENTHREAD_CONFIG_MAC_BEACON_PAYLOAD_PARSING_ENABLE 0 #endif +/** + * @def OPENTHREAD_CONFIG_MAC_OUTGOING_BEACON_PAYLOAD_ENABLE + * + * This setting configures if the beacon payload needs to be enabled in outgoing beacon frames. This is optional and is + * disabled by default because Thread 1.2.1 has removed support for beacon payloads. + * + */ +#ifndef OPENTHREAD_CONFIG_MAC_OUTGOING_BEACON_PAYLOAD_ENABLE +#define OPENTHREAD_CONFIG_MAC_OUTGOING_BEACON_PAYLOAD_ENABLE 0 +#endif + #endif // CONFIG_MAC_H_ diff --git a/src/core/mac/mac.cpp b/src/core/mac/mac.cpp index b42300d83..6c99c573c 100644 --- a/src/core/mac/mac.cpp +++ b/src/core/mac/mac.cpp @@ -737,6 +737,10 @@ TxFrame *Mac::PrepareBeacon(void) TxFrame *frame; uint16_t fcf; Beacon * beacon = nullptr; +#if OPENTHREAD_CONFIG_MAC_OUTGOING_BEACON_PAYLOAD_ENABLE + uint8_t beaconLength; + BeaconPayload *beaconPayload = nullptr; +#endif #if OPENTHREAD_CONFIG_MULTI_RADIO OT_ASSERT(!mTxBeaconRadioLinks.IsEmpty()); @@ -754,6 +758,30 @@ TxFrame *Mac::PrepareBeacon(void) beacon = reinterpret_cast(frame->GetPayload()); beacon->Init(); +#if OPENTHREAD_CONFIG_MAC_OUTGOING_BEACON_PAYLOAD_ENABLE + beaconLength = sizeof(*beacon); + + beaconPayload = reinterpret_cast(beacon->GetPayload()); + + beaconPayload->Init(); + + if (IsJoinable()) + { + beaconPayload->SetJoiningPermitted(); + } + else + { + beaconPayload->ClearJoiningPermitted(); + } + + beaconPayload->SetNetworkName(Get().GetNetworkName().GetAsData()); + beaconPayload->SetExtendedPanId(Get().GetExtPanId()); + + beaconLength += sizeof(*beaconPayload); + + frame->SetPayloadLength(beaconLength); +#endif + LogBeacon("Sending"); return frame; diff --git a/src/core/mac/mac_frame.hpp b/src/core/mac/mac_frame.hpp index 10de014ed..9ffe2d57c 100644 --- a/src/core/mac/mac_frame.hpp +++ b/src/core/mac/mac_frame.hpp @@ -43,6 +43,7 @@ #include "common/const_cast.hpp" #include "common/encoding.hpp" #include "mac/mac_types.hpp" +#include "meshcop/network_name.hpp" namespace ot { namespace Mac { @@ -1482,7 +1483,6 @@ private: uint8_t mPendingAddressSpec; } OT_TOOL_PACKED_END; -#if OPENTHREAD_CONFIG_MAC_BEACON_PAYLOAD_PARSING_ENABLE /** * This class implements IEEE 802.15.4 Beacon Payload generation and parsing. * @@ -1491,16 +1491,22 @@ OT_TOOL_PACKED_BEGIN class BeaconPayload { public: - static constexpr uint8_t kProtocolId = 3; ///< Thread Protocol ID. - static constexpr uint8_t kVersionOffset = 4; ///< Version field bit offset. - static constexpr uint8_t kNativeFlag = 1 << 3; ///< Native Commissioner flag. - static constexpr uint8_t kJoiningFlag = 1 << 0; ///< Joining Permitted flag. + static constexpr uint8_t kProtocolId = 3; ///< Thread Protocol ID. + static constexpr uint8_t kProtocolVersion = 2; ///< Thread Protocol version. + static constexpr uint8_t kVersionOffset = 4; ///< Version field bit offset. + static constexpr uint8_t kVersionMask = 0xf << kVersionOffset; ///< Version field mask. + static constexpr uint8_t kNativeFlag = 1 << 3; ///< Native Commissioner flag. + static constexpr uint8_t kJoiningFlag = 1 << 0; ///< Joining Permitted flag. /** - * This constant specified the maximum number of chars in Network Name (excludes null char). + * This method initializes the Beacon Payload. * */ - static constexpr uint8_t kMaxSize = OT_NETWORK_NAME_MAX_SIZE; + void Init(void) + { + mProtocolId = kProtocolId; + mFlags = kProtocolVersion << kVersionOffset; + } /** * This method indicates whether or not the beacon appears to be a valid Thread Beacon Payload. @@ -1536,6 +1542,18 @@ public: */ bool IsNative(void) const { return (mFlags & kNativeFlag) != 0; } + /** + * This method clears the Native Commissioner flag. + * + */ + void ClearNative(void) { mFlags &= ~kNativeFlag; } + + /** + * This method sets the Native Commissioner flag. + * + */ + void SetNative(void) { mFlags |= kNativeFlag; } + /** * This method indicates whether or not the Joining Permitted flag is set. * @@ -1545,13 +1563,41 @@ public: */ bool IsJoiningPermitted(void) const { return (mFlags & kJoiningFlag) != 0; } + /** + * This method clears the Joining Permitted flag. + * + */ + void ClearJoiningPermitted(void) { mFlags &= ~kJoiningFlag; } + + /** + * This method sets the Joining Permitted flag. + * + */ + void SetJoiningPermitted(void) + { + mFlags |= kJoiningFlag; + +#if OPENTHREAD_CONFIG_MAC_JOIN_BEACON_VERSION != 2 // check against kProtocolVersion + mFlags &= ~kVersionMask; + mFlags |= OPENTHREAD_CONFIG_MAC_JOIN_BEACON_VERSION << kVersionOffset; +#endif + } + /** * This method gets the Network Name field. * * @returns The Network Name field as `NameData`. * */ - const char *GetNetworkName(void) const { return mNetworkName; } + MeshCoP::NameData GetNetworkName(void) const { return MeshCoP::NameData(mNetworkName, sizeof(mNetworkName)); } + + /** + * This method sets the Network Name field. + * + * @param[in] aNameData The Network Name (as a `NameData`). + * + */ + void SetNetworkName(const MeshCoP::NameData &aNameData) { aNameData.CopyTo(mNetworkName, sizeof(mNetworkName)); } /** * This method returns the Extended PAN ID field. @@ -1561,13 +1607,20 @@ public: */ const otExtendedPanId &GetExtendedPanId(void) const { return mExtendedPanId; } + /** + * This method sets the Extended PAN ID field. + * + * @param[in] aExtPanId An Extended PAN ID. + * + */ + void SetExtendedPanId(const otExtendedPanId &aExtPanId) { mExtendedPanId = aExtPanId; } + private: uint8_t mProtocolId; uint8_t mFlags; - char mNetworkName[kMaxSize]; + char mNetworkName[MeshCoP::NetworkName::kMaxSize]; otExtendedPanId mExtendedPanId; } OT_TOOL_PACKED_END; -#endif // OPENTHREAD_CONFIG_MAC_BEACON_PAYLOAD_PARSING_ENABLE /** * This class implements CSL IE data structure. From 9bb09e74e2bfddb8e5272cc8ade6e0fa4f8125f0 Mon Sep 17 00:00:00 2001 From: jinran-google Date: Fri, 3 Jun 2022 00:04:14 +0800 Subject: [PATCH 50/80] [mle] add API to detach gracefully (#7666) This commit adds an API `otThreadDetachGracefully` to notify other nodes in the network (if any) and then stop Thread protocol operation. It sends an Address Release if it's a router, or sets its child timeout to 0 if it's a child. --- include/openthread/instance.h | 2 +- include/openthread/thread.h | 26 ++++ src/cli/cli.cpp | 31 ++++ src/cli/cli.hpp | 3 + src/core/api/thread_api.cpp | 5 + src/core/thread/mle.cpp | 101 ++++++++++++- src/core/thread/mle.hpp | 40 +++++ src/core/thread/mle_router.cpp | 5 +- src/core/thread/mle_router.hpp | 10 +- tests/scripts/thread-cert/Makefile.am | 2 + tests/scripts/thread-cert/node.py | 24 +++ tests/scripts/thread-cert/test_detach.py | 181 +++++++++++++++++++++++ 12 files changed, 423 insertions(+), 7 deletions(-) create mode 100755 tests/scripts/thread-cert/test_detach.py diff --git a/include/openthread/instance.h b/include/openthread/instance.h index ae935f154..02e5b89b7 100644 --- a/include/openthread/instance.h +++ b/include/openthread/instance.h @@ -53,7 +53,7 @@ extern "C" { * @note This number versions both OpenThread platform and user APIs. * */ -#define OPENTHREAD_API_VERSION (216) +#define OPENTHREAD_API_VERSION (217) /** * @addtogroup api-instance diff --git a/include/openthread/thread.h b/include/openthread/thread.h index fe829003c..04cb747f1 100644 --- a/include/openthread/thread.h +++ b/include/openthread/thread.h @@ -194,11 +194,22 @@ typedef struct otThreadParentResponseInfo bool mIsAttached; ///< Is the node receiving parent response attached } otThreadParentResponseInfo; +/** + * This callback informs the application that the detaching process has finished. + * + * @param[in] aContext A pointer to application-specific context. + * + */ +typedef void (*otDetachGracefullyCallback)(void *aContext); + /** * This function starts Thread protocol operation. * * The interface must be up when calling this function. * + * Calling this function with @p aEnabled set to FALSE stops any ongoing processes of detaching started by + * otThreadDetachGracefully(). Its callback will be called. + * * @param[in] aInstance A pointer to an OpenThread instance. * @param[in] aEnabled TRUE if Thread is enabled, FALSE otherwise. * @@ -1009,6 +1020,21 @@ otError otThreadSendProactiveBackboneNotification(otInstance * aIns otIp6InterfaceIdentifier *aMlIid, uint32_t aTimeSinceLastTransaction); +/** + * This function notifies other nodes in the network (if any) and then stops Thread protocol operation. + * + * It sends an Address Release if it's a router, or sets its child timeout to 0 if it's a child. + * + * @param[in] aInstance A pointer to an OpenThread instance. + * @param[in] aCallback A pointer to a function that is called upon finishing detaching. + * @param[in] aContext A pointer to callback application-specific context. + * + * @retval OT_ERROR_NONE Successfully started detaching. + * @retval OT_ERROR_BUSY Detaching is already in progress. + * + */ +otError otThreadDetachGracefully(otInstance *aInstance, otDetachGracefullyCallback aCallback, void *aContext); + /** * @} * diff --git a/src/cli/cli.cpp b/src/cli/cli.cpp index ef5376ff3..b1f250ffd 100644 --- a/src/cli/cli.cpp +++ b/src/cli/cli.cpp @@ -1453,6 +1453,25 @@ exit: } #endif +template <> otError Interpreter::Process(Arg aArgs[]) +{ + otError error = OT_ERROR_NONE; + + if (aArgs[0] == "async") + { + SuccessOrExit(error = otThreadDetachGracefully(GetInstancePtr(), nullptr, nullptr)); + } + else + { + SuccessOrExit(error = + otThreadDetachGracefully(GetInstancePtr(), &Interpreter::HandleDetachGracefullyResult, this)); + error = OT_ERROR_PENDING; + } + +exit: + return error; +} + template <> otError Interpreter::Process(Arg aArgs[]) { otError error = OT_ERROR_NONE; @@ -4903,6 +4922,17 @@ void Interpreter::OutputChildTableEntry(uint8_t aIndentSize, const otNetworkDiag } #endif // OPENTHREAD_FTD || OPENTHREAD_CONFIG_TMF_NETWORK_DIAG_MTD_ENABLE +void Interpreter::HandleDetachGracefullyResult(void *aContext) +{ + static_cast(aContext)->HandleDetachGracefullyResult(); +} + +void Interpreter::HandleDetachGracefullyResult(void) +{ + OutputLine("Finished detaching"); + OutputResult(OT_ERROR_NONE); +} + void Interpreter::HandleDiscoveryRequest(const otThreadDiscoveryRequestInfo &aInfo) { OutputFormat("~ Discovery Request from "); @@ -5017,6 +5047,7 @@ otError Interpreter::ProcessCommand(Arg aArgs[]) #if OPENTHREAD_FTD CmdEntry("delaytimermin"), #endif + CmdEntry("detach"), #endif // OPENTHREAD_FTD || OPENTHREAD_MTD #if OPENTHREAD_CONFIG_DIAG_ENABLE CmdEntry("diag"), diff --git a/src/cli/cli.hpp b/src/cli/cli.hpp index 90144d196..196703a9e 100644 --- a/src/cli/cli.hpp +++ b/src/cli/cli.hpp @@ -452,6 +452,9 @@ private: const char *LinkMetricsStatusToStr(uint8_t aStatus); #endif // OPENTHREAD_CONFIG_MLE_LINK_METRICS_INITIATOR_ENABLE + static void HandleDetachGracefullyResult(void *aContext); + void HandleDetachGracefullyResult(void); + static void HandleDiscoveryRequest(const otThreadDiscoveryRequestInfo *aInfo, void *aContext) { static_cast(aContext)->HandleDiscoveryRequest(*aInfo); diff --git a/src/core/api/thread_api.cpp b/src/core/api/thread_api.cpp index ca38757e8..bec8c0633 100644 --- a/src/core/api/thread_api.cpp +++ b/src/core/api/thread_api.cpp @@ -497,4 +497,9 @@ bool otThreadIsAnycastLocateInProgress(otInstance *aInstance) } #endif +otError otThreadDetachGracefully(otInstance *aInstance, otDetachGracefullyCallback aCallback, void *aContext) +{ + return AsCoreType(aInstance).Get().DetachGracefully(aCallback, aContext); +} + #endif // OPENTHREAD_FTD || OPENTHREAD_MTD diff --git a/src/core/thread/mle.cpp b/src/core/thread/mle.cpp index 8e3a646cb..99b93ef05 100644 --- a/src/core/thread/mle.cpp +++ b/src/core/thread/mle.cpp @@ -81,7 +81,10 @@ Mle::Mle(Instance &aInstance) , mAttachTimer(aInstance, Mle::HandleAttachTimer) , mDelayedResponseTimer(aInstance, Mle::HandleDelayedResponseTimer) , mMessageTransmissionTimer(aInstance, Mle::HandleMessageTransmissionTimer) + , mDetachGracefullyTimer(aInstance, Mle::HandleDetachGracefullyTimer) , mParentLeaderCost(0) + , mDetachGracefullyCallback(nullptr) + , mDetachGracefullyContext(nullptr) , mAttachMode(kAnyPartition) , mParentPriority(0) , mParentLinkQuality3(0) @@ -259,7 +262,18 @@ void Mle::Stop(StopMode aMode) SetRole(kRoleDisabled); exit: - return; + mDetachGracefullyTimer.Stop(); + + if (mDetachGracefullyCallback != nullptr) + { + otDetachGracefullyCallback callback = mDetachGracefullyCallback; + void * context = mDetachGracefullyContext; + + mDetachGracefullyCallback = nullptr; + mDetachGracefullyContext = nullptr; + + callback(context); + } } void Mle::SetRole(DeviceRole aRole) @@ -2422,6 +2436,11 @@ exit: } Error Mle::SendChildUpdateRequest(void) +{ + return SendChildUpdateRequest(mTimeout); +} + +Error Mle::SendChildUpdateRequest(uint32_t aTimeout) { Error error = kErrorNone; Ip6::Address destination; @@ -2452,7 +2471,7 @@ Error Mle::SendChildUpdateRequest(void) case kRoleChild: SuccessOrExit(error = message->AppendSourceAddressTlv()); SuccessOrExit(error = message->AppendLeaderDataTlv()); - SuccessOrExit(error = message->AppendTimeoutTlv(mTimeout)); + SuccessOrExit(error = message->AppendTimeoutTlv(aTimeout)); #if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE if (Get().IsCslEnabled()) { @@ -4017,7 +4036,14 @@ void Mle::HandleChildUpdateResponse(RxInfo &aRxInfo) switch (Tlv::Find(aRxInfo.mMessage, timeout)) { case kErrorNone: - mTimeout = timeout; + if (timeout == 0 && IsDetachingGracefully()) + { + Stop(); + } + else + { + mTimeout = timeout; + } break; case kErrorNotFound: break; @@ -4743,5 +4769,74 @@ void Mle::DelayedResponseMetadata::RemoveFrom(Message &aMessage) const SuccessOrAssert(aMessage.SetLength(aMessage.GetLength() - sizeof(*this))); } +Error Mle::DetachGracefully(otDetachGracefullyCallback aCallback, void *aContext) +{ + Error error = kErrorNone; + + VerifyOrExit(!IsDetachingGracefully(), error = kErrorBusy); + + OT_ASSERT(mDetachGracefullyCallback == nullptr); + + mDetachGracefullyCallback = aCallback; + mDetachGracefullyContext = aContext; + + if (IsChild() || IsRouter()) + { + mDetachGracefullyTimer.Start(kDetachGracefullyTimeout); + } + else + { + // If the device is a leader, or it's already detached or disabled, we start the timer with zero duration to + // stop and invoke the callback when the timer fires, so the operation finishes immediately and asynchronously. + mDetachGracefullyTimer.Start(0); + } + + if (IsChild()) + { + IgnoreError(SendChildUpdateRequest(/*aTimeout=*/0)); + } +#if OPENTHREAD_FTD + else if (IsRouter()) + { + Get().SendAddressRelease(&Mle::HandleDetachGracefullyAddressReleaseResponse, this); + } +#endif + +exit: + return error; +} + +void Mle::HandleDetachGracefullyTimer(Timer &aTimer) +{ + aTimer.Get().HandleDetachGracefullyTimer(); +} + +void Mle::HandleDetachGracefullyTimer(void) +{ + Stop(); +} + +#if OPENTHREAD_FTD +void Mle::HandleDetachGracefullyAddressReleaseResponse(void * aContext, + otMessage * aMessage, + const otMessageInfo *aMessageInfo, + Error aResult) +{ + OT_UNUSED_VARIABLE(aMessage); + OT_UNUSED_VARIABLE(aMessageInfo); + OT_UNUSED_VARIABLE(aResult); + + static_cast(aContext)->HandleDetachGracefullyAddressReleaseResponse(); +} + +void Mle::HandleDetachGracefullyAddressReleaseResponse(void) +{ + if (IsDetachingGracefully()) + { + Stop(); + } +} +#endif // OPENTHREAD_FTD + } // namespace Mle } // namespace ot diff --git a/src/core/thread/mle.hpp b/src/core/thread/mle.hpp index ff3c173e8..ddd8b91cd 100644 --- a/src/core/thread/mle.hpp +++ b/src/core/thread/mle.hpp @@ -186,6 +186,20 @@ public: */ Error BecomeChild(void); + /** + * This function notifies other nodes in the network (if any) and then stops Thread protocol operation. + * + * It sends an Address Release if it's a router, or sets its child timeout to 0 if it's a child. + * + * @param[in] aCallback A pointer to a function that is called upon finishing detaching. + * @param[in] aContext A pointer to callback application-specific context. + * + * @retval OT_ERROR_NONE Successfully started detaching. + * @retval OT_ERROR_BUSY Detaching is already in progress. + * + */ + Error DetachGracefully(otDetachGracefullyCallback aCallback, void *aContext); + /** * This method indicates whether or not the Thread device is attached to a Thread network. * @@ -1651,6 +1665,15 @@ protected: #endif + /** + * This method indicates whether the device is detaching gracefully. + * + * @retval TRUE Detaching is in progress. + * @retval FALSE Not detaching. + * + */ + bool IsDetachingGracefully(void) { return mDetachGracefullyTimer.IsRunning(); } + Ip6::Netif::UnicastAddress mLeaderAloc; ///< Leader anycast locator LeaderData mLeaderData; ///< Last received Leader Data TLV. @@ -1667,8 +1690,14 @@ protected: TimerMilli mAttachTimer; ///< The timer for driving the attach process. TimerMilli mDelayedResponseTimer; ///< The timer to delay MLE responses. TimerMilli mMessageTransmissionTimer; ///< The timer for (re-)sending of MLE messages (e.g. Child Update). + TimerMilli mDetachGracefullyTimer; uint8_t mParentLeaderCost; + otDetachGracefullyCallback mDetachGracefullyCallback; + void * mDetachGracefullyContext; + + static constexpr uint32_t kDetachGracefullyTimeout = 1000; + private: static constexpr uint8_t kMleHopLimit = 255; static constexpr uint8_t kMleSecurityTagSize = 4; // Security tag size in bytes. @@ -1795,6 +1824,17 @@ private: static void HandleUdpReceive(void *aContext, otMessage *aMessage, const otMessageInfo *aMessageInfo); void HandleUdpReceive(Message &aMessage, const Ip6::MessageInfo &aMessageInfo); void ScheduleMessageTransmissionTimer(void); + static void HandleDetachGracefullyTimer(Timer &aTimer); + void HandleDetachGracefullyTimer(void); + Error SendChildUpdateRequest(uint32_t aTimeout); + +#if OPENTHREAD_FTD + static void HandleDetachGracefullyAddressReleaseResponse(void * aContext, + otMessage * aMessage, + const otMessageInfo *aMessageInfo, + Error aResult); + void HandleDetachGracefullyAddressReleaseResponse(void); +#endif void HandleAdvertisement(RxInfo &aRxInfo); void HandleChildIdResponse(RxInfo &aRxInfo); diff --git a/src/core/thread/mle_router.cpp b/src/core/thread/mle_router.cpp index 813f1a2a1..3f5016fd7 100644 --- a/src/core/thread/mle_router.cpp +++ b/src/core/thread/mle_router.cpp @@ -3730,7 +3730,7 @@ exit: return error; } -void MleRouter::SendAddressRelease(void) +void MleRouter::SendAddressRelease(Coap::ResponseHandler aResponseHandler, void *aResponseHandlerContext) { Error error = kErrorNone; Tmf::MessageInfo messageInfo(GetInstance()); @@ -3744,7 +3744,8 @@ void MleRouter::SendAddressRelease(void) SuccessOrExit(error = messageInfo.SetSockAddrToRlocPeerAddrToLeaderRloc()); - SuccessOrExit(error = Get().SendMessage(*message, messageInfo)); + SuccessOrExit(error = + Get().SendMessage(*message, messageInfo, aResponseHandler, aResponseHandlerContext)); Log(kMessageSend, kTypeAddressRelease, messageInfo.GetPeerAddr()); diff --git a/src/core/thread/mle_router.hpp b/src/core/thread/mle_router.hpp index 3ccd231fb..e84899f35 100644 --- a/src/core/thread/mle_router.hpp +++ b/src/core/thread/mle_router.hpp @@ -570,6 +570,15 @@ public: void SetThreadVersionCheckEnabled(bool aEnabled) { mThreadVersionCheckEnabled = aEnabled; } #endif + /** + * This function sends an Address Release. + * + * @param[in] aResponseHandler A pointer to a function that is called upon response reception or time-out. + * @param[in] aResponseHandlerContext A pointer to callback application-specific context. + * + */ + void SendAddressRelease(Coap::ResponseHandler aResponseHandler = nullptr, void *aResponseHandlerContext = nullptr); + private: static constexpr uint16_t kDiscoveryMaxJitter = 250; // Max jitter delay Discovery Responses (in msec). static constexpr uint32_t kStateUpdatePeriod = 1000; // State update period (in msec). @@ -602,7 +611,6 @@ private: Error ProcessRouteTlv(RxInfo &aRxInfo, RouteTlv &aRouteTlv); void StopAdvertiseTrickleTimer(void); Error SendAddressSolicit(ThreadStatusTlv::Status aStatus); - void SendAddressRelease(void); void SendAddressSolicitResponse(const Coap::Message & aRequest, ThreadStatusTlv::Status aResponseStatus, const Router * aRouter, diff --git a/tests/scripts/thread-cert/Makefile.am b/tests/scripts/thread-cert/Makefile.am index 326869093..72906e699 100644 --- a/tests/scripts/thread-cert/Makefile.am +++ b/tests/scripts/thread-cert/Makefile.am @@ -160,6 +160,7 @@ EXTRA_DIST = \ test_common.py \ test_crypto.py \ test_dataset_updater.py \ + test_detach.py \ test_diag.py \ test_dns_client_config_auto_start.py \ test_dnssd.py \ @@ -236,6 +237,7 @@ check_SCRIPTS = \ test_common.py \ test_crypto.py \ test_dataset_updater.py \ + test_detach.py \ test_diag.py \ test_dns_client_config_auto_start.py \ test_dnssd.py \ diff --git a/tests/scripts/thread-cert/node.py b/tests/scripts/thread-cert/node.py index f2cf62b98..9b317bac5 100755 --- a/tests/scripts/thread-cert/node.py +++ b/tests/scripts/thread-cert/node.py @@ -815,6 +815,30 @@ class NodeImpl: self.send_command('thread stop') self._expect_done() + def detach(self, is_async=False): + cmd = 'detach' + if is_async: + cmd += ' async' + + self.send_command(cmd) + + if is_async: + self._expect_done() + return + + end = self.simulator.now() + 4 + while True: + self.simulator.go(1) + try: + self._expect_done(timeout=0.1) + return + except (pexpect.TIMEOUT, socket.timeout): + if self.simulator.now() > end: + raise + + def expect_finished_detaching(self): + self._expect('Finished detaching') + def commissioner_start(self): cmd = 'commissioner start' self.send_command(cmd) diff --git a/tests/scripts/thread-cert/test_detach.py b/tests/scripts/thread-cert/test_detach.py new file mode 100755 index 000000000..e9f79fa7b --- /dev/null +++ b/tests/scripts/thread-cert/test_detach.py @@ -0,0 +1,181 @@ +#!/usr/bin/env python3 +# +# Copyright (c) 2022, The OpenThread Authors. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. Neither the name of the copyright holder nor the +# names of its contributors may be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 'AS IS' +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. +# + +import unittest + +import thread_cert +import config +from pktverify.consts import MLE_CHILD_UPDATE_REQUEST, TIMEOUT_TLV, ADDR_REL_URI +from pktverify.packet_verifier import PacketVerifier + +# Test description: +# This test verifies that detaching function can send correct "goodbye" messages. +# +# Topology: +# +# CHILD_1 ----- ROUTER_1 ----- LEADER +# +# + +LEADER = 1 +ROUTER_1 = 2 +CHILD_1 = 3 + + +class TestDetach(thread_cert.TestCase): + USE_MESSAGE_FACTORY = False + SUPPORT_NCP = False + + TOPOLOGY = { + LEADER: { + 'name': 'Leader', + 'allowlist': [ROUTER_1], + 'mode': 'rdn', + }, + ROUTER_1: { + 'name': 'Router_1', + 'allowlist': [LEADER, CHILD_1], + 'mode': 'rdn', + }, + CHILD_1: { + 'name': 'Child_1', + 'is_mtd': True, + 'allowlist': [ROUTER_1], + 'mode': '-', + 'timeout': 10, + }, + } + + def test(self): + leader = self.nodes[LEADER] + router1 = self.nodes[ROUTER_1] + child1 = self.nodes[CHILD_1] + + leader.start() + self.simulator.go(5) + self.assertEqual(leader.get_state(), 'leader') + + router1.start() + self.simulator.go(5) + self.assertEqual(router1.get_state(), 'router') + router1_rloc16 = router1.get_addr16() + self.assertTrue(list(filter(lambda x: x[1]['rloc16'] == router1_rloc16, leader.router_table().items()))) + + self.collect_rloc16s() + + child1.start() + self.simulator.go(7) + self.assertEqual(child1.get_state(), 'child') + child_table = router1.get_child_table() + self.assertEqual(len(child_table), 1) + self.assertEqual(child_table[1]['timeout'], 10) + + child1.detach() + self.assertEqual(child1.get_state(), 'disabled') + self.assertFalse(router1.get_child_table()) + + router1.detach() + self.assertEqual(router1.get_state(), 'disabled') + self.assertFalse(list(filter(lambda x: x[1]['rloc16'] == router1_rloc16, leader.router_table().items()))) + + router1.start() + self.simulator.go(5) + self.assertEqual(router1.get_state(), 'router') + + child1.start() + self.simulator.go(7) + self.assertEqual(child1.get_state(), 'child') + child_table = router1.get_child_table() + self.assertEqual(len(child_table), 1) + self.assertEqual(child_table[2]['timeout'], 10) + + router1.thread_stop() + self.assertEqual(router1.get_state(), 'disabled') + child1.detach() + self.assertEqual(child1.get_state(), 'disabled') + + router1.start() + self.simulator.go(5) + self.assertEqual(router1.get_state(), 'router') + + child1.start() + self.simulator.go(7) + self.assertEqual(child1.get_state(), 'child') + + leader.detach() + self.assertEqual(leader.get_state(), 'disabled') + + self.assertTrue(child1.ping(router1.get_mleid(), timeout=20)) + + router1.detach() + self.assertEqual(router1.get_state(), 'disabled') + + leader.detach() + self.assertEqual(leader.get_state(), 'disabled') + + leader.start() + self.assertEqual(leader.get_state(), 'detached') + leader.detach() + self.assertEqual(leader.get_state(), 'disabled') + + leader.start() + self.simulator.go(5) + self.assertEqual(leader.get_state(), 'leader') + router1.start() + self.simulator.go(5) + self.assertEqual(router1.get_state(), 'router') + + leader.thread_stop() + router1.detach(is_async=True) + self.assertEqual(router1.get_state(), 'router') + router1.thread_stop() + self.assertEqual(router1.get_state(), 'disabled') + router1.detach() + self.assertEqual(router1.get_state(), 'disabled') + + def verify(self, pv: PacketVerifier): + pkts = pv.pkts + pv.summary.show() + + leader = pv.vars['Leader'] + router1 = pv.vars['Router_1'] + child1 = pv.vars['Child_1'] + leader_rloc16 = pv.vars['Leader_RLOC16'] + + pkts.filter_wpan_src64(child1).filter_mle_cmd(MLE_CHILD_UPDATE_REQUEST).filter_wpan_dst64( + router1).must_next().must_verify(lambda p: TIMEOUT_TLV in set(p.mle.tlv.type) and p.mle.tlv.timeout == 0) + pkts.filter_wpan_src64(router1).filter_coap_request(ADDR_REL_URI).filter_wpan_dst16(leader_rloc16).must_next() + pkts.filter_wpan_src64(child1).filter_mle_cmd(MLE_CHILD_UPDATE_REQUEST).filter_wpan_dst64( + router1).must_next().must_verify(lambda p: TIMEOUT_TLV in set(p.mle.tlv.type) and p.mle.tlv.timeout == 0) + pkts.filter_wpan_src64(leader).filter_coap_request(ADDR_REL_URI).must_not_next() + pkts.filter_wpan_src64(router1).filter_coap_request(ADDR_REL_URI).filter_wpan_dst16(leader_rloc16).must_next() + + +if __name__ == '__main__': + unittest.main() From e83ecab39587e854492d0184d0b610dfe93da1d8 Mon Sep 17 00:00:00 2001 From: Zhanglong Xia Date: Fri, 3 Jun 2022 04:58:36 +0800 Subject: [PATCH 51/80] [log] add log to record the RCP restoration state (#7743) --- src/lib/spinel/radio_spinel_impl.hpp | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/src/lib/spinel/radio_spinel_impl.hpp b/src/lib/spinel/radio_spinel_impl.hpp index 176e79419..b7650ddf7 100644 --- a/src/lib/spinel/radio_spinel_impl.hpp +++ b/src/lib/spinel/radio_spinel_impl.hpp @@ -1671,18 +1671,11 @@ otError RadioSpinel::WaitResponse(void) do { uint64_t now; - uint64_t remain; now = otPlatTimeGet(); - if (end <= now) - { - HandleRcpTimeout(); - ExitNow(mError = OT_ERROR_NONE); - } - remain = end - now; - - if (mSpinelInterface.WaitForFrame(remain) != OT_ERROR_NONE) + if ((end <= now) || (mSpinelInterface.WaitForFrame(end - now) != OT_ERROR_NONE)) { + otLogWarnPlat("Wait for response timeout"); HandleRcpTimeout(); ExitNow(mError = OT_ERROR_NONE); } @@ -2309,6 +2302,7 @@ void RadioSpinel::RecoverFromRcpFailure(void) } --mRcpFailureCount; + otLogNotePlat("RCP recovery is done"); exit: return; From 24d633d99d1e336ea7db0bdb62b2961e4d110a40 Mon Sep 17 00:00:00 2001 From: Abtin Keshavarzian Date: Thu, 2 Jun 2022 15:32:08 -0700 Subject: [PATCH 52/80] [srp-client] add auto host address mode (#7655) This commit adds a "auto host address mode" in SRP client. When enabled, host IPv6 addresses are automatically set by SRP client using all the unicast addresses on Thread netif excluding the link-local and mesh-local addresses. If there is no valid address, then Mesh Local EID address is added. The SRP client will automatically re-register when/if addresses on Thread netif are updated (e.g., new addresses are added or existing addresses are removed). This commit updates CLI to add support for new SRP client APIs related to the new feature. It also adds `test_srp_auto_host_address` test to cover the behavior of this feature. --- include/openthread/instance.h | 2 +- include/openthread/srp_client.h | 27 +- src/cli/README_SRP_CLIENT.md | 29 +- src/cli/cli_srp_client.cpp | 38 ++- src/core/api/srp_client_api.cpp | 5 + src/core/net/srp_client.cpp | 120 ++++++-- src/core/net/srp_client.hpp | 47 +++- tests/scripts/thread-cert/Makefile.am | 2 + tests/scripts/thread-cert/node.py | 4 + .../thread-cert/test_srp_auto_host_address.py | 258 ++++++++++++++++++ tests/toranj/cli/cli.py | 3 + 11 files changed, 500 insertions(+), 35 deletions(-) create mode 100755 tests/scripts/thread-cert/test_srp_auto_host_address.py diff --git a/include/openthread/instance.h b/include/openthread/instance.h index 02e5b89b7..e0ff78f8b 100644 --- a/include/openthread/instance.h +++ b/include/openthread/instance.h @@ -53,7 +53,7 @@ extern "C" { * @note This number versions both OpenThread platform and user APIs. * */ -#define OPENTHREAD_API_VERSION (217) +#define OPENTHREAD_API_VERSION (218) /** * @addtogroup api-instance diff --git a/include/openthread/srp_client.h b/include/openthread/srp_client.h index 1dc0c9e3e..bb291d414 100644 --- a/include/openthread/srp_client.h +++ b/include/openthread/srp_client.h @@ -75,8 +75,9 @@ typedef enum typedef struct otSrpClientHostInfo { const char * mName; ///< Host name (label) string (NULL if not yet set). - const otIp6Address * mAddresses; ///< Pointer to an array of host IPv6 addresses (NULL if not yet set). + const otIp6Address * mAddresses; ///< Array of host IPv6 addresses (NULL if not set or auto address is enabled). uint8_t mNumAddresses; ///< Number of IPv6 addresses in `mAddresses` array. + bool mAutoAddress; ///< Indicates whether auto address mode is enabled or not. otSrpClientItemState mState; ///< Host info state. } otSrpClientHostInfo; @@ -428,6 +429,27 @@ const otSrpClientHostInfo *otSrpClientGetHostInfo(otInstance *aInstance); */ otError otSrpClientSetHostName(otInstance *aInstance, const char *aName); +/** + * This function enables auto host address mode. + * + * When enabled host IPv6 addresses are automatically set by SRP client using all the unicast addresses on Thread netif + * excluding all link-local and mesh-local addresses. If there is no valid address, then Mesh Local EID address is + * added. The SRP client will automatically re-register when/if addresses on Thread netif are updated (new addresses + * are added or existing addresses are removed). + * + * The auto host address mode can be enabled before start or during operation of SRP client except when the host info + * is being removed (client is busy handling a remove request from an call to `otSrpClientRemoveHostAndServices()` and + * host info still being in either `STATE_TO_REMOVE` or `STATE_REMOVING` states). + * + * After auto host address mode is enabled, it can be disabled by a call to `otSrpClientSetHostAddresses()` which + * then explicitly sets the host addresses. + * + * @retval OT_ERROR_NONE Successfully enabled auto host address mode. + * @retval OT_ERROR_INVALID_STATE Host is being removed and therefore cannot enable auto host address mode. + * + */ +otError otSrpClientEnableAutoHostAddress(otInstance *aInstance); + /** * This function sets/updates the list of host IPv6 address. * @@ -442,6 +464,9 @@ otError otSrpClientSetHostName(otInstance *aInstance, const char *aName); * After a successful call to this function, `otSrpClientCallback` will be called to report the status of the address * registration with SRP server. * + * Calling this function disables auto host address mode if it was previously enabled from a successful call to + * `otSrpClientEnableAutoHostAddress()`. + * * @param[in] aInstance A pointer to the OpenThread instance. * @param[in] aIp6Addresses A pointer to the an array containing the host IPv6 addresses. * @param[in] aNumAddresses The number of addresses in the @p aIp6Addresses array. diff --git a/src/cli/README_SRP_CLIENT.md b/src/cli/README_SRP_CLIENT.md index ab5dcdb77..ae48fbf1e 100644 --- a/src/cli/README_SRP_CLIENT.md +++ b/src/cli/README_SRP_CLIENT.md @@ -139,6 +139,14 @@ name:"dev4312", state:Registered, addrs:[fd00:0:0:0:0:0:0:1234, fd00:0:0:0:0:0:0 Done ``` +When auto host address mode is enabled. + +```bash +srp client host +name:"dev1234", state:Registered, addrs:auto +Done +``` + ### host name Usage: `srp client host name [name]` @@ -160,9 +168,17 @@ Done ### host address -Usage : `srp client host address [
...]` +Usage : `srp client host address [auto |
...]` -Get the list of host addresses. +Indicate auto address mode is enabled. + +```bash +> srp client host address +auto +Done +``` + +Get the list of host addresses (when auto host address is not enabled). ```bash > srp client host address @@ -171,7 +187,14 @@ fd00:0:0:0:0:0:0:beef Done ``` -Set the list of host addresses (can be set while client is running to update the host addresses) +Enable auto host address mode. When enabled client will automatically use all Thread netif unicast addresses excluding all link-local and mesh-local addresses. If there is no valid address, then Mesh Local EID address is added. SRP client will automatically re-register if/when addresses on Thread netif get changed (e.g., new address is added or existing address is removed). + +```bash +> srp client host address auto +Done +``` + +Explicitly set the list of host addresses (can be set while client is running to update the host addresses), also disabled auto host address mode. ```bash > srp client host address fd00::cafe diff --git a/src/cli/cli_srp_client.cpp b/src/cli/cli_srp_client.cpp index 4d81cd901..0732ac2e4 100644 --- a/src/cli/cli_srp_client.cpp +++ b/src/cli/cli_srp_client.cpp @@ -160,10 +160,21 @@ template <> otError SrpClient::Process(Arg aArgs[]) { const otSrpClientHostInfo *hostInfo = otSrpClientGetHostInfo(GetInstancePtr()); - for (uint8_t index = 0; index < hostInfo->mNumAddresses; index++) + if (hostInfo->mAutoAddress) { - OutputIp6AddressLine(hostInfo->mAddresses[index]); + OutputLine("auto"); } + else + { + for (uint8_t index = 0; index < hostInfo->mNumAddresses; index++) + { + OutputIp6AddressLine(hostInfo->mAddresses[index]); + } + } + } + else if (aArgs[1] == "auto") + { + error = otSrpClientEnableAutoHostAddress(GetInstancePtr()); } else { @@ -447,19 +458,28 @@ void SrpClient::OutputHostInfo(uint8_t aIndentSize, const otSrpClientHostInfo &a OutputFormat("(null)"); } - OutputFormat(", state:%s, addrs:[", otSrpClientItemStateToString(aHostInfo.mState)); + OutputFormat(", state:%s, addrs:", otSrpClientItemStateToString(aHostInfo.mState)); - for (uint8_t index = 0; index < aHostInfo.mNumAddresses; index++) + if (aHostInfo.mAutoAddress) { - if (index > 0) + OutputLine("auto"); + } + else + { + OutputFormat("["); + + for (uint8_t index = 0; index < aHostInfo.mNumAddresses; index++) { - OutputFormat(", "); + if (index > 0) + { + OutputFormat(", "); + } + + OutputIp6Address(aHostInfo.mAddresses[index]); } - OutputIp6Address(aHostInfo.mAddresses[index]); + OutputLine("]"); } - - OutputLine("]"); } void SrpClient::OutputServiceList(uint8_t aIndentSize, const otSrpClientService *aServices) diff --git a/src/core/api/srp_client_api.cpp b/src/core/api/srp_client_api.cpp index a0c16e3d9..3ec356f34 100644 --- a/src/core/api/srp_client_api.cpp +++ b/src/core/api/srp_client_api.cpp @@ -124,6 +124,11 @@ otError otSrpClientSetHostName(otInstance *aInstance, const char *aName) return AsCoreType(aInstance).Get().SetHostName(aName); } +otError otSrpClientEnableAutoHostAddress(otInstance *aInstance) +{ + return AsCoreType(aInstance).Get().EnableAutoHostAddress(); +} + otError otSrpClientSetHostAddresses(otInstance *aInstance, const otIp6Address *aIp6Addresses, uint8_t aNumAddresses) { return AsCoreType(aInstance).Get().SetHostAddresses(AsCoreTypePtr(aIp6Addresses), aNumAddresses); diff --git a/src/core/net/srp_client.cpp b/src/core/net/srp_client.cpp index e148d659a..a65fe9251 100644 --- a/src/core/net/srp_client.cpp +++ b/src/core/net/srp_client.cpp @@ -76,10 +76,20 @@ void Client::HostInfo::SetState(ItemState aState) } } +void Client::HostInfo::EnableAutoAddress(void) +{ + mAddresses = nullptr; + mNumAddresses = 0; + mAutoAddress = true; + + LogInfo("HostInfo enabled auto address", GetNumAddresses()); +} + void Client::HostInfo::SetAddresses(const Ip6::Address *aAddresses, uint8_t aNumAddresses) { mAddresses = aAddresses; mNumAddresses = aNumAddresses; + mAutoAddress = false; LogInfo("HostInfo set %d addrs", GetNumAddresses()); @@ -239,6 +249,7 @@ Client::Client(Instance &aInstance) , mState(kStateStopped) , mTxFailureRetryCount(0) , mShouldRemoveKeyLease(false) + , mAutoHostAddressAddedMeshLocal(false) #if OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE , mServiceKeyRecordEnabled(false) #endif @@ -415,6 +426,22 @@ void Client::HandleNotifierEvents(Events aEvents) ProcessAutoStart(); } #endif + + if (mHostInfo.IsAutoAddressEnabled()) + { + Events::Flags eventFlags = (kEventIp6AddressAdded | kEventIp6AddressRemoved); + + if (mAutoHostAddressAddedMeshLocal) + { + eventFlags |= kEventThreadMeshLocalAddrChanged; + } + + if (aEvents.ContainsAny(eventFlags)) + { + IgnoreError(UpdateHostInfoStateOnAddressChange()); + UpdateState(); + } + } } void Client::HandleRoleChanged(void) @@ -466,11 +493,37 @@ exit: return error; } +Error Client::EnableAutoHostAddress(void) +{ + Error error = kErrorNone; + + VerifyOrExit(!mHostInfo.IsAutoAddressEnabled()); + SuccessOrExit(error = UpdateHostInfoStateOnAddressChange()); + + mHostInfo.EnableAutoAddress(); + UpdateState(); + +exit: + return error; +} + Error Client::SetHostAddresses(const Ip6::Address *aAddresses, uint8_t aNumAddresses) { Error error = kErrorNone; VerifyOrExit((aAddresses != nullptr) && (aNumAddresses > 0), error = kErrorInvalidArgs); + SuccessOrExit(error = UpdateHostInfoStateOnAddressChange()); + + mHostInfo.SetAddresses(aAddresses, aNumAddresses); + UpdateState(); + +exit: + return error; +} + +Error Client::UpdateHostInfoStateOnAddressChange(void) +{ + Error error = kErrorNone; VerifyOrExit((mHostInfo.GetState() != kToRemove) && (mHostInfo.GetState() != kRemoving), error = kErrorInvalidState); @@ -484,9 +537,6 @@ Error Client::SetHostAddresses(const Ip6::Address *aAddresses, uint8_t aNumAddre mHostInfo.SetState(kToRefresh); } - mHostInfo.SetAddresses(aAddresses, aNumAddresses); - UpdateState(); - exit: return error; } @@ -1008,10 +1058,9 @@ exit: return error; } -Error Client::AppendHostDescriptionInstruction(Message &aMessage, Info &aInfo) const +Error Client::AppendHostDescriptionInstruction(Message &aMessage, Info &aInfo) { - Error error = kErrorNone; - Dns::ResourceRecord rr; + Error error = kErrorNone; //---------------------------------- // Host Description Instruction @@ -1024,16 +1073,37 @@ Error Client::AppendHostDescriptionInstruction(Message &aMessage, Info &aInfo) c // AAAA RRs - rr.Init(Dns::ResourceRecord::kTypeAaaa); - rr.SetTtl(GetTtl()); - rr.SetLength(sizeof(Ip6::Address)); - - for (uint8_t index = 0; index < mHostInfo.GetNumAddresses(); index++) + if (mHostInfo.IsAutoAddressEnabled()) { - SuccessOrExit(error = AppendHostName(aMessage, aInfo)); - SuccessOrExit(error = aMessage.Append(rr)); - SuccessOrExit(error = aMessage.Append(mHostInfo.GetAddress(index))); - aInfo.mRecordCount++; + // Append all addresses on Thread netif excluding link-local and + // mesh-local addresses. If no address is appended, we include + // the mesh local address. + + mAutoHostAddressAddedMeshLocal = true; + + for (const Ip6::Netif::UnicastAddress &unicastAddress : Get().GetUnicastAddresses()) + { + if (unicastAddress.GetAddress().IsLinkLocal() || + Get().IsMeshLocalAddress(unicastAddress.GetAddress())) + { + continue; + } + + SuccessOrExit(error = AppendAaaaRecord(unicastAddress.GetAddress(), aMessage, aInfo)); + mAutoHostAddressAddedMeshLocal = false; + } + + if (mAutoHostAddressAddedMeshLocal) + { + SuccessOrExit(error = AppendAaaaRecord(Get().GetMeshLocal64(), aMessage, aInfo)); + } + } + else + { + for (uint8_t index = 0; index < mHostInfo.GetNumAddresses(); index++) + { + SuccessOrExit(error = AppendAaaaRecord(mHostInfo.GetAddress(index), aMessage, aInfo)); + } } // KEY RR @@ -1045,6 +1115,24 @@ exit: return error; } +Error Client::AppendAaaaRecord(const Ip6::Address &aAddress, Message &aMessage, Info &aInfo) const +{ + Error error; + Dns::ResourceRecord rr; + + rr.Init(Dns::ResourceRecord::kTypeAaaa); + rr.SetTtl(GetTtl()); + rr.SetLength(sizeof(Ip6::Address)); + + SuccessOrExit(error = AppendHostName(aMessage, aInfo)); + SuccessOrExit(error = aMessage.Append(rr)); + SuccessOrExit(error = aMessage.Append(aAddress)); + aInfo.mRecordCount++; + +exit: + return error; +} + Error Client::AppendKeyRecord(Message &aMessage, Info &aInfo) const { Error error; @@ -1519,7 +1607,7 @@ void Client::UpdateState(void) // host address, otherwise no need to send SRP update message. // The exception is when removing host info where we allow // for empty service list. - VerifyOrExit(!mServices.IsEmpty() && (mHostInfo.GetNumAddresses() > 0)); + VerifyOrExit(!mServices.IsEmpty() && (mHostInfo.IsAutoAddressEnabled() || (mHostInfo.GetNumAddresses() > 0))); // Fall through diff --git a/src/core/net/srp_client.hpp b/src/core/net/srp_client.hpp index 8e7cca7dc..4a3502bb6 100644 --- a/src/core/net/srp_client.hpp +++ b/src/core/net/srp_client.hpp @@ -103,7 +103,7 @@ public: * This type represents an SRP client host info. * */ - class HostInfo : public otSrpClientHostInfo, public Clearable + class HostInfo : public otSrpClientHostInfo, private Clearable { friend class Client; @@ -128,6 +128,15 @@ public: */ const char *GetName(void) const { return mName; } + /** + * This method indicates whether or not the host auto address mode is enabled. + * + * @retval TRUE If the auto address mode is enabled. + * @retval FALSE If the auto address mode is disabled. + * + */ + bool IsAutoAddressEnabled(void) const { return mAutoAddress; } + /** * This method gets the number of host IPv6 addresses. * @@ -158,6 +167,7 @@ public: void SetName(const char *aName) { mName = aName; } void SetState(ItemState aState); void SetAddresses(const Ip6::Address *aAddresses, uint8_t aNumAddresses); + void EnableAutoAddress(void); }; /** @@ -489,16 +499,16 @@ public: const HostInfo &GetHostInfo(void) const { return mHostInfo; } /** - * This function sets the host name label. + * This method sets the host name label. * - * After a successful call to this function, `Callback` will be called to report the status of host info + * After a successful call to this method, `Callback` will be called to report the status of host info * registration with SRP server. * * The host name can be set before client is started or after start but before host info is registered with server * (host info should be in either `kToAdd` or `kRemoved`). * * @param[in] aName A pointer to host name label string (MUST NOT be NULL). Pointer the string buffer MUST - * persist and remain valid and constant after return from this function. + * persist and remain valid and constant after return from this method. * * @retval kErrorNone The host name label was set successfully. * @retval kErrorInvalidArgs The @p aName is NULL. @@ -507,6 +517,27 @@ public: */ Error SetHostName(const char *aName); + /** + * This method enables auto host address mode. + * + * When enabled host IPv6 addresses are automatically set by SRP client using all the unicast addresses on Thread + * netif excluding the link-local and mesh-local addresses. If there is no valid address, then Mesh Local EID + * address is added. The SRP client will automatically re-register when/if addresses on Thread netif are updated + * (new addresses are added or existing addresses are removed). + * + * The auto host address mode can be enabled before start or during operation of SRP client except when the host + * info is being removed (client is busy handling a remove request from an call to `RemoveHostAndServices()` and + * host info still being in either `kStateToRemove` or `kStateRemoving` states). + * + * After auto host address mode is enabled, it can be disabled by a call to `SetHostAddresses()` which then + * explicitly sets the host addresses. + * + * @retval kErrorNone Successfully enabled auto host address mode. + * @retval kErrorInvalidState Host is being removed and therefore cannot enable auto host address mode. + * + */ + Error EnableAutoHostAddress(void); + /** * This method sets/updates the list of host IPv6 address. * @@ -518,6 +549,9 @@ public: * After a successful call to this method, `Callback` will be called to report the status of the address * registration with SRP server. * + * Calling this method disables auto host address mode if it was previously enabled from a successful call to + * `EnableAutoHostAddress()`. + * * @param[in] aAddresses A pointer to the an array containing the host IPv6 addresses. * @param[in] aNumAddresses The number of addresses in the @p aAddresses array. * @@ -884,6 +918,7 @@ private: void Pause(void); void HandleNotifierEvents(Events aEvents); void HandleRoleChanged(void); + Error UpdateHostInfoStateOnAddressChange(void); void UpdateServiceStateToRemove(Service &aService); State GetState(void) const { return mState; } void SetState(State aState); @@ -895,10 +930,11 @@ private: Error PrepareUpdateMessage(Message &aMessage); Error ReadOrGenerateKey(Crypto::Ecdsa::P256::KeyPair &aKeyPair); Error AppendServiceInstructions(Service &aService, Message &aMessage, Info &aInfo); - Error AppendHostDescriptionInstruction(Message &aMessage, Info &aInfo) const; + Error AppendHostDescriptionInstruction(Message &aMessage, Info &aInfo); Error AppendKeyRecord(Message &aMessage, Info &aInfo) const; Error AppendDeleteAllRrsets(Message &aMessage) const; Error AppendHostName(Message &aMessage, Info &aInfo, bool aDoNotCompress = false) const; + Error AppendAaaaRecord(const Ip6::Address &aAddress, Message &aMessage, Info &aInfo) const; Error AppendUpdateLeaseOptRecord(Message &aMessage) const; Error AppendSignature(Message &aMessage, Info &aInfo); void UpdateRecordLengthInMessage(Dns::ResourceRecord &aRecord, uint16_t aOffset, Message &aMessage) const; @@ -938,6 +974,7 @@ private: State mState; uint8_t mTxFailureRetryCount : 4; bool mShouldRemoveKeyLease : 1; + bool mAutoHostAddressAddedMeshLocal : 1; #if OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE bool mServiceKeyRecordEnabled : 1; #endif diff --git a/tests/scripts/thread-cert/Makefile.am b/tests/scripts/thread-cert/Makefile.am index 72906e699..200c46a3c 100644 --- a/tests/scripts/thread-cert/Makefile.am +++ b/tests/scripts/thread-cert/Makefile.am @@ -188,6 +188,7 @@ EXTRA_DIST = \ test_router_upgrade.py \ test_service.py \ test_set_mliid.py \ + test_srp_auto_host_address.py \ test_srp_auto_start_mode.py \ test_srp_client_remove_host.py \ test_srp_client_save_server_info.py \ @@ -264,6 +265,7 @@ check_SCRIPTS = \ test_router_reattach.py \ test_router_upgrade.py \ test_service.py \ + test_srp_auto_host_address.py \ test_srp_auto_start_mode.py \ test_srp_client_remove_host.py \ test_srp_client_save_server_info.py \ diff --git a/tests/scripts/thread-cert/node.py b/tests/scripts/thread-cert/node.py index 9b317bac5..ccc400ed2 100755 --- a/tests/scripts/thread-cert/node.py +++ b/tests/scripts/thread-cert/node.py @@ -1146,6 +1146,10 @@ class NodeImpl: self.send_command(f'srp client host clear') self._expect_done() + def srp_client_enable_auto_host_address(self): + self.send_command(f'srp client host address auto') + self._expect_done() + def srp_client_set_host_address(self, *addrs: str): self.send_command(f'srp client host address {" ".join(addrs)}') self._expect_done() diff --git a/tests/scripts/thread-cert/test_srp_auto_host_address.py b/tests/scripts/thread-cert/test_srp_auto_host_address.py new file mode 100755 index 000000000..bb9ac6e1e --- /dev/null +++ b/tests/scripts/thread-cert/test_srp_auto_host_address.py @@ -0,0 +1,258 @@ +#!/usr/bin/env python3 +# +# Copyright (c) 2022, The OpenThread Authors. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. Neither the name of the copyright holder nor the +# names of its contributors may be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. +# + +import ipaddress +import unittest + +import command +import config +import thread_cert + +# Test description: +# This test verifies SRP client auto host address mode. +# +# Topology: +# SRP client (leader) +# | +# | +# SRP server (router) +# + +CLIENT = 1 +SERVER = 2 + + +class SrpAutoHostAddress(thread_cert.TestCase): + USE_MESSAGE_FACTORY = False + SUPPORT_NCP = False + + TOPOLOGY = { + CLIENT: { + 'name': 'SRP_CLIENT', + 'mode': 'rdn', + }, + SERVER: { + 'name': 'SRP_SERVER', + 'mode': 'rdn', + }, + } + + def test(self): + client = self.nodes[CLIENT] + server = self.nodes[SERVER] + + #------------------------------------------------------------------- + # Form the network. + + client.srp_server_set_enabled(False) + client.start() + self.simulator.go(15) + self.assertEqual(client.get_state(), 'leader') + + server.start() + self.simulator.go(5) + self.assertEqual(server.get_state(), 'router') + + #------------------------------------------------------------------- + # Enable SRP server + + server.srp_server_set_enabled(True) + self.simulator.go(5) + + #------------------------------------------------------------------- + # Enable auto start mode on SRP client + + self.assertEqual(client.srp_client_get_state(), 'Disabled') + client.srp_client_enable_auto_start_mode() + self.assertEqual(client.srp_client_get_auto_start_mode(), 'Enabled') + self.simulator.go(2) + + self.assertEqual(client.srp_client_get_state(), 'Enabled') + + #------------------------------------------------------------------- + # Set host name and enable auto host address on client + + client.srp_client_set_host_name('host') + client.srp_client_enable_auto_host_address() + + #------------------------------------------------------------------- + # Register a service on client + + client.srp_client_add_service('test_srv', '_test._udo', 12345, 0, 0) + self.simulator.go(2) + self.check_registered_addresses(client, server) + + #------------------------------------------------------------------- + # Add an address and check the SRP client re-registered and updated + # server with new address. + + client.add_ipaddr('fd00:1:2:3:4:5:6:7') + + self.simulator.go(5) + client_addresses = [addr.strip() for addr in client.get_addrs()] + self.assertIn('fd00:1:2:3:4:5:6:7', client_addresses) + self.check_registered_addresses(client, server) + + #------------------------------------------------------------------- + # Remove the address and check the SRP client re-registered and updated + # server. + + client.del_ipaddr('fd00:1:2:3:4:5:6:7') + + self.simulator.go(5) + client_addresses = [addr.strip() for addr in client.get_addrs()] + self.assertNotIn('fd00:1:2:3:4:5:6:7', client_addresses) + self.check_registered_addresses(client, server) + + #------------------------------------------------------------------- + # Add an SLAAC on-mesh prefix (which will trigger an address to be + # added) and check that the SRP client re-registered and updated + # server with the new address. + + client.add_prefix('fd00:abba:cafe:bee::/64', 'paos') + client.register_netdata() + self.simulator.go(5) + + slaac_addr = [addr.strip() for addr in client.get_addrs() if addr.strip().startswith('fd00:abba:cafe:bee:')] + self.assertEqual(len(slaac_addr), 1) + self.check_registered_addresses(client, server) + + #------------------------------------------------------------------- + # Add another SLAAC on-mesh prefix and check that the SRP client + # re-registered and updated server with all address. + + client.add_prefix('fd00:9:8:7::/64', 'paos') + client.register_netdata() + self.simulator.go(5) + + slaac_addr = [addr.strip() for addr in client.get_addrs() if addr.strip().startswith('fd00:9:8:7:')] + self.assertEqual(len(slaac_addr), 1) + self.check_registered_addresses(client, server) + + #------------------------------------------------------------------- + # Remove the on-mesh prefix (which will trigger an address to be + # removed) and check that the SRP client re-registered and updated + # server with the remaining address. + + client.remove_prefix('fd00:abba:cafe:bee::/64') + client.register_netdata() + self.simulator.go(5) + + slaac_addr = [addr.strip() for addr in client.get_addrs() if addr.strip().startswith('fd00:abba:cafe:bee:')] + self.assertEqual(len(slaac_addr), 0) + self.check_registered_addresses(client, server) + + #------------------------------------------------------------------- + # Remove the next on-mesh prefix. Check that SRP client re-registered + # now with only ML-EID. + + client.remove_prefix('fd00:9:8:7::/64') + client.register_netdata() + self.simulator.go(5) + + slaac_addr = [addr.strip() for addr in client.get_addrs() if addr.strip().startswith('fd00:9:8:7:')] + self.assertEqual(len(slaac_addr), 0) + self.check_registered_addresses(client, server) + + #------------------------------------------------------------------- + # Explicitly set the host addresses (which disables the auto host + # address mode) and check that only the new addresses are registered. + + client.srp_client_set_host_address('fd00:f:e:d:c:b:a:9') + self.simulator.go(5) + + self.assertEqual(client.srp_client_get_host_state(), 'Registered') + server_hosts = server.srp_server_get_hosts() + self.assertEqual(len(server_hosts), 1) + server_host = server_hosts[0] + self.assertEqual(server_host['deleted'], 'false') + self.assertEqual(server_host['fullname'], 'host.default.service.arpa.') + host_addresses = [addr.strip() for addr in server_host['addresses']] + self.assertEqual(len(host_addresses), 1) + self.assertEqual(host_addresses[0], 'fd00:f:e:d:c:b:a:9') + + #------------------------------------------------------------------- + # Re-enable auto host address mode and check that addresses are + # updated and registered properly. + + client.srp_client_enable_auto_host_address() + self.simulator.go(5) + self.check_registered_addresses(client, server) + + def check_registered_addresses(self, client, server): + # Ensure client has registered successfully. + self.assertEqual(client.srp_client_get_host_state(), 'Registered') + + # Check the host info on server. + server_hosts = server.srp_server_get_hosts() + self.assertEqual(len(server_hosts), 1) + server_host = server_hosts[0] + self.assertEqual(server_host['deleted'], 'false') + self.assertEqual(server_host['fullname'], 'host.default.service.arpa.') + + # Check the host addresses on server to match client. + + host_addresses = [addr.strip() for addr in server_host['addresses']] + client_addresses = [addr.strip() for addr in client.get_addrs()] + + # All registered addresses must be in client list of addresses. + + for addr in host_addresses: + self.assertIn(addr, client_addresses) + + # All addresses on client excluding link-local and mesh-local + # addresses must be seen on server side. But if there was + # no address, then mesh-local address should be the only + # one registered. + + client_mleid = client.get_mleid() + checked_address = False + + for addr in client_addresses: + if not self.is_address_link_local(addr) and not self.is_address_locator(addr) and addr != client_mleid: + self.assertIn(addr, host_addresses) + checked_address = True + + if not checked_address: + self.assertEqual(len(host_addresses), 1) + self.assertIn(client_mleid, host_addresses) + + def is_address_locator(self, addr): + # Checks if an IPv6 address is a locator (IID should match `0:ff:fe00:xxxx`) + u32s = addr.split(':') + self.assertEqual(len(u32s), 8) + return ':'.join(u32s[4:]).startswith('0:ff:fe00:') + + def is_address_link_local(self, addr): + # Checks if an IPv6 address is link-local + return addr.startswith('fe80:') + + +if __name__ == '__main__': + unittest.main() diff --git a/tests/toranj/cli/cli.py b/tests/toranj/cli/cli.py index 5e24bedec..3bfb7a830 100644 --- a/tests/toranj/cli/cli.py +++ b/tests/toranj/cli/cli.py @@ -329,6 +329,9 @@ class Node(object): def srp_client_clear_host(self): self._cli_no_output('srp client host clear') + def srp_client_enable_auto_host_address(self): + self._cli_no_output('srp client host address auto') + def srp_client_set_host_address(self, *addrs): self._cli_no_output('srp client host address', *addrs) From c0babfe319a101b4a27a36789b0b8facde1fef43 Mon Sep 17 00:00:00 2001 From: Abtin Keshavarzian Date: Thu, 2 Jun 2022 15:33:12 -0700 Subject: [PATCH 53/80] [routing-manager] add `OmrPrefix` class, include OMR preference in RIO (#7778) This commit updates `RoutingManager` to track the preference of the discovered OMR prefixes from Thread Network Data and advertise the OMR prefixes with the same preference in Route Info Options in the emitted Router Advertisements. This commit adds `OmrPrefix` class (which tracks an OMR prefix along with its preference) and provides helpers method (e.g., method to compare two OMR prefixes to determine which one is favored, `ToString ()` method). The `EvaluateOmrPrefix()` is updated to use the new `OmrPrefix` class. --- src/core/border_router/routing_manager.cpp | 162 ++++++++++++++------- src/core/border_router/routing_manager.hpp | 23 ++- src/core/thread/network_data_types.hpp | 8 + 3 files changed, 142 insertions(+), 51 deletions(-) diff --git a/src/core/border_router/routing_manager.cpp b/src/core/border_router/routing_manager.cpp index 1ea7059f6..cf053a658 100644 --- a/src/core/border_router/routing_manager.cpp +++ b/src/core/border_router/routing_manager.cpp @@ -365,85 +365,98 @@ void RoutingManager::EvaluateOmrPrefix(OmrPrefixArray &aNewOmrPrefixes) { NetworkData::Iterator iterator = NetworkData::kIteratorInit; NetworkData::OnMeshPrefixConfig onMeshPrefixConfig; - Ip6::Prefix * electedOmrPrefix = nullptr; - signed int electedOmrPrefixPreference = 0; - Ip6::Prefix * publishedLocalOmrPrefix = nullptr; + OmrPrefix * favoredOmrEntry = nullptr; + OmrPrefix * localOmrEntry = nullptr; OT_ASSERT(mIsRunning); while (Get().GetNextOnMeshPrefix(iterator, onMeshPrefixConfig) == kErrorNone) { - const Ip6::Prefix &prefix = onMeshPrefixConfig.GetPrefix(); + OmrPrefix *entry; if (!IsValidOmrPrefix(onMeshPrefixConfig)) { continue; } - if (aNewOmrPrefixes.Contains(prefix)) - { - // Ignore duplicate prefixes. - continue; - } + entry = aNewOmrPrefixes.FindMatching(onMeshPrefixConfig.GetPrefix()); - if (aNewOmrPrefixes.PushBack(prefix) != kErrorNone) + if (entry != nullptr) { - LogWarn("EvaluateOmrPrefix: Too many OMR prefixes, ignoring prefix %s", prefix.ToString().AsCString()); - continue; + // Update the entry if we find the same prefix with higher + // preference in network data + + if (onMeshPrefixConfig.GetPreference() <= entry->GetPreference()) + { + continue; + } + + entry->SetPreference(onMeshPrefixConfig.GetPreference()); + } + else + { + entry = aNewOmrPrefixes.PushBack(); + + if (entry == nullptr) + { + LogWarn("EvaluateOmrPrefix: Too many OMR prefixes, ignoring prefix %s", + onMeshPrefixConfig.GetPrefix().ToString().AsCString()); + continue; + } + + entry->InitFrom(onMeshPrefixConfig); } if (onMeshPrefixConfig.mPreferred) { - if (electedOmrPrefix == nullptr || onMeshPrefixConfig.mPreference > electedOmrPrefixPreference || - (onMeshPrefixConfig.mPreference == electedOmrPrefixPreference && prefix < *electedOmrPrefix)) + if ((favoredOmrEntry == nullptr) || (entry->IsFavoredOver(*favoredOmrEntry))) { - electedOmrPrefix = aNewOmrPrefixes.Back(); - electedOmrPrefixPreference = onMeshPrefixConfig.mPreference; + favoredOmrEntry = entry; } } - if (prefix == mLocalOmrPrefix) + if (entry->GetPrefix() == mLocalOmrPrefix) { - publishedLocalOmrPrefix = aNewOmrPrefixes.Back(); + localOmrEntry = entry; } } // Decide if we need to add or remove our local OMR prefix. - if (electedOmrPrefix == nullptr) + if (favoredOmrEntry == nullptr) { LogInfo("EvaluateOmrPrefix: No preferred OMR prefixes found in Thread network"); - if (PublishLocalOmrPrefix() == kErrorNone) - { - IgnoreError(aNewOmrPrefixes.PushBack(mLocalOmrPrefix)); - } - // The `aNewOmrPrefixes` remains empty if we fail to publish // the local OMR prefix. + SuccessOrExit(PublishLocalOmrPrefix()); + + localOmrEntry = aNewOmrPrefixes.PushBack(); + VerifyOrExit(localOmrEntry != nullptr); + + localOmrEntry->Init(mLocalOmrPrefix, NetworkData::kRoutePreferenceMedium); } - else + else if (favoredOmrEntry == localOmrEntry) { - if (*electedOmrPrefix == mLocalOmrPrefix) - { - IgnoreError(PublishLocalOmrPrefix()); - } - else if (IsOmrPrefixAddedToLocalNetworkData()) - { - LogInfo("EvaluateOmrPrefix: There is already a preferred OMR prefix %s (pref=%d) in the Thread network", - electedOmrPrefix->ToString().AsCString(), electedOmrPrefixPreference); + IgnoreError(PublishLocalOmrPrefix()); + } + else if (IsOmrPrefixAddedToLocalNetworkData()) + { + LogInfo("EvaluateOmrPrefix: There is already a preferred OMR prefix %s in the Thread network", + favoredOmrEntry->ToString().AsCString()); - UnpublishLocalOmrPrefix(); + UnpublishLocalOmrPrefix(); + if (localOmrEntry != nullptr) + { // Remove the local OMR prefix from the list by overwriting it - // with the last element and then popping it from the list. - if (publishedLocalOmrPrefix != nullptr) - { - *publishedLocalOmrPrefix = *aNewOmrPrefixes.Back(); - aNewOmrPrefixes.PopBack(); - } + // with popped last entry in the list. + *localOmrEntry = *aNewOmrPrefixes.PopBack(); } } + +exit: + return; } Error RoutingManager::PublishLocalOmrPrefix(void) @@ -874,13 +887,14 @@ void RoutingManager::SendRouterAdvertisement(const OmrPrefixArray &aNewOmrPrefix // Invalidate the advertised OMR prefixes if they are no longer in the new OMR prefix array. - for (const Ip6::Prefix &advertisedOmrPrefix : mAdvertisedOmrPrefixes) + for (const OmrPrefix &advertisedOmrPrefix : mAdvertisedOmrPrefixes) { - if (!aNewOmrPrefixes.Contains(advertisedOmrPrefix)) + if (!aNewOmrPrefixes.ContainsMatching(advertisedOmrPrefix.GetPrefix())) { RouterAdv::RouteInfoOption *rio; - OT_ASSERT(bufferLength + RouterAdv::RouteInfoOption::OptionSizeForPrefix(advertisedOmrPrefix.GetLength()) <= + OT_ASSERT(bufferLength + RouterAdv::RouteInfoOption::OptionSizeForPrefix( + advertisedOmrPrefix.GetPrefix().GetLength()) <= sizeof(buffer)); rio = reinterpret_cast(buffer + bufferLength); @@ -888,7 +902,7 @@ void RoutingManager::SendRouterAdvertisement(const OmrPrefixArray &aNewOmrPrefix // Set zero route lifetime to immediately invalidate the advertised OMR prefix. rio->Init(); rio->SetRouteLifetime(0); - rio->SetPrefix(advertisedOmrPrefix); + rio->SetPrefix(advertisedOmrPrefix.GetPrefix()); bufferLength += rio->GetSize(); @@ -897,18 +911,20 @@ void RoutingManager::SendRouterAdvertisement(const OmrPrefixArray &aNewOmrPrefix } } - for (const Ip6::Prefix &newOmrPrefix : aNewOmrPrefixes) + for (const OmrPrefix &newOmrPrefix : aNewOmrPrefixes) { RouterAdv::RouteInfoOption *rio; - OT_ASSERT(bufferLength + RouterAdv::RouteInfoOption::OptionSizeForPrefix(newOmrPrefix.GetLength()) <= + OT_ASSERT(bufferLength + + RouterAdv::RouteInfoOption::OptionSizeForPrefix(newOmrPrefix.GetPrefix().GetLength()) <= sizeof(buffer)); rio = reinterpret_cast(buffer + bufferLength); rio->Init(); rio->SetRouteLifetime(kDefaultOmrPrefixLifetime); - rio->SetPrefix(newOmrPrefix); + rio->SetPreference(newOmrPrefix.GetPreference()); + rio->SetPrefix(newOmrPrefix.GetPrefix()); bufferLength += rio->GetSize(); @@ -1254,7 +1270,7 @@ void RoutingManager::UpdateDiscoveredOmrPrefix(const RouterAdv::RouteInfoOption // messages are usually faster than Thread Network Data propagation). // They are the reasons why we need both the checks. - VerifyOrExit(!mAdvertisedOmrPrefixes.Contains(prefix)); + VerifyOrExit(!mAdvertisedOmrPrefixes.ContainsMatching(prefix)); VerifyOrExit(!NetworkDataContainsOmrPrefix(prefix)); LogInfo("Discovered OMR prefix (%s, %u seconds) from %s", prefix.ToString().AsCString(), aRio.GetRouteLifetime(), @@ -1315,8 +1331,8 @@ void RoutingManager::InvalidateDiscoveredPrefixes(void) // Data). if ((prefix.GetExpireTime() <= now) || - (!prefix.IsOnLinkPrefix() && - (mAdvertisedOmrPrefixes.Contains(prefix.GetPrefix()) || NetworkDataContainsOmrPrefix(prefix.GetPrefix())))) + (!prefix.IsOnLinkPrefix() && (mAdvertisedOmrPrefixes.ContainsMatching(prefix.GetPrefix()) || + NetworkDataContainsOmrPrefix(prefix.GetPrefix())))) { UnpublishExternalRoute(prefix.GetPrefix()); @@ -1560,6 +1576,52 @@ uint32_t RoutingManager::ExternalPrefix::GetPrefixExpireDelay(uint32_t aValidLif return delay; } +//--------------------------------------------------------------------------------------------------------------------- +// OmrPrefix + +void RoutingManager::OmrPrefix::Init(const Ip6::Prefix &aPrefix, RoutePreference aPreference) +{ + mPrefix = aPrefix; + mPreference = aPreference; +} + +void RoutingManager::OmrPrefix::InitFrom(NetworkData::OnMeshPrefixConfig &aOnMeshPrefixConfig) +{ + Init(aOnMeshPrefixConfig.GetPrefix(), aOnMeshPrefixConfig.GetPreference()); +} + +bool RoutingManager::OmrPrefix::IsFavoredOver(const OmrPrefix &aOther) const +{ + // This method determines whether this OMR prefix is favored + // over `aOther` prefix. A prefix with higher preference is + // favored. If the preference is the same, then the smaller + // prefix (in the sense defined by `Ip6::Prefix`) is favored. + + return (mPreference > aOther.mPreference) || ((mPreference == aOther.mPreference) && (mPrefix < aOther.mPrefix)); +} + +RoutingManager::OmrPrefix::InfoString RoutingManager::OmrPrefix::ToString(void) const +{ + InfoString string; + + string.Append("%s (prf:", mPrefix.ToString().AsCString()); + + switch (mPreference) + { + case NetworkData::kRoutePreferenceHigh: + string.Append("high)"); + break; + case NetworkData::kRoutePreferenceMedium: + string.Append("med)"); + break; + case NetworkData::kRoutePreferenceLow: + string.Append("low)"); + break; + } + + return string; +} + } // namespace BorderRouter } // namespace ot diff --git a/src/core/border_router/routing_manager.hpp b/src/core/border_router/routing_manager.hpp index b4b501460..7afc06715 100644 --- a/src/core/border_router/routing_manager.hpp +++ b/src/core/border_router/routing_manager.hpp @@ -55,6 +55,7 @@ #include "common/error.hpp" #include "common/locator.hpp" #include "common/notifier.hpp" +#include "common/string.hpp" #include "common/timer.hpp" #include "net/ip6.hpp" #include "thread/network_data.hpp" @@ -278,7 +279,27 @@ private: bool mIsOnLinkPrefix; }; - typedef Array OmrPrefixArray; + class OmrPrefix // An OMR Prefix + { + public: + static constexpr uint16_t kInfoStringSize = 60; + typedef String InfoString; + + void Init(const Ip6::Prefix &aPrefix, RoutePreference aPreference); + void InitFrom(NetworkData::OnMeshPrefixConfig &aOnMeshPrefixConfig); + const Ip6::Prefix &GetPrefix(void) const { return mPrefix; } + RoutePreference GetPreference(void) const { return mPreference; } + void SetPreference(RoutePreference aPreference) { mPreference = aPreference; } + bool Matches(const Ip6::Prefix &aPrefix) const { return mPrefix == aPrefix; } + bool IsFavoredOver(const OmrPrefix &aOther) const; + InfoString ToString(void) const; + + private: + Ip6::Prefix mPrefix; + RoutePreference mPreference; + }; + + typedef Array OmrPrefixArray; typedef Array ExternalPrefixArray; void EvaluateState(void); diff --git a/src/core/thread/network_data_types.hpp b/src/core/thread/network_data_types.hpp index 06ff0a942..821534394 100644 --- a/src/core/thread/network_data_types.hpp +++ b/src/core/thread/network_data_types.hpp @@ -190,6 +190,14 @@ public: */ Ip6::Prefix &GetPrefix(void) { return AsCoreType(&mPrefix); } + /** + * This method gets the preference. + * + * @return The preference. + * + */ + RoutePreference GetPreference(void) const { return RoutePreferenceFromValue(RoutePreferenceToValue(mPreference)); } + #if OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE /** * This method indicates whether or not the prefix configuration is valid. From 1811ac763f94674f0770e9b2f98782422c07699a Mon Sep 17 00:00:00 2001 From: gabekassel Date: Thu, 2 Jun 2022 19:08:08 -0700 Subject: [PATCH 54/80] [cmake] add build option to configure outgoing beacon payload (#7782) --- etc/cmake/options.cmake | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/etc/cmake/options.cmake b/etc/cmake/options.cmake index 6eec0d623..d35b5d411 100644 --- a/etc/cmake/options.cmake +++ b/etc/cmake/options.cmake @@ -360,6 +360,11 @@ if (OT_TREL) target_compile_definitions(ot-config INTERFACE "OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE=1") endif() +option(OT_TX_BEACON_PAYLOAD "enable Thread beacon payload in outgoing beacons") +if (OT_TX_BEACON_PAYLOAD) + target_compile_definitions(ot-config INTERFACE "OPENTHREAD_CONFIG_MAC_OUTGOING_BEACON_PAYLOAD_ENABLE=1") +endif() + option(OT_UDP_FORWARD "enable UDP forward support") if(OT_UDP_FORWARD) target_compile_definitions(ot-config INTERFACE "OPENTHREAD_CONFIG_UDP_FORWARD_ENABLE=1") From c8556dfd6689abc2ff01e7b7d434c8c221ac32a2 Mon Sep 17 00:00:00 2001 From: Mason Tran Date: Thu, 2 Jun 2022 22:26:29 -0400 Subject: [PATCH 55/80] [cmake] add option for RCP specific mbedtls library (#7781) This commit enables a different mbedtls library to be used for RCP builds. This will make it possible to use a PSA Crypto mbedtls library for FTD/MTD images while using a non-PSA crypto library on RCPs. --- examples/apps/cli/radio.cmake | 6 +++++- src/cli/radio.cmake | 6 +++++- src/core/radio_cli.cmake | 6 +++++- 3 files changed, 15 insertions(+), 3 deletions(-) diff --git a/examples/apps/cli/radio.cmake b/examples/apps/cli/radio.cmake index dc3e4e981..4302adca5 100644 --- a/examples/apps/cli/radio.cmake +++ b/examples/apps/cli/radio.cmake @@ -37,13 +37,17 @@ if(NOT DEFINED OT_PLATFORM_LIB_RCP) set(OT_PLATFORM_LIB_RCP ${OT_PLATFORM_LIB}) endif() +if(NOT DEFINED OT_MBEDTLS_RCP) + set(OT_MBEDTLS_RCP ${OT_MBEDTLS}) +endif() + target_link_libraries(ot-cli-radio PRIVATE openthread-cli-radio ${OT_PLATFORM_LIB_RCP} openthread-radio-cli ${OT_PLATFORM_LIB_RCP} openthread-cli-radio - ${OT_MBEDTLS} + ${OT_MBEDTLS_RCP} ot-config ) diff --git a/src/cli/radio.cmake b/src/cli/radio.cmake index 9b69a9e6b..3e7354aa6 100644 --- a/src/cli/radio.cmake +++ b/src/cli/radio.cmake @@ -46,10 +46,14 @@ target_sources(openthread-cli-radio cli_output.cpp ) +if(NOT DEFINED OT_MBEDTLS_RCP) + set(OT_MBEDTLS_RCP ${OT_MBEDTLS}) +endif() + target_link_libraries(openthread-cli-radio PUBLIC openthread-radio PRIVATE - ${OT_MBEDTLS} + ${OT_MBEDTLS_RCP} ot-config ) diff --git a/src/core/radio_cli.cmake b/src/core/radio_cli.cmake index b7003d18d..b4e26fe18 100644 --- a/src/core/radio_cli.cmake +++ b/src/core/radio_cli.cmake @@ -47,8 +47,12 @@ if(OT_VENDOR_EXTENSION) target_sources(openthread-radio-cli PRIVATE ${OT_VENDOR_EXTENSION}) endif() +if(NOT DEFINED OT_MBEDTLS_RCP) + set(OT_MBEDTLS_RCP ${OT_MBEDTLS}) +endif() + target_link_libraries(openthread-radio-cli PRIVATE - ${OT_MBEDTLS} + ${OT_MBEDTLS_RCP} ot-config ) From 5daca7c684adf5124e659b0171fb1b8679e08750 Mon Sep 17 00:00:00 2001 From: Kangping Date: Sat, 4 Jun 2022 01:59:16 +0800 Subject: [PATCH 56/80] [dataset] use full Timestamp TLV value in otOperationalDataset (#7739) The `otOperationalDataset#mActiveTimestamp` should represent the full Active Timestamp TLV value but not only the seconds. The same for `otOperationalDataset#mPendingTimestamp`. --- include/openthread/dataset.h | 15 ++++++-- include/openthread/instance.h | 2 +- src/cli/README_DATASET.md | 8 ++--- src/cli/cli_dataset.cpp | 24 ++++++++----- src/core/meshcop/dataset.cpp | 29 ++++++++-------- src/core/meshcop/dataset.hpp | 12 +++---- src/core/meshcop/dataset_updater.cpp | 13 +++++-- src/core/meshcop/timestamp.cpp | 52 +++++++++++++++++++++++----- src/core/meshcop/timestamp.hpp | 26 ++++++++++++++ src/lib/spinel/radio_spinel_impl.hpp | 2 +- src/ncp/ncp_base_mtd.cpp | 16 ++++++--- tests/unit/test_meshcop.cpp | 3 +- 12 files changed, 150 insertions(+), 52 deletions(-) diff --git a/include/openthread/dataset.h b/include/openthread/dataset.h index a92f3a78c..63e1e26ad 100644 --- a/include/openthread/dataset.h +++ b/include/openthread/dataset.h @@ -213,6 +213,17 @@ typedef struct otOperationalDatasetComponents bool mIsChannelMaskPresent : 1; ///< TRUE if Channel Mask is present, FALSE otherwise. } otOperationalDatasetComponents; +/** + * This structure represents a Thread Dataset timestamp component. + * + */ +typedef struct otTimestamp +{ + uint64_t mSeconds; + uint16_t mTicks; + bool mAuthoritative; +} otTimestamp; + /** * This structure represents an Active or Pending Operational Dataset. * @@ -221,8 +232,8 @@ typedef struct otOperationalDatasetComponents */ typedef struct otOperationalDataset { - uint64_t mActiveTimestamp; ///< Active Timestamp - uint64_t mPendingTimestamp; ///< Pending Timestamp + otTimestamp mActiveTimestamp; ///< Active Timestamp + otTimestamp mPendingTimestamp; ///< Pending Timestamp otNetworkKey mNetworkKey; ///< Network Key otNetworkName mNetworkName; ///< Network Name otExtendedPanId mExtendedPanId; ///< Extended PAN ID diff --git a/include/openthread/instance.h b/include/openthread/instance.h index e0ff78f8b..5f91d8165 100644 --- a/include/openthread/instance.h +++ b/include/openthread/instance.h @@ -53,7 +53,7 @@ extern "C" { * @note This number versions both OpenThread platform and user APIs. * */ -#define OPENTHREAD_API_VERSION (218) +#define OPENTHREAD_API_VERSION (219) /** * @addtogroup api-instance diff --git a/src/cli/README_DATASET.md b/src/cli/README_DATASET.md index 1043bbaa0..87cd4d21c 100644 --- a/src/cli/README_DATASET.md +++ b/src/cli/README_DATASET.md @@ -196,7 +196,7 @@ Done Usage: `dataset activetimestamp [timestamp]` -Get active timestamp. +Get active timestamp seconds. ```bash > dataset activetimestamp @@ -204,7 +204,7 @@ Get active timestamp. Done ``` -Set active timestamp. +Set active timestamp seconds. ```bash > dataset activetimestamp 123456789 @@ -457,7 +457,7 @@ Done Usage: `dataset pendingtimestamp [timestamp]` -Get pending timestamp. +Get pending timestamp seconds. ```bash > dataset pendingtimestamp @@ -465,7 +465,7 @@ Get pending timestamp. Done ``` -Set pending timestamp. +Set pending timestamp seconds. ```bash > dataset pendingtimestamp 123456789 diff --git a/src/cli/cli_dataset.cpp b/src/cli/cli_dataset.cpp index efade0586..573436aea 100644 --- a/src/cli/cli_dataset.cpp +++ b/src/cli/cli_dataset.cpp @@ -51,12 +51,12 @@ otError Dataset::Print(otOperationalDataset &aDataset) { if (aDataset.mComponents.mIsPendingTimestampPresent) { - OutputLine("Pending Timestamp: %lu", aDataset.mPendingTimestamp); + OutputLine("Pending Timestamp: %lu", aDataset.mPendingTimestamp.mSeconds); } if (aDataset.mComponents.mIsActiveTimestampPresent) { - OutputLine("Active Timestamp: %lu", aDataset.mActiveTimestamp); + OutputLine("Active Timestamp: %lu", aDataset.mActiveTimestamp.mSeconds); } if (aDataset.mComponents.mIsChannelPresent) @@ -205,12 +205,14 @@ template <> otError Dataset::Process(Arg aArgs[]) { if (sDataset.mComponents.mIsActiveTimestampPresent) { - OutputLine("%lu", sDataset.mActiveTimestamp); + OutputLine("%lu", sDataset.mActiveTimestamp.mSeconds); } } else { - SuccessOrExit(error = aArgs[0].ParseAsUint64(sDataset.mActiveTimestamp)); + SuccessOrExit(error = aArgs[0].ParseAsUint64(sDataset.mActiveTimestamp.mSeconds)); + sDataset.mActiveTimestamp.mTicks = 0; + sDataset.mActiveTimestamp.mAuthoritative = false; sDataset.mComponents.mIsActiveTimestampPresent = true; } @@ -423,12 +425,14 @@ template <> otError Dataset::Process(Arg aArgs[]) { if (sDataset.mComponents.mIsPendingTimestampPresent) { - OutputLine("%lu", sDataset.mPendingTimestamp); + OutputLine("%lu", sDataset.mPendingTimestamp.mSeconds); } } else { - SuccessOrExit(error = aArgs[0].ParseAsUint64(sDataset.mPendingTimestamp)); + SuccessOrExit(error = aArgs[0].ParseAsUint64(sDataset.mPendingTimestamp.mSeconds)); + sDataset.mPendingTimestamp.mTicks = 0; + sDataset.mPendingTimestamp.mAuthoritative = false; sDataset.mComponents.mIsPendingTimestampPresent = true; } @@ -450,14 +454,18 @@ template <> otError Dataset::Process(Arg aArgs[]) if (*arg == "activetimestamp") { arg++; + SuccessOrExit(error = arg->ParseAsUint64(dataset.mActiveTimestamp.mSeconds)); + dataset.mActiveTimestamp.mTicks = 0; + dataset.mActiveTimestamp.mAuthoritative = false; dataset.mComponents.mIsActiveTimestampPresent = true; - SuccessOrExit(error = arg->ParseAsUint64(dataset.mActiveTimestamp)); } else if (*arg == "pendingtimestamp") { arg++; + SuccessOrExit(error = arg->ParseAsUint64(dataset.mPendingTimestamp.mSeconds)); + dataset.mPendingTimestamp.mTicks = 0; + dataset.mPendingTimestamp.mAuthoritative = false; dataset.mComponents.mIsPendingTimestampPresent = true; - SuccessOrExit(error = arg->ParseAsUint64(dataset.mPendingTimestamp)); } else if (*arg == "networkkey") { diff --git a/src/core/meshcop/dataset.cpp b/src/core/meshcop/dataset.cpp index 74d9a0919..047808594 100644 --- a/src/core/meshcop/dataset.cpp +++ b/src/core/meshcop/dataset.cpp @@ -43,6 +43,7 @@ #include "common/log.hpp" #include "mac/mac_types.hpp" #include "meshcop/meshcop_tlvs.hpp" +#include "meshcop/timestamp.hpp" #include "thread/mle_tlvs.hpp" namespace ot { @@ -69,10 +70,12 @@ Error Dataset::Info::GenerateRandom(Instance &aInstance) Clear(); - mActiveTimestamp = 1; - mChannel = preferredChannels.ChooseRandomChannel(); - mChannelMask = supportedChannels.GetMask(); - mPanId = Mac::GenerateRandomPanId(); + mActiveTimestamp.mSeconds = 1; + mActiveTimestamp.mTicks = 0; + mActiveTimestamp.mAuthoritative = false; + mChannel = preferredChannels.ChooseRandomChannel(); + mChannelMask = supportedChannels.GetMask(); + mPanId = Mac::GenerateRandomPanId(); AsCoreType(&mSecurityPolicy).SetToDefault(); SuccessOrExit(error = AsCoreType(&mNetworkKey).GenerateRandom()); @@ -193,7 +196,7 @@ void Dataset::ConvertTo(Info &aDatasetInfo) const switch (cur->GetType()) { case Tlv::kActiveTimestamp: - aDatasetInfo.SetActiveTimestamp(As(cur)->GetTimestamp().GetSeconds()); + aDatasetInfo.SetActiveTimestamp(As(cur)->GetTimestamp()); break; case Tlv::kChannel: @@ -237,7 +240,7 @@ void Dataset::ConvertTo(Info &aDatasetInfo) const break; case Tlv::kPendingTimestamp: - aDatasetInfo.SetPendingTimestamp(As(cur)->GetTimestamp().GetSeconds()); + aDatasetInfo.SetPendingTimestamp(As(cur)->GetTimestamp()); break; case Tlv::kPskc: @@ -286,20 +289,18 @@ Error Dataset::SetFrom(const Info &aDatasetInfo) if (aDatasetInfo.IsActiveTimestampPresent()) { - Timestamp timestamp; + Timestamp activeTimestamp; - timestamp.Clear(); - timestamp.SetSeconds(aDatasetInfo.GetActiveTimestamp()); - IgnoreError(SetTlv(Tlv::kActiveTimestamp, timestamp)); + aDatasetInfo.GetActiveTimestamp(activeTimestamp); + IgnoreError(SetTlv(Tlv::kActiveTimestamp, activeTimestamp)); } if (aDatasetInfo.IsPendingTimestampPresent()) { - Timestamp timestamp; + Timestamp pendingTimestamp; - timestamp.Clear(); - timestamp.SetSeconds(aDatasetInfo.GetPendingTimestamp()); - IgnoreError(SetTlv(Tlv::kPendingTimestamp, timestamp)); + aDatasetInfo.GetPendingTimestamp(pendingTimestamp); + IgnoreError(SetTlv(Tlv::kPendingTimestamp, pendingTimestamp)); } if (aDatasetInfo.IsDelayPresent()) diff --git a/src/core/meshcop/dataset.hpp b/src/core/meshcop/dataset.hpp index 4e200d6cf..380951373 100644 --- a/src/core/meshcop/dataset.hpp +++ b/src/core/meshcop/dataset.hpp @@ -203,7 +203,7 @@ public: * @returns The Active Timestamp in the Dataset. * */ - uint64_t GetActiveTimestamp(void) const { return mActiveTimestamp; } + void GetActiveTimestamp(Timestamp &aTimestamp) const { aTimestamp.SetFromTimestamp(mActiveTimestamp); } /** * This method sets the Active Timestamp in the Dataset. @@ -211,9 +211,9 @@ public: * @param[in] aTimestamp A Timestamp value. * */ - void SetActiveTimestamp(uint64_t aTimestamp) + void SetActiveTimestamp(const Timestamp &aTimestamp) { - mActiveTimestamp = aTimestamp; + aTimestamp.ConvertTo(mActiveTimestamp); mComponents.mIsActiveTimestampPresent = true; } @@ -234,7 +234,7 @@ public: * @returns The Pending Timestamp in the Dataset. * */ - uint64_t GetPendingTimestamp(void) const { return mPendingTimestamp; } + void GetPendingTimestamp(Timestamp &aTimestamp) const { aTimestamp.SetFromTimestamp(mPendingTimestamp); } /** * This method sets the Pending Timestamp in the Dataset. @@ -242,9 +242,9 @@ public: * @param[in] aTimestamp A Timestamp value. * */ - void SetPendingTimestamp(uint64_t aTimestamp) + void SetPendingTimestamp(const Timestamp &aTimestamp) { - mPendingTimestamp = aTimestamp; + aTimestamp.ConvertTo(mPendingTimestamp); mComponents.mIsPendingTimestampPresent = true; } diff --git a/src/core/meshcop/dataset_updater.cpp b/src/core/meshcop/dataset_updater.cpp index 00782b227..36d63d7f9 100644 --- a/src/core/meshcop/dataset_updater.cpp +++ b/src/core/meshcop/dataset_updater.cpp @@ -41,6 +41,7 @@ #include "common/locator_getters.hpp" #include "common/log.hpp" #include "common/random.hpp" +#include "meshcop/timestamp.hpp" namespace ot { namespace MeshCoP { @@ -193,9 +194,17 @@ void DatasetUpdater::HandleNotifierEvents(Events aEvents) { Finish(kErrorNone); } - else if (requestedDataset.GetActiveTimestamp() <= dataset.GetActiveTimestamp()) + else { - Finish(kErrorAlready); + Timestamp requestedDatasetTimestamp; + Timestamp activeDatasetTimestamp; + + requestedDataset.GetActiveTimestamp(requestedDatasetTimestamp); + dataset.GetActiveTimestamp(activeDatasetTimestamp); + if (Timestamp::Compare(requestedDatasetTimestamp, activeDatasetTimestamp) <= 0) + { + Finish(kErrorAlready); + } } } diff --git a/src/core/meshcop/timestamp.cpp b/src/core/meshcop/timestamp.cpp index 5d8f59bf6..08ed36b94 100644 --- a/src/core/meshcop/timestamp.cpp +++ b/src/core/meshcop/timestamp.cpp @@ -38,13 +38,23 @@ namespace ot { namespace MeshCoP { +void Timestamp::ConvertTo(otTimestamp &aTimestamp) const +{ + aTimestamp.mSeconds = GetSeconds(); + aTimestamp.mTicks = GetTicks(); + aTimestamp.mAuthoritative = GetAuthoritative(); +} + +void Timestamp::SetFromTimestamp(const otTimestamp &aTimestamp) +{ + SetSeconds(aTimestamp.mSeconds); + SetTicks(aTimestamp.mTicks); + SetAuthoritative(aTimestamp.mAuthoritative); +} + int Timestamp::Compare(const Timestamp *aFirst, const Timestamp *aSecond) { - int rval; - uint64_t firstSeconds; - uint64_t secondSeconds; - uint16_t firstTicks; - uint16_t secondTicks; + int rval; if (aFirst == nullptr) { @@ -62,22 +72,46 @@ int Timestamp::Compare(const Timestamp *aFirst, const Timestamp *aSecond) // Both are non-null. - firstSeconds = aFirst->GetSeconds(); - secondSeconds = aSecond->GetSeconds(); + rval = Compare(*aFirst, *aSecond); + +exit: + return rval; +} + +int Timestamp::Compare(const Timestamp &aFirst, const Timestamp &aSecond) +{ + int rval; + uint64_t firstSeconds; + uint64_t secondSeconds; + uint16_t firstTicks; + uint16_t secondTicks; + bool firstAuthoritative; + bool secondAuthoritative; + + firstSeconds = aFirst.GetSeconds(); + secondSeconds = aSecond.GetSeconds(); if (firstSeconds != secondSeconds) { ExitNow(rval = (firstSeconds > secondSeconds) ? 1 : -1); } - firstTicks = aFirst->GetTicks(); - secondTicks = aSecond->GetTicks(); + firstTicks = aFirst.GetTicks(); + secondTicks = aSecond.GetTicks(); if (firstTicks != secondTicks) { ExitNow(rval = (firstTicks > secondTicks) ? 1 : -1); } + firstAuthoritative = aFirst.GetAuthoritative(); + secondAuthoritative = aSecond.GetAuthoritative(); + + if (firstAuthoritative != secondAuthoritative) + { + ExitNow(rval = firstAuthoritative ? 1 : -1); + } + rval = 0; exit: diff --git a/src/core/meshcop/timestamp.hpp b/src/core/meshcop/timestamp.hpp index ea3b11583..b4a6bfd0a 100644 --- a/src/core/meshcop/timestamp.hpp +++ b/src/core/meshcop/timestamp.hpp @@ -39,6 +39,7 @@ #include +#include #include #include "common/clearable.hpp" @@ -59,6 +60,18 @@ OT_TOOL_PACKED_BEGIN class Timestamp : public Clearable { public: + /** + * This method converts the timestamp to `otTimestamp`. + * + */ + void ConvertTo(otTimestamp &aTimestamp) const; + + /** + * This method sets the timestamp from `otTimestamp`. + * + */ + void SetFromTimestamp(const otTimestamp &aTimestamp); + /** * This method returns the Seconds value. * @@ -143,6 +156,19 @@ public: */ static int Compare(const Timestamp *aFirst, const Timestamp *aSecond); + /** + * This static method compares two timestamps. + * + * @param[in] aFirst A reference to the first timestamp to compare. + * @param[in] aSecond A reference to the second timestamp to compare. + * + * @retval -1 if @p aFirst is less than @p aSecond (`aFirst < aSecond`). + * @retval 0 if @p aFirst is equal to @p aSecond (`aFirst == aSecond`). + * @retval 1 if @p aFirst is greater than @p aSecond (`aFirst > aSecond`). + * + */ + static int Compare(const Timestamp &aFirst, const Timestamp &aSecond); + private: static constexpr uint8_t kTicksOffset = 1; static constexpr uint16_t kTicksMask = 0x7fff << kTicksOffset; diff --git a/src/lib/spinel/radio_spinel_impl.hpp b/src/lib/spinel/radio_spinel_impl.hpp index b7650ddf7..0073817ae 100644 --- a/src/lib/spinel/radio_spinel_impl.hpp +++ b/src/lib/spinel/radio_spinel_impl.hpp @@ -752,7 +752,7 @@ otError RadioSpinel::ThreadDatasetHandler(con * Initially set Active Timestamp to 0. This is to allow the node to join the network * yet retrieve the full Active Dataset from a neighboring device if one exists. */ - opDataset.mActiveTimestamp = 0; + memset(&opDataset.mActiveTimestamp, 0, sizeof(opDataset.mActiveTimestamp)); opDataset.mComponents.mIsActiveTimestampPresent = true; SuccessOrExit(error = dataset.SetFrom(static_cast(opDataset))); diff --git a/src/ncp/ncp_base_mtd.cpp b/src/ncp/ncp_base_mtd.cpp index bd1f227cf..09fad54bd 100644 --- a/src/ncp/ncp_base_mtd.cpp +++ b/src/ncp/ncp_base_mtd.cpp @@ -1228,17 +1228,21 @@ otError NcpBase::EncodeOperationalDataset(const otOperationalDataset &aDataset) if (aDataset.mComponents.mIsActiveTimestampPresent) { + const otTimestamp &activeTimestamp = aDataset.mActiveTimestamp; + SuccessOrExit(error = mEncoder.OpenStruct()); SuccessOrExit(error = mEncoder.WriteUintPacked(SPINEL_PROP_DATASET_ACTIVE_TIMESTAMP)); - SuccessOrExit(error = mEncoder.WriteUint64(aDataset.mActiveTimestamp)); + SuccessOrExit(error = mEncoder.WriteUint64(activeTimestamp.mSeconds)); SuccessOrExit(error = mEncoder.CloseStruct()); } if (aDataset.mComponents.mIsPendingTimestampPresent) { + const otTimestamp &pendingTimestamp = aDataset.mPendingTimestamp; + SuccessOrExit(error = mEncoder.OpenStruct()); SuccessOrExit(error = mEncoder.WriteUintPacked(SPINEL_PROP_DATASET_PENDING_TIMESTAMP)); - SuccessOrExit(error = mEncoder.WriteUint64(aDataset.mPendingTimestamp)); + SuccessOrExit(error = mEncoder.WriteUint64(pendingTimestamp.mSeconds)); SuccessOrExit(error = mEncoder.CloseStruct()); } @@ -1400,7 +1404,9 @@ otError NcpBase::DecodeOperationalDataset(otOperationalDataset &aDataset, if (!aAllowEmptyValues || !mDecoder.IsAllReadInStruct()) { - SuccessOrExit(error = mDecoder.ReadUint64(aDataset.mActiveTimestamp)); + SuccessOrExit(error = mDecoder.ReadUint64(aDataset.mActiveTimestamp.mSeconds)); + aDataset.mActiveTimestamp.mTicks = 0; + aDataset.mActiveTimestamp.mAuthoritative = false; } aDataset.mComponents.mIsActiveTimestampPresent = true; @@ -1410,7 +1416,9 @@ otError NcpBase::DecodeOperationalDataset(otOperationalDataset &aDataset, if (!aAllowEmptyValues || !mDecoder.IsAllReadInStruct()) { - SuccessOrExit(error = mDecoder.ReadUint64(aDataset.mPendingTimestamp)); + SuccessOrExit(error = mDecoder.ReadUint64(aDataset.mPendingTimestamp.mSeconds)); + aDataset.mPendingTimestamp.mTicks = 0; + aDataset.mPendingTimestamp.mAuthoritative = false; } aDataset.mComponents.mIsPendingTimestampPresent = true; diff --git a/tests/unit/test_meshcop.cpp b/tests/unit/test_meshcop.cpp index bcf823ba4..6dae027de 100644 --- a/tests/unit/test_meshcop.cpp +++ b/tests/unit/test_meshcop.cpp @@ -139,7 +139,7 @@ void TestTimestamp(void) VerifyOrQuit(MeshCoP::Timestamp::Compare(&t1, &t2) == 0); t1.SetAuthoritative(true); - VerifyOrQuit(MeshCoP::Timestamp::Compare(&t1, &t2) == 0); + VerifyOrQuit(MeshCoP::Timestamp::Compare(&t1, &t2) > 0); t1.SetSeconds(1); VerifyOrQuit(t1.GetSeconds() == 1); @@ -147,6 +147,7 @@ void TestTimestamp(void) VerifyOrQuit(MeshCoP::Timestamp::Compare(&t2, &t1) < 0); t2.SetSeconds(1); + t2.SetAuthoritative(true); VerifyOrQuit(MeshCoP::Timestamp::Compare(&t1, &t2) == 0); printf("TestTimestamp() passed\n"); From 4384a3100b56c2bfa3934e97329a620a28720d88 Mon Sep 17 00:00:00 2001 From: Jonathan Hui Date: Mon, 6 Jun 2022 10:51:47 -0700 Subject: [PATCH 57/80] [mle] fix handling of MLE Orphan Announce messages (#7786) MLE Orphan Announce messages include a timestamp value that has both seconds and ticks set to zero, but authoritative bit set. However, the default Active Timestamp value is all zeros. As a result, an MLE Orphan Announce timestamp can appear to be greater than the default Active Timestamp value and cause a device to detach. This commit adds a special case to check for the Orphan Announce timestamp. --- src/core/meshcop/timestamp.hpp | 9 +++++++++ src/core/thread/mle.cpp | 18 +++++++++--------- 2 files changed, 18 insertions(+), 9 deletions(-) diff --git a/src/core/meshcop/timestamp.hpp b/src/core/meshcop/timestamp.hpp index b4a6bfd0a..1eace251d 100644 --- a/src/core/meshcop/timestamp.hpp +++ b/src/core/meshcop/timestamp.hpp @@ -140,6 +140,15 @@ public: */ void AdvanceRandomTicks(void); + /** + * This method indicates whether the timestamp indicates an MLE Orphan Announce message. + * + * @retval TRUE The timestamp indicates an Orphan Announce message. + * @retval FALSE If the timestamp does not indicate an Orphan Announce message. + * + */ + bool IsOrphanTimestamp(void) const { return GetSeconds() == 0 && GetTicks() == 0 && GetAuthoritative(); } + /** * This static method compares two timestamps. * diff --git a/src/core/thread/mle.cpp b/src/core/thread/mle.cpp index 99b93ef05..bde0550ed 100644 --- a/src/core/thread/mle.cpp +++ b/src/core/thread/mle.cpp @@ -4113,7 +4113,15 @@ void Mle::HandleAnnounce(RxInfo &aRxInfo) localTimestamp = Get().GetTimestamp(); - if (MeshCoP::Timestamp::Compare(×tamp, localTimestamp) > 0) + if (timestamp.IsOrphanTimestamp() || MeshCoP::Timestamp::Compare(×tamp, localTimestamp) < 0) + { + SendAnnounce(channel); + +#if OPENTHREAD_CONFIG_MLE_SEND_UNICAST_ANNOUNCE_RESPONSE + SendAnnounce(channel, aRxInfo.mMessageInfo.GetPeerAddr()); +#endif + } + else if (MeshCoP::Timestamp::Compare(×tamp, localTimestamp) > 0) { // No action is required if device is detached, and current // channel and pan-id match the values from the received MLE @@ -4136,14 +4144,6 @@ void Mle::HandleAnnounce(RxInfo &aRxInfo) LogNote("Delay processing Announce - channel %d, panid 0x%02x", channel, panId); } - else if (MeshCoP::Timestamp::Compare(×tamp, localTimestamp) < 0) - { - SendAnnounce(channel); - -#if OPENTHREAD_CONFIG_MLE_SEND_UNICAST_ANNOUNCE_RESPONSE - SendAnnounce(channel, aRxInfo.mMessageInfo.GetPeerAddr()); -#endif - } else { // Timestamps are equal. From 171fbd82cf57a399afbbace78514923451ea5da4 Mon Sep 17 00:00:00 2001 From: Jonathan Hui Date: Mon, 6 Jun 2022 10:52:15 -0700 Subject: [PATCH 58/80] [tests] relax MLE Adv delay check in Cert_5_3_06_RouterIdMask (#7788) Time delay between MLE Advertisements after reset can be up to 3.5 seconds. For example: - 0.0s: Reset Interval, reset interval to 1 seconds, random interval at 0.5 second - 0.5s: Transmit MLE Advertisement - 1.0s: Set interval to 2 seconds, random interval at 2 seconds - 3.0s: Receive MLE Advertisement, reset interval to 1 second, random interval at 1.0 second - 4.0s: Transmit MLE Advertisement --- tests/scripts/thread-cert/Cert_5_3_06_RouterIdMask.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/tests/scripts/thread-cert/Cert_5_3_06_RouterIdMask.py b/tests/scripts/thread-cert/Cert_5_3_06_RouterIdMask.py index b7a21d3d8..baf2b1c1f 100755 --- a/tests/scripts/thread-cert/Cert_5_3_06_RouterIdMask.py +++ b/tests/scripts/thread-cert/Cert_5_3_06_RouterIdMask.py @@ -179,10 +179,16 @@ class Cert_5_3_6_RouterIdMask(thread_cert.TestCase): filter_LLANMA().\ filter_mle_cmd(MLE_ADVERTISEMENT).\ must_next() + # Time between MLE Advertisements after reset can be up to 3.5 seconds + # 0.0s: Reset Interval, reset interval to 1 seconds, random interval at 0.5 second + # 0.5s: Transmit MLE Advertisement + # 1.0s: Set interval to 2 seconds, random interval at 2 seconds + # 3.0s: Receive MLE Advertisement, reset interval to 1 second, random interval at 1.0 second + # 4.0s: Transmit MLE Advertisement pkts.filter_wpan_src64(LEADER).\ filter_LLANMA().\ filter_mle_cmd(MLE_ADVERTISEMENT).\ - filter(lambda p: p.sniff_timestamp - _pkt.sniff_timestamp <= 3).\ + filter(lambda p: p.sniff_timestamp - _pkt.sniff_timestamp <= 4).\ must_next() # check router cost before and after the re-attach pkts.filter_wpan_src64(LEADER).\ From 9e887eb77acd004888725b94d6e0bbeae5f368ea Mon Sep 17 00:00:00 2001 From: Abtin Keshavarzian Date: Tue, 7 Jun 2022 11:13:20 -0700 Subject: [PATCH 59/80] [srp-client] message MTU check and single service mode (#7776) This commit adds a new mechanism in `Srp::Client`: When preparing an update message, client will check whether the prepared message fits in an IPv6 MTU (1280 bytes). If it does not, the SRP client would temporarily enable "single service mode" which ensures only a single service (along with its sub-types) to be appended in the SRP update message. After the message is sent SRP client would switch back to normal behavior and disables the single service mode. This change addresses situation where user may register many services (with multiple sub-types and long TXT data) which then may not fit in a single UDP message payload. This commit also adds `test_srp_many_services_mtu_check` test which verifies the newly added behavior. --- src/core/net/srp_client.cpp | 34 +++++ src/core/net/srp_client.hpp | 21 +++ .../platform/openthread-core-posix-config.h | 12 ++ tests/scripts/thread-cert/Makefile.am | 2 + .../test_srp_many_services_mtu_check.py | 124 ++++++++++++++++++ 5 files changed, 193 insertions(+) create mode 100755 tests/scripts/thread-cert/test_srp_many_services_mtu_check.py diff --git a/src/core/net/srp_client.cpp b/src/core/net/srp_client.cpp index a65fe9251..ee8f0403a 100644 --- a/src/core/net/srp_client.cpp +++ b/src/core/net/srp_client.cpp @@ -335,6 +335,8 @@ void Client::Stop(Requester aRequester, StopMode aMode) VerifyOrExit(GetState() != kStateStopped); + mSingleServiceMode.Disable(); + // State changes: // kAdding -> kToRefresh // kRefreshing -> kToRefresh @@ -403,6 +405,8 @@ void Client::Pause(void) /* (7) kRemoved -> */ kRemoved, }; + mSingleServiceMode.Disable(); + // State changes: // kAdding -> kToRefresh // kRefreshing -> kToRefresh @@ -710,6 +714,11 @@ void Client::ChangeHostAndServiceStates(const ItemState *aNewStates) for (Service &service : mServices) { + if (mSingleServiceMode.IsEnabled() && mSingleServiceMode.GetService() != &service) + { + continue; + } + service.SetState(aNewStates[service.GetState()]); } @@ -768,9 +777,21 @@ void Client::SendUpdate(void) Error error = kErrorNone; Message *message = mSocket.NewMessage(0); + uint32_t length; VerifyOrExit(message != nullptr, error = kErrorNoBufs); SuccessOrExit(error = PrepareUpdateMessage(*message)); + + length = message->GetLength() + sizeof(Ip6::Udp::Header) + sizeof(Ip6::Header); + + if (length >= Ip6::kMaxDatagramLength) + { + LogInfo("Msg len %u is larger than MTU, enabling single service mode", length); + mSingleServiceMode.Enable(); + IgnoreError(message->SetLength(0)); + SuccessOrExit(error = PrepareUpdateMessage(*message)); + } + SuccessOrExit(error = mSocket.SendTo(*message, Ip6::MessageInfo())); LogInfo("Send update"); @@ -808,6 +829,7 @@ exit: LogInfo("Failed to send update: %s", ErrorToString(error)); + mSingleServiceMode.Disable(); FreeMessage(message); SetState(kStateToRetry); @@ -882,6 +904,11 @@ Error Client::PrepareUpdateMessage(Message &aMessage) for (Service &service : mServices) { SuccessOrExit(error = AppendServiceInstructions(service, aMessage, info)); + + if (mSingleServiceMode.IsEnabled() && (mSingleServiceMode.GetService() != nullptr)) + { + break; + } } } @@ -1054,6 +1081,11 @@ Error Client::AppendServiceInstructions(Service &aService, Message &aMessage, In } #endif + if (mSingleServiceMode.IsEnabled()) + { + mSingleServiceMode.SetService(aService); + } + exit: return error; } @@ -1473,6 +1505,7 @@ void Client::ProcessResponse(Message &aMessage) // kRemoving -> kRemoved ChangeHostAndServiceStates(kNewStateOnUpdateDone); + mSingleServiceMode.Disable(); HandleUpdateDone(); UpdateState(); @@ -1742,6 +1775,7 @@ void Client::HandleTimer(void) break; case kStateUpdating: + mSingleServiceMode.Disable(); LogRetryWaitInterval(); LogInfo("Timed out, no response"); GrowRetryWaitInterval(); diff --git a/src/core/net/srp_client.hpp b/src/core/net/srp_client.hpp index 4a3502bb6..fb367719d 100644 --- a/src/core/net/srp_client.hpp +++ b/src/core/net/srp_client.hpp @@ -854,6 +854,26 @@ private: kKeepRetryInterval, }; + class SingleServiceMode + { + public: + SingleServiceMode(void) + : mEnabled(false) + , mService(nullptr) + { + } + + void Enable(void) { mEnabled = true, mService = nullptr; } + void Disable(void) { mEnabled = false; } + bool IsEnabled(void) const { return mEnabled; } + Service *GetService(void) { return mService; } + void SetService(Service &aService) { mService = &aService; } + + private: + bool mEnabled; + Service *mService; + }; + #if OPENTHREAD_CONFIG_SRP_CLIENT_AUTO_START_API_ENABLE class AutoStart : Clearable { @@ -995,6 +1015,7 @@ private: const char * mDomainName; HostInfo mHostInfo; LinkedList mServices; + SingleServiceMode mSingleServiceMode; TimerMilli mTimer; #if OPENTHREAD_CONFIG_SRP_CLIENT_AUTO_START_API_ENABLE AutoStart mAutoStart; diff --git a/src/posix/platform/openthread-core-posix-config.h b/src/posix/platform/openthread-core-posix-config.h index a9754656e..6708751f2 100644 --- a/src/posix/platform/openthread-core-posix-config.h +++ b/src/posix/platform/openthread-core-posix-config.h @@ -287,4 +287,16 @@ #define OPENTHREAD_CONFIG_LOG_PREPEND_UPTIME 1 #endif +/** + * @def OPENTHREAD_CONFIG_SRP_CLIENT_BUFFERS_MAX_SERVICES + * + * Specifies number of service entries in the SRP client service pool. + * + * This config is applicable only when `OPENTHREAD_CONFIG_SRP_CLIENT_BUFFERS_ENABLE` is enabled. + * + */ +#ifndef OPENTHREAD_CONFIG_SRP_CLIENT_BUFFERS_MAX_SERVICES +#define OPENTHREAD_CONFIG_SRP_CLIENT_BUFFERS_MAX_SERVICES 20 +#endif + #endif // OPENTHREAD_CORE_POSIX_CONFIG_H_ diff --git a/tests/scripts/thread-cert/Makefile.am b/tests/scripts/thread-cert/Makefile.am index 200c46a3c..2604dc504 100644 --- a/tests/scripts/thread-cert/Makefile.am +++ b/tests/scripts/thread-cert/Makefile.am @@ -193,6 +193,7 @@ EXTRA_DIST = \ test_srp_client_remove_host.py \ test_srp_client_save_server_info.py \ test_srp_lease.py \ + test_srp_many_services_mtu_check.py \ test_srp_name_conflicts.py \ test_srp_register_single_service.py \ test_srp_server_anycast_mode.py \ @@ -270,6 +271,7 @@ check_SCRIPTS = \ test_srp_client_remove_host.py \ test_srp_client_save_server_info.py \ test_srp_lease.py \ + test_srp_many_services_mtu_check.py \ test_srp_name_conflicts.py \ test_srp_register_single_service.py \ test_srp_server_anycast_mode.py \ diff --git a/tests/scripts/thread-cert/test_srp_many_services_mtu_check.py b/tests/scripts/thread-cert/test_srp_many_services_mtu_check.py new file mode 100755 index 000000000..87ea05210 --- /dev/null +++ b/tests/scripts/thread-cert/test_srp_many_services_mtu_check.py @@ -0,0 +1,124 @@ +#!/usr/bin/env python3 +# +# Copyright (c) 2022, The OpenThread Authors. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. Neither the name of the copyright holder nor the +# names of its contributors may be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. +# + +import ipaddress +import unittest + +import command +import config +import thread_cert + +# Test description: +# +# This test verifies SRP client behavior when there are many services +# causing SRP Update message going over the IPv6 MTU size. In such +# a case the SRP client attempts to register a single service at +# a time. +# +# Topology: +# +# LEADER (SRP server) +# | +# | +# ROUTER (SRP client) +# + +SERVER = 1 +CLIENT = 2 + + +class SrpManyServicesMtuCheck(thread_cert.TestCase): + USE_MESSAGE_FACTORY = False + SUPPORT_NCP = False + + TOPOLOGY = { + SERVER: { + 'name': 'SRP_SERVER', + 'mode': 'rdn', + }, + CLIENT: { + 'name': 'SRP_CLIENT', + 'mode': 'rdn', + }, + } + + def test(self): + server = self.nodes[SERVER] + client = self.nodes[CLIENT] + + # Start the server & client devices. + + server.start() + self.simulator.go(5) + self.assertEqual(server.get_state(), 'leader') + + client.start() + self.simulator.go(config.ROUTER_STARTUP_DELAY) + self.assertEqual(client.get_state(), 'router') + + server.srp_server_set_enabled(True) + client.srp_client_enable_auto_start_mode() + self.simulator.go(5) + + # Register 8 services with long name, 6 sub-types and long txt record. + # The 8 services won't be fit in a single MTU (1280 bytes) UDP message + # SRP Client would then register services one by one. + + num_services = 8 + + client.srp_client_set_host_name('hosthosthosthosthosthosthosthosthosthosthosthosthosthosthosthos') + client.srp_client_set_host_address('2001::1') + + txt_info = ["xyz=987654321", "uvw=abcdefghijklmnopqrstu", "qwp=564321abcdefghij"] + + for num in range(num_services): + name = chr(ord('a') + num) * 63 + client.srp_client_add_service( + name, + '_longsrvname._udp,_subtype1,_subtype2,_subtype3,_subtype4,_subtype5,_subtype6', + 1977, + txt_entries=txt_info) + + self.simulator.go(10) + + # Make sure all services are successfully registered + # (from both client and server perspectives). + + client_services = client.srp_client_get_services() + self.assertEqual(len(client_services), num_services) + + for service in client_services: + self.assertEqual(service['state'], 'Registered') + + server_services = server.srp_server_get_services() + self.assertEqual(len(server_services), num_services) + + +if __name__ == '__main__': + unittest.main() From 0c48ade2ffb6fafd17a9b1ff94e3da453e4c519d Mon Sep 17 00:00:00 2001 From: Jonathan Hui Date: Wed, 8 Jun 2022 11:44:23 -0700 Subject: [PATCH 60/80] [tests] change 1.2 builds to 1.3 (#7756) --- .github/workflows/otbr.yml | 10 ++--- .github/workflows/otci.yml | 2 +- .github/workflows/simulation-1.2.yml | 44 +++++++++---------- script/check-arm-build-autotools | 2 +- script/check-arm-build-cmake | 2 +- script/check-gn-build | 4 +- script/check-simulation-build-autotools | 14 +++--- script/check-simulation-build-cmake | 16 +++---- script/check-size | 6 +-- script/make-pretty | 6 +-- script/test | 42 +++++++++--------- src/core/BUILD.gn | 2 + tests/scripts/thread-cert/config.py | 1 + tests/scripts/thread-cert/node.py | 22 +++++----- tests/scripts/thread-cert/pktverify/consts.py | 2 + .../thread-cert/pktverify/packet_verifier.py | 2 +- tests/scripts/thread-cert/run_cert_suite.py | 4 +- tests/scripts/thread-cert/thread_cert.py | 11 +++-- ...Power_6_1_07_PreferringARouterOverAReed.py | 8 ++-- .../scripts/thread-cert/v1_2_router_5_1_1.py | 6 +-- .../v1_2_test_enhanced_keep_alive.py | 6 +-- 21 files changed, 111 insertions(+), 101 deletions(-) diff --git a/.github/workflows/otbr.yml b/.github/workflows/otbr.yml index 159c84d59..368dc72df 100644 --- a/.github/workflows/otbr.yml +++ b/.github/workflows/otbr.yml @@ -46,7 +46,7 @@ jobs: REFERENCE_DEVICE: 1 VIRTUAL_TIME: 0 PACKET_VERIFICATION: 1 - THREAD_VERSION: 1.2 + THREAD_VERSION: 1.3 INTER_OP: 1 COVERAGE: 1 MULTIPLY: 1 @@ -83,12 +83,12 @@ jobs: sudo -E ./script/test cert_suite ./tests/scripts/thread-cert/backbone/*.py || (sudo chmod a+r *.log *.json *.pcap && false) - uses: actions/upload-artifact@v2 with: - name: cov-thread-1-2-backbone-docker + name: cov-thread-1-3-backbone-docker path: /tmp/coverage/ - uses: actions/upload-artifact@v2 if: ${{ failure() }} with: - name: thread-1-2-backbone-results + name: thread-1-3-backbone-results path: | *.pcap *.json @@ -100,7 +100,7 @@ jobs: ./script/test generate_coverage gcc - uses: actions/upload-artifact@v2 with: - name: cov-thread-1-2-backbone + name: cov-thread-1-3-backbone path: tmp/coverage.info thread-border-router: @@ -144,7 +144,7 @@ jobs: REFERENCE_DEVICE: 1 VIRTUAL_TIME: 0 PACKET_VERIFICATION: ${{ matrix.packet_verification }} - THREAD_VERSION: 1.2 + THREAD_VERSION: 1.3 INTER_OP: 1 COVERAGE: 1 MULTIPLY: 1 diff --git a/.github/workflows/otci.yml b/.github/workflows/otci.yml index 5023d7c16..a1e3a9b99 100644 --- a/.github/workflows/otci.yml +++ b/.github/workflows/otci.yml @@ -61,7 +61,7 @@ jobs: - name: Build run: | ./bootstrap - make -f examples/Makefile-simulation THREAD_VERSION=1.2 DUA=1 MLR=1 BACKBONE_ROUTER=1 CSL_RECEIVER=1 + make -f examples/Makefile-simulation THREAD_VERSION=1.3 DUA=1 MLR=1 BACKBONE_ROUTER=1 CSL_RECEIVER=1 - name: Install OTCI Python Library run: | (cd tools/otci && python3 setup.py install --user) diff --git a/.github/workflows/simulation-1.2.yml b/.github/workflows/simulation-1.2.yml index 7b7a949c9..26166466d 100644 --- a/.github/workflows/simulation-1.2.yml +++ b/.github/workflows/simulation-1.2.yml @@ -26,7 +26,7 @@ # POSSIBILITY OF SUCH DAMAGE. # -name: Simulation 1.2 +name: Simulation 1.3 on: [push, pull_request] @@ -40,15 +40,15 @@ jobs: GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}" if: "github.ref != 'refs/heads/main'" - thread-1-2: - name: thread-1-2-${{ matrix.compiler.c }}-${{ matrix.arch }} + thread-1-3: + name: thread-1-3-${{ matrix.compiler.c }}-${{ matrix.arch }} runs-on: ubuntu-20.04 env: CFLAGS: -${{ matrix.arch }} CXXFLAGS: -${{ matrix.arch }} LDFLAGS: -${{ matrix.arch }} COVERAGE: 1 - THREAD_VERSION: 1.2 + THREAD_VERSION: 1.3 VIRTUAL_TIME: 1 INTER_OP: 1 CC: ${{ matrix.compiler.c }} @@ -87,12 +87,12 @@ jobs: - uses: actions/upload-artifact@v2 if: ${{ failure() }} with: - name: thread-1-2-${{ matrix.compiler.c }}-${{ matrix.arch }}-pcaps + name: thread-1-3-${{ matrix.compiler.c }}-${{ matrix.arch }}-pcaps path: "*.pcap" - uses: actions/upload-artifact@v2 if: ${{ failure() && env.CRASHED == '1' }} with: - name: core-packet-verification-thread-1-2 + name: core-packet-verification-thread-1-3 path: | ./ot-core-dump/* - name: Generate Coverage @@ -100,7 +100,7 @@ jobs: ./script/test generate_coverage "${{ matrix.compiler.gcov }}" - uses: actions/upload-artifact@v2 with: - name: cov-thread-1-2-${{ matrix.compiler.c }}-${{ matrix.arch }} + name: cov-thread-1-3-${{ matrix.compiler.c }}-${{ matrix.arch }} path: tmp/coverage.info packet-verification-low-power: @@ -110,7 +110,7 @@ jobs: VIRTUAL_TIME: 1 COVERAGE: 1 PACKET_VERIFICATION: 1 - THREAD_VERSION: 1.2 + THREAD_VERSION: 1.3 MAC_FILTER: 1 INTER_OP: 1 INTER_OP_BBR: 0 @@ -164,13 +164,13 @@ jobs: name: cov-packet-verification-low-power path: tmp/coverage.info - packet-verification-1-1-on-1-2: + packet-verification-1-1-on-1-3: runs-on: ubuntu-20.04 env: REFERENCE_DEVICE: 1 VIRTUAL_TIME: 1 PACKET_VERIFICATION: 1 - THREAD_VERSION: 1.2 + THREAD_VERSION: 1.3 MULTIPLY: 3 steps: - uses: actions/checkout@v2 @@ -193,7 +193,7 @@ jobs: - uses: actions/upload-artifact@v2 if: ${{ failure() }} with: - name: packet-verification-1.1-on-1.2-pcaps + name: packet-verification-1.1-on-1.3-pcaps path: | *.pcap *.json @@ -202,14 +202,14 @@ jobs: ./script/test generate_coverage gcc - uses: actions/upload-artifact@v2 with: - name: cov-packet-verification-1-1-on-1-2 + name: cov-packet-verification-1-1-on-1-3 path: tmp/coverage.info expects: runs-on: ubuntu-20.04 env: COVERAGE: 1 - THREAD_VERSION: 1.2 + THREAD_VERSION: 1.3 VIRTUAL_TIME: 0 steps: - uses: actions/checkout@v2 @@ -232,7 +232,7 @@ jobs: - uses: actions/upload-artifact@v2 if: ${{ failure() && env.CRASHED == '1' }} with: - name: core-expect-1-2 + name: core-expect-1-3 path: | ./ot-core-dump/* - name: Generate Coverage @@ -243,13 +243,13 @@ jobs: name: cov-expects path: tmp/coverage.info - thread-1-2-posix: + thread-1-3-posix: runs-on: ubuntu-20.04 env: COVERAGE: 1 PYTHONUNBUFFERED: 1 READLINE: readline - THREAD_VERSION: 1.2 + THREAD_VERSION: 1.3 OT_NODE_TYPE: rcp USE_MTD: 1 VIRTUAL_TIME: 1 @@ -285,12 +285,12 @@ jobs: - uses: actions/upload-artifact@v2 if: ${{ failure() }} with: - name: thread-1-2-posix-pcaps + name: thread-1-3-posix-pcaps path: "*.pcap" - uses: actions/upload-artifact@v2 if: ${{ failure() && env.CRASHED == '1' }} with: - name: core-thread-1-2-posix + name: core-thread-1-3-posix path: | ./ot-core-dump/* - name: Generate Coverage @@ -298,16 +298,16 @@ jobs: ./script/test generate_coverage gcc - uses: actions/upload-artifact@v2 with: - name: cov-thread-1-2-posix + name: cov-thread-1-3-posix path: tmp/coverage.info upload-coverage: needs: - - thread-1-2 + - thread-1-3 - packet-verification-low-power - - packet-verification-1-1-on-1-2 + - packet-verification-1-1-on-1-3 - expects - - thread-1-2-posix + - thread-1-3-posix runs-on: ubuntu-20.04 steps: - uses: actions/checkout@v2 diff --git a/script/check-arm-build-autotools b/script/check-arm-build-autotools index 4390ff8da..e994c2025 100755 --- a/script/check-arm-build-autotools +++ b/script/check-arm-build-autotools @@ -43,7 +43,7 @@ build_cc2538() "DNS_CLIENT=1" "JOINER=1" "SLAAC=1" - # cc2538 does not have enough resources to support Thread 1.2 + # cc2538 does not have enough resources to support Thread 1.3 "THREAD_VERSION=1.1" ) diff --git a/script/check-arm-build-cmake b/script/check-arm-build-cmake index d8210ab1f..3ee62b06e 100755 --- a/script/check-arm-build-cmake +++ b/script/check-arm-build-cmake @@ -51,7 +51,7 @@ reset_source() build_cc2538() { local options=( - # cc2538 does not have enough resources to support Thread 1.2 + # cc2538 does not have enough resources to support Thread 1.3 "-DOT_THREAD_VERSION=1.1" ) diff --git a/script/check-gn-build b/script/check-gn-build index 2a02c3ffe..68b17a7b7 100755 --- a/script/check-gn-build +++ b/script/check-gn-build @@ -43,10 +43,10 @@ main() ninja -C gn-out test -f gn-out/obj/src/core/libopenthread-ftd.a - # Check GN build for OT1.2 + # Check GN build for OT1.3 rm gn-out -r || true mkdir gn-out - echo 'openthread_config_thread_version = "1.2"' >gn-out/args.gn + echo 'openthread_config_thread_version = "1.3"' >gn-out/args.gn gn gen --check gn-out gn args gn-out --list ninja -C gn-out diff --git a/script/check-simulation-build-autotools b/script/check-simulation-build-autotools index 086f2688c..6605c5209 100755 --- a/script/check-simulation-build-autotools +++ b/script/check-simulation-build-autotools @@ -93,7 +93,7 @@ build_all_features() "-DOPENTHREAD_CONFIG_MAC_OUTGOING_BEACON_PAYLOAD_ENABLE=1" ) - local options_1_2=( + local options_1_3=( "-DOPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE=1" "-DOPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE=1" "-DOPENTHREAD_CONFIG_DUA_ENABLE=1" @@ -110,15 +110,15 @@ build_all_features() reset_source make -f examples/Makefile-simulation THREAD_VERSION=1.1 FULL_LOGS=1 - # Build Thread 1.2 with full features and logs - export CPPFLAGS="${options[*]} ${options_1_2[*]} -DOPENTHREAD_CONFIG_LOG_OUTPUT=OT_LOG_OUTPUT_NONE" + # Build Thread 1.3 with full features and logs + export CPPFLAGS="${options[*]} ${options_1_3[*]} -DOPENTHREAD_CONFIG_LOG_OUTPUT=OT_LOG_OUTPUT_NONE" reset_source - make -f examples/Makefile-simulation THREAD_VERSION=1.2 + make -f examples/Makefile-simulation THREAD_VERSION=1.3 - # Build Thread 1.2 with full features and full logs - export CPPFLAGS="${options[*]} ${options_1_2[*]}" + # Build Thread 1.3 with full features and full logs + export CPPFLAGS="${options[*]} ${options_1_3[*]}" reset_source - make -f examples/Makefile-simulation THREAD_VERSION=1.2 FULL_LOGS=1 + make -f examples/Makefile-simulation THREAD_VERSION=1.3 FULL_LOGS=1 # Build Thread 1.1 with ASSERT disabled export CPPFLAGS="${options[*]} -DOPENTHREAD_CONFIG_ASSERT_ENABLE=0" diff --git a/script/check-simulation-build-cmake b/script/check-simulation-build-cmake index e9a3b4549..f296ab1ec 100755 --- a/script/check-simulation-build-cmake +++ b/script/check-simulation-build-cmake @@ -53,7 +53,7 @@ build_all_features() -DOT_FTD=OFF \ -DOT_MTD=OFF - # Thread 1.2 options + # Thread 1.3 options local options=( "-DOT_BACKBONE_ROUTER=ON" "-DOT_BORDER_ROUTING=ON" @@ -61,18 +61,18 @@ build_all_features() "-DOT_MLR=ON" "-DOT_OTNS=ON" "-DOT_SIMULATION_VIRTUAL_TIME=ON" - "-DOT_THREAD_VERSION=1.2" + "-DOT_THREAD_VERSION=1.3" ) - # Build Thread 1.2 with full features + # Build Thread 1.3 with full features reset_source "$(dirname "$0")"/cmake-build simulation "${options[@]}" -DOT_DUA=ON - # Build Thread 1.2 Backbone Router without DUA ND Proxying + # Build Thread 1.3 Backbone Router without DUA ND Proxying reset_source "$(dirname "$0")"/cmake-build simulation "${options[@]}" -DOT_BACKBONE_ROUTER_DUA_NDPROXYING=OFF - # Build Thread 1.2 Backbone Router without Multicast Routing + # Build Thread 1.3 Backbone Router without Multicast Routing reset_source "$(dirname "$0")"/cmake-build simulation "${options[@]}" -DOT_BACKBONE_ROUTER_MULTICAST_ROUTING=OFF @@ -82,11 +82,11 @@ build_all_features() -DOT_THREAD_VERSION=1.1 \ -DOT_VENDOR_EXTENSION=../../src/core/common/extension_example.cpp - # Build Thread 1.2 with no additional features + # Build Thread 1.3 with no additional features reset_source - "$(dirname "$0")"/cmake-build simulation -DOT_THREAD_VERSION=1.2 + "$(dirname "$0")"/cmake-build simulation -DOT_THREAD_VERSION=1.3 - # Build Thread 1.2 with full features and OT_ASSERT=OFF + # Build Thread 1.3 with full features and OT_ASSERT=OFF reset_source "$(dirname "$0")"/cmake-build simulation "${options[@]}" -DOT_DUA=ON -DOT_ASSERT=OFF diff --git a/script/check-size b/script/check-size index 2163d0576..e81c0bfaf 100755 --- a/script/check-size +++ b/script/check-size @@ -151,9 +151,9 @@ size_nrf52840_version() local thread_version=$1 - if [[ ${thread_version} == "1.2" ]]; then + if [[ ${thread_version} != "1.1" ]]; then options+=( - "-DOT_THREAD_VERSION=1.2" + "-DOT_THREAD_VERSION=1.3" "-DOT_BACKBONE_ROUTER=ON" "-DOT_DUA=ON" "-DOT_MLR=ON" @@ -243,7 +243,7 @@ size_nrf52840() "${reporter}" init OpenThread size_nrf52840_version 1.1 - size_nrf52840_version 1.2 + size_nrf52840_version 1.3 "${reporter}" post } diff --git a/script/make-pretty b/script/make-pretty index 70984de7c..22201c3f1 100755 --- a/script/make-pretty +++ b/script/make-pretty @@ -121,7 +121,7 @@ readonly OT_CLANG_TIDY_BUILD_OPTS=( '-DOT_SNTP_CLIENT=ON' '-DOT_SRP_CLIENT=ON' '-DOT_SRP_SERVER=ON' - '-DOT_THREAD_VERSION=1.2' + '-DOT_THREAD_VERSION=1.3' '-DOT_TREL=ON' '-DOT_COVERAGE=ON' '-DOT_LOG_LEVEL_DYNAMIC=ON' @@ -177,7 +177,7 @@ do_clang_tidy_fix() (mkdir -p ./build/cmake-tidy \ && cd ./build/cmake-tidy \ - && THREAD_VERSION=1.2 cmake "${OT_CLANG_TIDY_BUILD_OPTS[@]}" ../.. \ + && THREAD_VERSION=1.3 cmake "${OT_CLANG_TIDY_BUILD_OPTS[@]}" ../.. \ && ../../script/clang-tidy -header-filter='.*' -checks="${OT_CLANG_TIDY_CHECKS}" -j"$OT_BUILD_JOBS" "${OT_CLANG_TIDY_FIX_DIRS[@]}" -fix) } @@ -190,7 +190,7 @@ do_clang_tidy_check() ( mkdir -p ./build/cmake-tidy \ && cd ./build/cmake-tidy \ - && THREAD_VERSION=1.2 cmake "${OT_CLANG_TIDY_BUILD_OPTS[@]}" ../.. \ + && THREAD_VERSION=1.3 cmake "${OT_CLANG_TIDY_BUILD_OPTS[@]}" ../.. \ && ../../script/clang-tidy -header-filter='.*' -checks="${OT_CLANG_TIDY_CHECKS}" -j"$OT_BUILD_JOBS" "${OT_CLANG_TIDY_FIX_DIRS[@]}" \ | grep -v -E "third_party" >output.txt if grep -q "warning: \|error: " output.txt; then diff --git a/script/test b/script/test index 2c5226145..631013ad2 100755 --- a/script/test +++ b/script/test @@ -42,7 +42,7 @@ readonly OT_COLOR_NONE='\033[0m' readonly OT_NODE_TYPE="${OT_NODE_TYPE:-cli}" readonly OT_NATIVE_IP="${OT_NATIVE_IP:-0}" -readonly THREAD_VERSION="${THREAD_VERSION:-1.2}" +readonly THREAD_VERSION="${THREAD_VERSION:-1.3}" readonly INTER_OP="${INTER_OP:-0}" readonly VERBOSE="${VERBOSE:-0}" readonly BORDER_ROUTING="${BORDER_ROUTING:-1}" @@ -81,7 +81,7 @@ build_simulation() options+=("-DOT_FULL_LOGS=ON") fi - if [[ ${version} == "1.2" ]]; then + if [[ ${version} != "1.1" ]]; then options+=("-DOT_DUA=ON") options+=("-DOT_MLR=ON") fi @@ -90,7 +90,7 @@ build_simulation() options+=("-DOT_SIMULATION_VIRTUAL_TIME=ON") fi - if [[ ${version} == "1.2" ]]; then + if [[ ${version} != "1.1" ]]; then options+=("-DOT_CSL_RECEIVER=ON") options+=("-DOT_LINK_METRICS_INITIATOR=ON") options+=("-DOT_LINK_METRICS_SUBJECT=ON") @@ -106,7 +106,7 @@ build_simulation() OT_CMAKE_NINJA_TARGET=ot-rcp OT_CMAKE_BUILD_DIR="${OT_BUILDDIR}/openthread-simulation-${version}" "${OT_SRCDIR}"/script/cmake-build simulation "${options[@]}" "-DOT_SIMULATION_VIRTUAL_TIME_UART=ON" fi - if [[ ${version} == "1.2" && ${INTER_OP_BBR} == 1 ]]; then + if [[ ${version} != "1.1" && ${INTER_OP_BBR} == 1 ]]; then options+=("-DOT_BACKBONE_ROUTER=ON") @@ -124,7 +124,7 @@ build_posix() local version="$1" local options=("-DOT_MESSAGE_USE_HEAP=ON" "-DOT_THREAD_VERSION=${version}" "-DBUILD_TESTING=ON") - if [[ ${version} == "1.2" ]]; then + if [[ ${version} != "1.1" ]]; then options+=("-DOT_DUA=ON") options+=("-DOT_MLR=ON") fi @@ -147,7 +147,7 @@ build_posix() OT_CMAKE_BUILD_DIR="${OT_BUILDDIR}/openthread-posix-${version}" "${OT_SRCDIR}"/script/cmake-build posix "${options[@]}" - if [[ ${version} == "1.2" && ${INTER_OP_BBR} == 1 ]]; then + if [[ ${version} != "1.1" && ${INTER_OP_BBR} == 1 ]]; then options+=("-DOT_BACKBONE_ROUTER=ON") @@ -170,7 +170,7 @@ do_build() { build_for_one_version "${THREAD_VERSION}" - if [[ ${THREAD_VERSION} == "1.2" && ${INTER_OP} == "1" ]]; then + if [[ ${THREAD_VERSION} != "1.1" && ${INTER_OP} == "1" ]]; then build_for_one_version 1.1 fi } @@ -201,8 +201,8 @@ do_unit() { do_unit_version "${THREAD_VERSION}" - if [[ ${THREAD_VERSION} == "1.2" && ${INTER_OP_BBR} == 1 ]]; then - do_unit_version "1.2-bbr" + if [[ ${THREAD_VERSION} != "1.1" && ${INTER_OP_BBR} == 1 ]]; then + do_unit_version "1.3-bbr" fi } @@ -220,8 +220,8 @@ do_cert() ;; esac - if [[ ${THREAD_VERSION} == "1.2" ]]; then - export top_builddir_1_2_bbr="${OT_BUILDDIR}/openthread-simulation-1.2-bbr" + if [[ ${THREAD_VERSION} != "1.1" ]]; then + export top_builddir_1_3_bbr="${OT_BUILDDIR}/openthread-simulation-1.3-bbr" if [[ ${INTER_OP} == "1" ]]; then export top_builddir_1_1="${OT_BUILDDIR}/openthread-simulation-1.1" fi @@ -238,8 +238,8 @@ do_cert_suite() { export top_builddir="${OT_BUILDDIR}/openthread-simulation-${THREAD_VERSION}" - if [[ ${THREAD_VERSION} == "1.2" ]]; then - export top_builddir_1_2_bbr="${OT_BUILDDIR}/openthread-simulation-1.2-bbr" + if [[ ${THREAD_VERSION} != "1.1" ]]; then + export top_builddir_1_3_bbr="${OT_BUILDDIR}/openthread-simulation-1.3-bbr" if [[ ${INTER_OP} == "1" ]]; then export top_builddir_1_1="${OT_BUILDDIR}/openthread-simulation-1.1" fi @@ -386,7 +386,7 @@ do_expect() test_patterns=(-name 'tun-*.exp') else test_patterns=(-name 'posix-*.exp' -o -name 'cli-*.exp') - if [[ ${THREAD_VERSION} == "1.2" ]]; then + if [[ ${THREAD_VERSION} != "1.1" ]]; then test_patterns+=(-o -name 'v1_2-*.exp') fi fi @@ -421,9 +421,9 @@ ENVIRONMENTS: VERBOSE 1 to build or test verbosely. The default is 0. VIRTUAL_TIME 1 for virtual time, otherwise real time. The default value is 0 when running expect tests, otherwise default value is 1. - THREAD_VERSION 1.1 for Thread 1.1 stack, 1.2 for Thread 1.2 stack. The default is 1.2. - INTER_OP 1 to build 1.1 together. Only works when THREAD_VERSION is 1.2. The default is 0. - INTER_OP_BBR 1 to build bbr version together. Only works when THREAD_VERSION is 1.2. The default is 1. + THREAD_VERSION 1.1 for Thread 1.1 stack, 1.3 for Thread 1.3 stack. The default is 1.3. + INTER_OP 1 to build 1.1 together. Only works when THREAD_VERSION is 1.3. The default is 0. + INTER_OP_BBR 1 to build bbr version together. Only works when THREAD_VERSION is 1.3. The default is 1. COMMANDS: clean Clean built files to prepare for new build. @@ -455,7 +455,7 @@ EXAMPLES: THREAD_VERSION=1.1 VIRTUAL_TIME=0 $0 clean build cert tests/scripts/thread-cert/Cert_5_1_01_RouterAttach.py THREAD_VERSION=1.1 VIRTUAL_TIME=0 $0 cert tests/scripts/thread-cert/Cert_5_1_02_ChildAddressTimeout.py - # Test Thread 1.2 with real time, use 'INTER_OP=1' when the case needs both versions. + # Test Thread 1.3 with real time, use 'INTER_OP=1' when the case needs both versions. VIRTUAL_TIME=0 $0 clean build cert tests/scripts/thread-cert/v1_2_test_enhanced_keep_alive.py INTER_OP=1 VIRTUAL_TIME=0 $0 clean build cert tests/scripts/thread-cert/v1_2_router_5_1_1.py INTER_OP=1 VIRTUAL_TIME=0 $0 clean build cert_suite tests/scripts/thread-cert/v1_2_* @@ -559,10 +559,10 @@ envsetup() export RADIO_DEVICE="${OT_BUILDDIR}/openthread-simulation-${THREAD_VERSION}/examples/apps/ncp/ot-rcp" export OT_CLI_PATH="${OT_BUILDDIR}/openthread-posix-${THREAD_VERSION}/src/posix/ot-cli" - if [[ ${THREAD_VERSION} == "1.2" ]]; then + if [[ ${THREAD_VERSION} != "1.1" ]]; then export RADIO_DEVICE_1_1="${OT_BUILDDIR}/openthread-simulation-1.1/examples/apps/ncp/ot-rcp" export OT_CLI_PATH_1_1="${OT_BUILDDIR}/openthread-posix-1.1/src/posix/ot-cli" - export OT_CLI_PATH_1_2_BBR="${OT_BUILDDIR}/openthread-posix-1.2-bbr/src/posix/ot-cli" + export OT_CLI_PATH_BBR="${OT_BUILDDIR}/openthread-posix-1.3-bbr/src/posix/ot-cli" fi fi @@ -606,7 +606,7 @@ main() fi [[ ${VIRTUAL_TIME} == 1 ]] && echo "Using virtual time" || echo "Using real time" - [[ ${THREAD_VERSION} == "1.2" ]] && echo "Using Thread 1.2 stack" || echo "Using Thread 1.1 stack" + [[ ${THREAD_VERSION} != "1.1" ]] && echo "Using Thread 1.3 stack" || echo "Using Thread 1.1 stack" while [[ $# != 0 ]]; do case "$1" in diff --git a/src/core/BUILD.gn b/src/core/BUILD.gn index 8b2b2c05e..5de07c157 100644 --- a/src/core/BUILD.gn +++ b/src/core/BUILD.gn @@ -37,6 +37,8 @@ if (openthread_enable_core_config_args) { defines += [ "OPENTHREAD_CONFIG_THREAD_VERSION=OT_THREAD_VERSION_1_1" ] } else if (openthread_config_thread_version == "1.2") { defines += [ "OPENTHREAD_CONFIG_THREAD_VERSION=OT_THREAD_VERSION_1_2" ] + } else if (openthread_config_thread_version == "1.3") { + defines += [ "OPENTHREAD_CONFIG_THREAD_VERSION=OT_THREAD_VERSION_1_3" ] } else if (openthread_config_thread_version != "") { assert(false, "Unrecognized Thread version: ${openthread_config_thread_version}") diff --git a/tests/scripts/thread-cert/config.py b/tests/scripts/thread-cert/config.py index 7f10f27a6..a7f0e3b14 100755 --- a/tests/scripts/thread-cert/config.py +++ b/tests/scripts/thread-cert/config.py @@ -146,6 +146,7 @@ LEADER_NOTIFY_SED_BY_CHILD_UPDATE_REQUEST = True THREAD_VERSION_1_1 = 2 THREAD_VERSION_1_2 = 3 +THREAD_VERSION_1_3 = 4 PACKET_VERIFICATION_NONE = 0 PACKET_VERIFICATION_DEFAULT = 1 diff --git a/tests/scripts/thread-cert/node.py b/tests/scripts/thread-cert/node.py index ccc400ed2..d8b254c5f 100755 --- a/tests/scripts/thread-cert/node.py +++ b/tests/scripts/thread-cert/node.py @@ -441,7 +441,7 @@ class OtCli: cmd = './ot-cli-%s' % (mode) # For Thread 1.2 MTD node, use ot-cli-mtd build regardless of OT_CLI_PATH - if self.version == '1.2' and mode == 'mtd' and 'top_builddir' in os.environ: + if self.version != '1.1' and mode == 'mtd' and 'top_builddir' in os.environ: srcdir = os.environ['top_builddir'] cmd = '%s/examples/apps/cli/ot-cli-%s %d' % (srcdir, mode, nodeid) @@ -449,11 +449,11 @@ class OtCli: elif self.version == self.env_version: # Load Thread 1.2 BBR device when testing Thread 1.2 scenarios # which requires device with Backbone functionality. - if self.version == '1.2' and self.is_bbr: - if 'OT_CLI_PATH_1_2_BBR' in os.environ: - cmd = os.environ['OT_CLI_PATH_1_2_BBR'] - elif 'top_builddir_1_2_bbr' in os.environ: - srcdir = os.environ['top_builddir_1_2_bbr'] + if self.version != '1.1' and self.is_bbr: + if 'OT_CLI_PATH_BBR' in os.environ: + cmd = os.environ['OT_CLI_PATH_BBR'] + elif 'top_builddir_1_3_bbr' in os.environ: + srcdir = os.environ['top_builddir_1_3_bbr'] cmd = '%s/examples/apps/cli/ot-cli-%s' % (srcdir, mode) # Load Thread device of the testing environment version (may be 1.1 or 1.2) @@ -518,14 +518,14 @@ class OtCli: # Load Thread 1.2 BBR device when testing Thread 1.2 scenarios # which requires device with Backbone functionality. - if self.version == '1.2' and self.is_bbr: - if 'OT_NCP_PATH_1_2_BBR' in os.environ: + if self.version != '1.1' and self.is_bbr: + if 'OT_NCP_PATH_1_3_BBR' in os.environ: cmd = 'spinel-cli.py -p "%s%s" -n' % ( - os.environ['OT_NCP_PATH_1_2_BBR'], + os.environ['OT_NCP_PATH_1_3_BBR'], args, ) - elif 'top_builddir_1_2_bbr' in os.environ: - srcdir = os.environ['top_builddir_1_2_bbr'] + elif 'top_builddir_1_3_bbr' in os.environ: + srcdir = os.environ['top_builddir_1_3_bbr'] cmd = '%s/examples/apps/ncp/ot-ncp-%s' % (srcdir, mode) cmd = 'spinel-cli.py -p "%s%s" -n' % ( cmd, diff --git a/tests/scripts/thread-cert/pktverify/consts.py b/tests/scripts/thread-cert/pktverify/consts.py index 888e9e7e5..9afde5aa8 100644 --- a/tests/scripts/thread-cert/pktverify/consts.py +++ b/tests/scripts/thread-cert/pktverify/consts.py @@ -387,7 +387,9 @@ CSL_DEFAULT_TIMEOUT = 30 CSL_DEFAULT_CHANNEL = 12 # Thread Version TLV value +THREAD_VERSION_1_1 = 2 THREAD_VERSION_1_2 = 3 +THREAD_VERSION_1_3 = 4 # ICMPv6 Types ICMPV6_TYPE_DESTINATION_UNREACHABLE = 1 diff --git a/tests/scripts/thread-cert/pktverify/packet_verifier.py b/tests/scripts/thread-cert/pktverify/packet_verifier.py index c78ec7e0b..6873cf90c 100644 --- a/tests/scripts/thread-cert/pktverify/packet_verifier.py +++ b/tests/scripts/thread-cert/pktverify/packet_verifier.py @@ -176,7 +176,7 @@ class PacketVerifier(object): for i, topo in self.test_info.topology.items(): name = self.test_info.get_node_name(i) if topo['version']: - self._vars[name + '_VERSION'] = {'1.1': 2, '1.2': 3}[topo['version']] + self._vars[name + '_VERSION'] = {'1.1': 2, '1.2': 3, '1.3': 4}[topo['version']] def verify_attached(self, child: str, parent: str = None, child_type: str = 'FTD', pkts=None) -> VerifyResult: """ diff --git a/tests/scripts/thread-cert/run_cert_suite.py b/tests/scripts/thread-cert/run_cert_suite.py index 512ab815d..6af09d864 100755 --- a/tests/scripts/thread-cert/run_cert_suite.py +++ b/tests/scripts/thread-cert/run_cert_suite.py @@ -94,8 +94,8 @@ def cleanup_backbone_env(): def setup_backbone_env(): - if THREAD_VERSION != '1.2': - raise RuntimeError('Backbone tests only work with THREAD_VERSION=1.2') + if THREAD_VERSION == '1.1': + raise RuntimeError('Backbone tests do not work with THREAD_VERSION=1.1') if VIRTUAL_TIME: raise RuntimeError('Backbone tests only work with VIRTUAL_TIME=0') diff --git a/tests/scripts/thread-cert/thread_cert.py b/tests/scripts/thread-cert/thread_cert.py index fdbdacec4..369ed5075 100755 --- a/tests/scripts/thread-cert/thread_cert.py +++ b/tests/scripts/thread-cert/thread_cert.py @@ -482,15 +482,20 @@ class TestCase(NcpSupportMixin, unittest.TestCase): params = params or {} if params.get('is_bbr') or params.get('is_otbr'): - # BBRs must use thread version 1.2 - assert params.get('version', '1.2') == '1.2', params - params['version'] = '1.2' + # BBRs must not use thread version 1.1 + version = params.get('version', '1.3') + assert version != '1.1', params + params['version'] = version params.setdefault('bbr_registration_jitter', config.DEFAULT_BBR_REGISTRATION_JITTER) elif params.get('is_host'): # Hosts must not specify thread version assert params.get('version', '') == '', params params['version'] = '' + # use 1.3 node for 1.2 tests + if params.get('version') == '1.2': + params['version'] = '1.3' + is_ftd = (not params.get('is_mtd') and not params.get('is_host')) effective_params = DEFAULT_PARAMS.copy() diff --git a/tests/scripts/thread-cert/v1_2_LowPower_6_1_07_PreferringARouterOverAReed.py b/tests/scripts/thread-cert/v1_2_LowPower_6_1_07_PreferringARouterOverAReed.py index ca468e425..c5114f720 100755 --- a/tests/scripts/thread-cert/v1_2_LowPower_6_1_07_PreferringARouterOverAReed.py +++ b/tests/scripts/thread-cert/v1_2_LowPower_6_1_07_PreferringARouterOverAReed.py @@ -118,7 +118,7 @@ class LowPower_6_1_07_PreferringARouterOverAReed_Base(thread_cert.TestCase): .filter_mle_cmd(consts.MLE_PARENT_REQUEST) \ .filter_mle_has_tlv(TlvType.CHALLENGE, TlvType.MODE) \ .filter(lambda p: p.mle.tlv.scan_mask.r == 1 and p.mle.tlv.scan_mask.e == 0) \ - .filter(lambda p: p.mle.tlv.version == 3) \ + .filter(lambda p: p.mle.tlv.version >= config.THREAD_VERSION_1_2) \ .filter(lambda p: p.ipv6.hlim == 255) \ .filter_LLARMA() \ .must_next() @@ -127,7 +127,7 @@ class LowPower_6_1_07_PreferringARouterOverAReed_Base(thread_cert.TestCase): pkts.filter_wpan_src64(ROUTER_1) \ .filter_wpan_dst64(DUT) \ .filter_mle_cmd(consts.MLE_PARENT_RESPONSE) \ - .filter(lambda p: p.mle.tlv.version == 2) \ + .filter(lambda p: p.mle.tlv.version == config.THREAD_VERSION_1_1) \ .must_next() # Step 5 - DUT sends another multicast MLE Parent Request to the all-routers multicast with the Scan Mask TLV @@ -136,7 +136,7 @@ class LowPower_6_1_07_PreferringARouterOverAReed_Base(thread_cert.TestCase): .filter_mle_cmd(consts.MLE_PARENT_REQUEST) \ .filter_mle_has_tlv(TlvType.CHALLENGE, TlvType.MODE) \ .filter(lambda p: p.mle.tlv.scan_mask.r == 1 and p.mle.tlv.scan_mask.e == 1) \ - .filter(lambda p: p.mle.tlv.version == consts.THREAD_VERSION_1_2) \ + .filter(lambda p: p.mle.tlv.version >= consts.THREAD_VERSION_1_2) \ .filter(lambda p: p.ipv6.hlim == 255) \ .filter_LLARMA() \ .must_next() @@ -152,7 +152,7 @@ class LowPower_6_1_07_PreferringARouterOverAReed_Base(thread_cert.TestCase): .filter_mle_cmd(consts.MLE_CHILD_ID_REQUEST) \ .filter_wpan_dst64(ROUTER_1) \ .filter_mle_has_tlv(TlvType.ADDRESS_REGISTRATION, TlvType.LINK_LAYER_FRAME_COUNTER, TlvType.MODE, TlvType.RESPONSE, TlvType.TIMEOUT, TlvType.TLV_REQUEST) \ - .filter(lambda p: p.mle.tlv.version == consts.THREAD_VERSION_1_2) \ + .filter(lambda p: p.mle.tlv.version >= consts.THREAD_VERSION_1_2) \ .must_next() diff --git a/tests/scripts/thread-cert/v1_2_router_5_1_1.py b/tests/scripts/thread-cert/v1_2_router_5_1_1.py index 39e0c357e..8af0ae5d5 100755 --- a/tests/scripts/thread-cert/v1_2_router_5_1_1.py +++ b/tests/scripts/thread-cert/v1_2_router_5_1_1.py @@ -79,7 +79,7 @@ class Router_5_1_01(thread_cert.TestCase): msg.assertMleMessageContainsTlv(mle.Challenge) msg.assertMleMessageContainsTlv(mle.ScanMask) msg.assertMleMessageContainsTlv(mle.Version) - assert msg.get_mle_message_tlv(mle.Version).version == 3 + assert msg.get_mle_message_tlv(mle.Version).version >= config.THREAD_VERSION_1_2 scan_mask_tlv = msg.get_mle_message_tlv(mle.ScanMask) self.assertEqual(1, scan_mask_tlv.router) @@ -97,7 +97,7 @@ class Router_5_1_01(thread_cert.TestCase): msg.assertMleMessageContainsTlv(mle.LinkMargin) msg.assertMleMessageContainsTlv(mle.Connectivity) msg.assertMleMessageContainsTlv(mle.Version) - assert msg.get_mle_message_tlv(mle.Version).version == 3 + assert msg.get_mle_message_tlv(mle.Version).version >= config.THREAD_VERSION_1_2 # 4 - Router_1 receives the MLE Parent Response and sends a Child ID Request msg = router_messages.next_mle_message(mle.CommandType.CHILD_ID_REQUEST) @@ -110,7 +110,7 @@ class Router_5_1_01(thread_cert.TestCase): msg.assertMleMessageContainsTlv(mle.Version) msg.assertMleMessageContainsTlv(mle.TlvRequest) msg.assertMleMessageDoesNotContainTlv(mle.AddressRegistration) - assert msg.get_mle_message_tlv(mle.Version).version == 3 + assert msg.get_mle_message_tlv(mle.Version).version >= config.THREAD_VERSION_1_2 # 5 - Leader responds with a Child ID Response msg = leader_messages.next_mle_message(mle.CommandType.CHILD_ID_RESPONSE) diff --git a/tests/scripts/thread-cert/v1_2_test_enhanced_keep_alive.py b/tests/scripts/thread-cert/v1_2_test_enhanced_keep_alive.py index 04d55d252..a104a51ac 100755 --- a/tests/scripts/thread-cert/v1_2_test_enhanced_keep_alive.py +++ b/tests/scripts/thread-cert/v1_2_test_enhanced_keep_alive.py @@ -88,7 +88,7 @@ class SED_EnhancedKeepAlive(thread_cert.TestCase): msg.assertMleMessageContainsTlv(mle.Challenge) msg.assertMleMessageContainsTlv(mle.ScanMask) msg.assertMleMessageContainsTlv(mle.Version) - self.assertEqual(msg.get_mle_message_tlv(mle.Version).version, 3) + self.assertGreaterEqual(msg.get_mle_message_tlv(mle.Version).version, config.THREAD_VERSION_1_2) scan_mask_tlv = msg.get_mle_message_tlv(mle.ScanMask) self.assertEqual(1, scan_mask_tlv.router) @@ -106,7 +106,7 @@ class SED_EnhancedKeepAlive(thread_cert.TestCase): msg.assertMleMessageContainsTlv(mle.LinkMargin) msg.assertMleMessageContainsTlv(mle.Connectivity) msg.assertMleMessageContainsTlv(mle.Version) - self.assertEqual(msg.get_mle_message_tlv(mle.Version).version, 3) + self.assertGreaterEqual(msg.get_mle_message_tlv(mle.Version).version, config.THREAD_VERSION_1_2) # 4 - SED_1 receives the MLE Parent Response and sends a Child ID Request msg = sed_messages.next_mle_message(mle.CommandType.CHILD_ID_REQUEST) @@ -118,7 +118,7 @@ class SED_EnhancedKeepAlive(thread_cert.TestCase): msg.assertMleMessageContainsTlv(mle.Timeout) msg.assertMleMessageContainsTlv(mle.Version) msg.assertMleMessageContainsTlv(mle.TlvRequest) - self.assertEqual(msg.get_mle_message_tlv(mle.Version).version, 3) + self.assertGreaterEqual(msg.get_mle_message_tlv(mle.Version).version, config.THREAD_VERSION_1_2) # 5 - Leader responds with a Child ID Response msg = leader_messages.next_mle_message(mle.CommandType.CHILD_ID_RESPONSE) From b2132a903162be76eb4ea8a107e82c4e0737525f Mon Sep 17 00:00:00 2001 From: Jonathan Hui Date: Wed, 8 Jun 2022 15:22:36 -0700 Subject: [PATCH 61/80] [tests] add `1.3.0` as valid version in `test_publish_meshcop_service.py` (#7801) --- .../thread-cert/border_router/test_publish_meshcop_service.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/scripts/thread-cert/border_router/test_publish_meshcop_service.py b/tests/scripts/thread-cert/border_router/test_publish_meshcop_service.py index 93f049736..25e5cd4f3 100755 --- a/tests/scripts/thread-cert/border_router/test_publish_meshcop_service.py +++ b/tests/scripts/thread-cert/border_router/test_publish_meshcop_service.py @@ -161,7 +161,7 @@ class PublishMeshCopService(thread_cert.TestCase): self.assertEqual((state_bitmap >> 8 & 1), br.get_backbone_router_state() == 'Primary') # BBR is primary or not self.assertEqual(service_data['txt']['nn'], br.get_network_name()) self.assertEqual(service_data['txt']['rv'], '1') - self.assertIn(service_data['txt']['tv'], ['1.1.0', '1.1.1', '1.2.0']) + self.assertIn(service_data['txt']['tv'], ['1.1.0', '1.1.1', '1.2.0', '1.3.0']) def discover_all_meshcop_services(self, host): instance_names = host.browse_mdns_services('_meshcop._udp') From edbb07d95f5c98f0f6092c2e400373b61b727fea Mon Sep 17 00:00:00 2001 From: Simon Lin Date: Tue, 14 Jun 2022 00:54:57 +0800 Subject: [PATCH 62/80] [tests] verify a Router can establish links quickly via multicast Link Request (#7807) This commit adds a test to make sure delay sending multicast Link Request (#7745) works as intended. --- .../test_router_multicast_link_request.py | 140 ++++++++++++++++++ 1 file changed, 140 insertions(+) create mode 100755 tests/scripts/thread-cert/test_router_multicast_link_request.py diff --git a/tests/scripts/thread-cert/test_router_multicast_link_request.py b/tests/scripts/thread-cert/test_router_multicast_link_request.py new file mode 100755 index 000000000..563185f82 --- /dev/null +++ b/tests/scripts/thread-cert/test_router_multicast_link_request.py @@ -0,0 +1,140 @@ +#!/usr/bin/env python3 +# +# Copyright (c) 2022, The OpenThread Authors. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. Neither the name of the copyright holder nor the +# names of its contributors may be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. +# +import logging +import unittest + +import config +import thread_cert +from pktverify.consts import MLE_LINK_REQUEST, MLE_LINK_ACCEPT +from pktverify.packet_verifier import PacketVerifier + +LEADER = 1 +REED = 2 +ROUTER1, ROUTER2, ROUTER3 = 3, 4, 5 + +# Test Purpose and Description: +# ----------------------------- +# This test verifies if a device can quickly and efficiently establish links with +# neighboring routers by sending a multicast Link Request message after becoming +# a router +# +# Test Topology: +# ------------- +# Leader------+ +# | \ \ +# Router1 Router2 Router3 +# \ | | +# +---REED-----+ +# + +# REED should establish links to the three Routers within the delay threshold. +# The max delay consists: +# Leader may delay 1 second for MLE Advertisement +# Router may delay 1 second for MLE Advertisement +# Router may delay 1 second for MLE Link Accept after receiving MLE Link Request (multicast) +LINK_ESTABLISH_DELAY_THRESHOLD = 3 + + +class TestRouterMulticastLinkRequest(thread_cert.TestCase): + USE_MESSAGE_FACTORY = False + + TOPOLOGY = { + LEADER: { + 'name': 'LEADER', + 'mode': 'rdn', + 'allowlist': [ROUTER1, ROUTER2, ROUTER3] + }, + ROUTER1: { + 'name': 'ROUTER1', + 'mode': 'rdn', + 'allowlist': [LEADER, REED] + }, + ROUTER2: { + 'name': 'ROUTER2', + 'mode': 'rdn', + 'allowlist': [LEADER, REED] + }, + ROUTER3: { + 'name': 'ROUTER3', + 'mode': 'rdn', + 'allowlist': [LEADER, REED] + }, + REED: { + 'name': 'REED', + 'mode': 'rdn', + 'allowlist': [ROUTER1, ROUTER2, ROUTER3] + }, + } + + def test(self): + self.nodes[LEADER].start() + self.simulator.go(5) + self.assertEqual(self.nodes[LEADER].get_state(), 'leader') + + for routerid in (ROUTER1, ROUTER2, ROUTER3): + self.nodes[routerid].start() + self.simulator.go(config.ROUTER_STARTUP_DELAY) + self.assertEqual(self.nodes[routerid].get_state(), 'router') + + # Wait for the network to stabilize + self.simulator.go(60) + + self.nodes[REED].start() + self.simulator.go(config.ROUTER_STARTUP_DELAY) + self.assertEqual(self.nodes[REED].get_state(), 'router') + self.simulator.go(LINK_ESTABLISH_DELAY_THRESHOLD + 3) + + def verify(self, pv: PacketVerifier): + pkts = pv.pkts + print(pv.vars) + pv.summary.show() + + REED = pv.vars['REED'] + as_pkt = pkts.filter_wpan_src64(REED).filter_coap_request('/a/as', confirmable=True).must_next() + parent_rloc16 = as_pkt.wpan.dst16 + as_ack_pkt = pkts.filter_wpan_src16(parent_rloc16).filter_coap_ack('/a/as').must_next() + become_router_timestamp = as_ack_pkt.sniff_timestamp + + # REED has just received `/a/as` and become a Router + # REED should send Multicast Link Request after becoming Router + link_request_pkt = pkts.filter_wpan_src64(REED).filter_mle_cmd(MLE_LINK_REQUEST).must_next() + link_request_pkt.must_verify('ipv6.dst == "ff02::2"') + + # REED should send Link Accept to the three Routers + for router in ('ROUTER1', 'ROUTER2', 'ROUTER3'): + with pkts.save_index(): + pkt = pkts.filter_wpan_src64(REED).filter_wpan_dst64( + pv.vars[router]).filter_mle_cmd(MLE_LINK_ACCEPT).must_next() + link_establish_delay = pkt.sniff_timestamp - become_router_timestamp + logging.info("Link to %s established in %.3f seconds", router, link_establish_delay) + self.assertLess(link_establish_delay, LINK_ESTABLISH_DELAY_THRESHOLD) + + +if __name__ == '__main__': + unittest.main() From abad2f2cc15b5b13be726c5ae80e682fad71cea8 Mon Sep 17 00:00:00 2001 From: Jonathan Hui Date: Mon, 13 Jun 2022 10:35:18 -0700 Subject: [PATCH 63/80] [docs] remove `linkquality` from `src/cli/README.md` (#7810) --- src/cli/README.md | 20 -------------------- 1 file changed, 20 deletions(-) diff --git a/src/cli/README.md b/src/cli/README.md index 1dd92e45a..cbb8ad06f 100644 --- a/src/cli/README.md +++ b/src/cli/README.md @@ -62,7 +62,6 @@ Done - [leaderdata](#leaderdata) - [leaderweight](#leaderweight) - [linkmetrics](#linkmetrics-mgmt-ipaddr-enhanced-ack-clear) -- [linkquality](#linkquality-extaddr) - [locate](#locate) - [log](#log-filename-filename) - [mac](#mac-retries-direct) @@ -1627,25 +1626,6 @@ Done - RSSI: -18 (dBm) (Exponential Moving Average) ``` -### linkquality \ - -Get the link quality on the link to a given extended address. - -```bash -> linkquality 36c1dd7a4f5201ff -3 -Done -``` - -### linkquality \ \ - -Set the link quality on the link to a given extended address. - -```bash -> linkquality 36c1dd7a4f5201ff 3 -Done -``` - ### locate Gets the current state (`In Progress` or `Idle`) of anycast locator. From d0a71471654129b2b0dfc780f52c83a3435c89b2 Mon Sep 17 00:00:00 2001 From: JaneFromSilabs <58004715+JaneFromSilabs@users.noreply.github.com> Date: Mon, 13 Jun 2022 13:36:54 -0400 Subject: [PATCH 64/80] [thci] add support for Thread Version 4 (1.3) (#7806) --- tools/harness-thci/OpenThread.py | 34 ++++++++++++++++++-------------- 1 file changed, 19 insertions(+), 15 deletions(-) diff --git a/tools/harness-thci/OpenThread.py b/tools/harness-thci/OpenThread.py index 7441f3312..d536c9c31 100644 --- a/tools/harness-thci/OpenThread.py +++ b/tools/harness-thci/OpenThread.py @@ -75,7 +75,8 @@ OT13_VERSION = 'OPENTHREAD' OT11_CAPBS = DevCapb.V1_1 OT12_CAPBS = (DevCapb.L_AIO | DevCapb.C_FFD | DevCapb.C_RFD) OT12BR_CAPBS = (DevCapb.C_BBR | DevCapb.C_Host | DevCapb.C_Comm) -OT13_CAPBS = DevCapb.NotSpecified +OT13_CAPBS = (DevCapb.C_FTD13 | DevCapb.C_MTD13) +OT13BR_CAPBS = (DevCapb.C_BR13 | DevCapb.C_Host13) ZEPHYR_PREFIX = 'ot ' """CLI prefix used for OpenThread commands in Zephyr systems""" @@ -3123,23 +3124,26 @@ class OpenThreadTHCI(object): def __discoverDeviceCapability(self): """Discover device capability according to version""" - if self.IsBorderRouter: + thver = self.__executeCommand('thread version')[0] + if thver in ['1.3', '4'] and not self.IsBorderRouter: + self.log("Setting capability of {}: (DevCapb.C_FTD13 | DevCapb.C_MTD13)".format(self)) + self.DeviceCapability = OT13_CAPBS + elif thver in ['1.3', '4'] and self.IsBorderRouter: + self.log("Setting capability of {}: (DevCapb.C_BR13 | DevCapb.C_Host13)".format(self)) + self.DeviceCapability = OT13BR_CAPBS + elif thver in ['1.2', '3'] and not self.IsBorderRouter: + self.log("Setting capability of {}: DevCapb.L_AIO | DevCapb.C_FFD | DevCapb.C_RFD".format(self)) + self.DeviceCapability = OT12_CAPBS + elif thver in ['1.2', '3'] and self.IsBorderRouter: self.log("Setting capability of BR {}: DevCapb.C_BBR | DevCapb.C_Host | DevCapb.C_Comm".format(self)) self.DeviceCapability = OT12BR_CAPBS + elif thver in ['1.1', '2']: + self.log("Setting capability of {}: DevCapb.V1_1".format(self)) + self.DeviceCapability = OT11_CAPBS else: - # Get Thread stack version to distinguish device capability. - thver = self.__executeCommand('thread version')[0] - - if thver in ['1.2', '3']: - self.log("Setting capability of {}: DevCapb.L_AIO | DevCapb.C_FFD | DevCapb.C_RFD".format(self)) - self.DeviceCapability = OT12_CAPBS - elif thver in ['1.1', '2']: - self.log("Setting capability of {}: DevCapb.V1_1".format(self)) - self.DeviceCapability = OT11_CAPBS - else: - self.log("Capability not specified for {}".format(self)) - self.DeviceCapability = DevCapb.NotSpecified - assert False, thver + self.log("Capability not specified for {}".format(self)) + self.DeviceCapability = DevCapb.NotSpecified + assert False, thver @staticmethod def __lstrip0x(s): From 19f8033b78c050586ccdbb337e7fb0adeef647cf Mon Sep 17 00:00:00 2001 From: Abtin Keshavarzian Date: Mon, 13 Jun 2022 10:56:42 -0700 Subject: [PATCH 65/80] [srp-server] validate sub-types and treat as atomic (#7795) This commit updates `Srp::Server` to treat the update instructions in a received SRP message for a service and all its sub-types as atomic. It adds a new method `ValidateServiceSubTypes()` which performs two checks. First, it verifies that there is a matching base service for all sub-type services in a received SRP Update message. Second, it treats the sub-types in the message as atomic, i.e., whatever information appears in the message is considered the entirety of information about the service and its sub-types. In particular, any previously registered service sub-type that does not appear in a new SRP Update is removed (marked as deleted). This commit updates `test_srp_sub_type.py` test-case to validate the behavior of newly added mechanism (i.e. removal of older sub-types when not present in the new SRP update message). --- src/core/net/srp_server.cpp | 104 ++++++++++++++++++++++++++++++++---- src/core/net/srp_server.hpp | 4 ++ 2 files changed, 99 insertions(+), 9 deletions(-) diff --git a/src/core/net/srp_server.cpp b/src/core/net/srp_server.cpp index dd18a4070..cdcee311f 100644 --- a/src/core/net/srp_server.cpp +++ b/src/core/net/srp_server.cpp @@ -718,6 +718,8 @@ void Server::ProcessDnsUpdate(Message &aMessage, MessageMetadata &aMetadata) // Parse lease time and validate signature. SuccessOrExit(error = ProcessAdditionalSection(host, aMessage, aMetadata)); + SuccessOrExit(error = ValidateServiceSubTypes(*host, aMetadata)); + HandleUpdate(*host, aMetadata); exit: @@ -1199,6 +1201,68 @@ exit: return error; } +Error Server::ValidateServiceSubTypes(Host &aHost, const MessageMetadata &aMetadata) +{ + Error error = kErrorNone; + Host *existingHost; + + // Verify that there is a matching base type service for all + // sub-type services in `aHost` (which is from the received + // and parsed SRP Update message). + + for (const Service &service : aHost.GetServices()) + { + if (service.IsSubType() && (aHost.FindBaseService(service.GetInstanceName()) == nullptr)) + { +#if OT_SHOULD_LOG_AT(OT_LOG_LEVEL_WARN) + char subLabel[Dns::Name::kMaxLabelSize]; + + IgnoreError(service.GetServiceSubTypeLabel(subLabel, sizeof(subLabel))); + LogWarn("Message contains instance %s with subtype %s without base type", service.GetInstanceName(), + subLabel); +#endif + + ExitNow(error = kErrorParse); + } + } + + // SRP server must treat the update instructions for a service type + // and all its sub-types as atomic, i.e., when a service and its + // sub-types are being updated, whatever information appears in the + // SRP Update is the entirety of information about that service and + // its sub-types. Any previously registered sub-type that does not + // appear in a new SRP Update, must be removed. + // + // We go though the list of registered services for the same host + // and if the base service is included in the new SRP Update + // message, we add any previously registered service sub-type that + // does not appear in new Update message as "deleted". + + existingHost = mHosts.FindMatching(aHost.GetFullName()); + VerifyOrExit(existingHost != nullptr); + + for (const Service &baseService : existingHost->GetServices()) + { + if (baseService.IsSubType() || (aHost.FindBaseService(baseService.GetInstanceName()) == nullptr)) + { + continue; + } + + for (const Service &subService : existingHost->GetServices()) + { + if (!subService.IsSubType() || !subService.MatchesInstanceName(baseService.GetInstanceName())) + { + continue; + } + + SuccessOrExit(error = aHost.AddCopyOfServiceAsDeletedIfNotPresent(subService, aMetadata.mRxTime)); + } + } + +exit: + return error; +} + void Server::HandleUpdate(Host &aHost, const MessageMetadata &aMetadata) { Error error = kErrorNone; @@ -1224,15 +1288,7 @@ void Server::HandleUpdate(Host &aHost, const MessageMetadata &aMetadata) continue; } - if (aHost.FindService(service.GetServiceName(), service.GetInstanceName()) == nullptr) - { - Service *newService = aHost.AddNewService(service.GetServiceName(), service.GetInstanceName(), - service.IsSubType(), aMetadata.mRxTime); - - VerifyOrExit(newService != nullptr, error = kErrorNoBufs); - newService->mDescription->mUpdateTime = aMetadata.mRxTime; - newService->mIsDeleted = true; - } + SuccessOrExit(error = aHost.AddCopyOfServiceAsDeletedIfNotPresent(service, aMetadata.mRxTime)); } exit: @@ -1959,6 +2015,25 @@ exit: return; } +Error Server::Host::AddCopyOfServiceAsDeletedIfNotPresent(const Service &aService, TimeMilli aUpdateTime) +{ + Error error = kErrorNone; + Service *newService; + + VerifyOrExit(FindService(aService.GetServiceName(), aService.GetInstanceName()) == nullptr); + + newService = + AddNewService(aService.GetServiceName(), aService.GetInstanceName(), aService.IsSubType(), aUpdateTime); + + VerifyOrExit(newService != nullptr, error = kErrorNoBufs); + + newService->mDescription->mUpdateTime = aUpdateTime; + newService->mIsDeleted = true; + +exit: + return error; +} + void Server::Host::FreeAllServices(void) { while (!mServices.IsEmpty()) @@ -2064,6 +2139,17 @@ Server::Service *Server::Host::FindService(const char *aServiceName, const char return AsNonConst(AsConst(this)->FindService(aServiceName, aInstanceName)); } +const Server::Service *Server::Host::FindBaseService(const char *aInstanceName) const +{ + return FindNextService(/*a PrevService */ nullptr, kFlagsBaseTypeServiceOnly, /* aServiceName */ nullptr, + aInstanceName); +} + +Server::Service *Server::Host::FindBaseService(const char *aInstanceName) +{ + return AsNonConst(AsConst(this)->FindBaseService(aInstanceName)); +} + Error Server::Host::AddIp6Address(const Ip6::Address &aIp6Address) { Error error = kErrorNone; diff --git a/src/core/net/srp_server.hpp b/src/core/net/srp_server.hpp index 6fcc1d818..b2c371688 100644 --- a/src/core/net/srp_server.hpp +++ b/src/core/net/srp_server.hpp @@ -585,6 +585,7 @@ public: bool aIsSubType, TimeMilli aUpdateTime); void RemoveService(Service *aService, RetainName aRetainName, NotifyMode aNotifyServiceHandler); + Error AddCopyOfServiceAsDeletedIfNotPresent(const Service &aService, TimeMilli aUpdateTime); void FreeAllServices(void); void ClearResources(void); Error MergeServicesAndResourcesFrom(Host &aHost); @@ -594,6 +595,8 @@ public: const RetainPtr FindServiceDescription(const char *aInstanceName) const; Service * FindService(const char *aServiceName, const char *aInstanceName); const Service * FindService(const char *aServiceName, const char *aInstanceName) const; + Service * FindBaseService(const char *aInstanceName); + const Service * FindBaseService(const char *aInstanceName) const; Host * mNext; Heap::String mFullName; @@ -977,6 +980,7 @@ private: uint16_t aSigRdataOffset, uint16_t aSigRdataLength, const char * aSignerName) const; + Error ValidateServiceSubTypes(Host &aHost, const MessageMetadata &aMetadata); Error ProcessZoneSection(const Message &aMessage, MessageMetadata &aMetadata) const; Error ProcessHostDescriptionInstruction(Host & aHost, const Message & aMessage, From ac62f7819c321e034b0f9bedadc36f855ae2d47c Mon Sep 17 00:00:00 2001 From: Abtin Keshavarzian Date: Tue, 14 Jun 2022 12:26:18 -0700 Subject: [PATCH 66/80] [border-router] use `Ip6::Nd` namespace for IPv6 Neighbor Discovery (#7780) This commit uses `Ip6::Nd` namespace for IPv6 Neighbor Discovery definitions (used for border router). It also renames and moves the source files to `net/nd6.*`. --- Android.mk | 2 +- src/core/BUILD.gn | 4 +- src/core/CMakeLists.txt | 2 +- src/core/Makefile.am | 4 +- src/core/border_router/routing_manager.cpp | 80 +++++++++---------- src/core/border_router/routing_manager.hpp | 20 ++--- .../router_advertisement.cpp => net/nd6.cpp} | 32 ++++---- .../router_advertisement.hpp => net/nd6.hpp} | 48 +++++------ 8 files changed, 89 insertions(+), 103 deletions(-) rename src/core/{border_router/router_advertisement.cpp => net/nd6.cpp} (87%) rename src/core/{border_router/router_advertisement.hpp => net/nd6.hpp} (93%) diff --git a/Android.mk b/Android.mk index a13194cd2..c1094e6d1 100644 --- a/Android.mk +++ b/Android.mk @@ -221,7 +221,6 @@ LOCAL_SRC_FILES := \ src/core/backbone_router/multicast_listeners_table.cpp \ src/core/backbone_router/ndproxy_table.cpp \ src/core/border_router/infra_if.cpp \ - src/core/border_router/router_advertisement.cpp \ src/core/border_router/routing_manager.cpp \ src/core/coap/coap.cpp \ src/core/coap/coap_message.cpp \ @@ -303,6 +302,7 @@ LOCAL_SRC_FILES := \ src/core/net/ip6_filter.cpp \ src/core/net/ip6_headers.cpp \ src/core/net/ip6_mpl.cpp \ + src/core/net/nd6.cpp \ src/core/net/nd_agent.cpp \ src/core/net/netif.cpp \ src/core/net/sntp_client.cpp \ diff --git a/src/core/BUILD.gn b/src/core/BUILD.gn index 5de07c157..0db992dfb 100644 --- a/src/core/BUILD.gn +++ b/src/core/BUILD.gn @@ -367,8 +367,6 @@ openthread_core_files = [ "backbone_router/ndproxy_table.hpp", "border_router/infra_if.cpp", "border_router/infra_if.hpp", - "border_router/router_advertisement.cpp", - "border_router/router_advertisement.hpp", "border_router/routing_manager.cpp", "border_router/routing_manager.hpp", "coap/coap.cpp", @@ -560,6 +558,8 @@ openthread_core_files = [ "net/ip6_mpl.cpp", "net/ip6_mpl.hpp", "net/ip6_types.hpp", + "net/nd6.cpp", + "net/nd6.hpp", "net/nd_agent.cpp", "net/nd_agent.hpp", "net/netif.cpp", diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index a9cc62329..3907c4374 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -87,7 +87,6 @@ set(COMMON_SOURCES backbone_router/multicast_listeners_table.cpp backbone_router/ndproxy_table.cpp border_router/infra_if.cpp - border_router/router_advertisement.cpp border_router/routing_manager.cpp coap/coap.cpp coap/coap_message.cpp @@ -169,6 +168,7 @@ set(COMMON_SOURCES net/ip6_filter.cpp net/ip6_headers.cpp net/ip6_mpl.cpp + net/nd6.cpp net/nd_agent.cpp net/netif.cpp net/sntp_client.cpp diff --git a/src/core/Makefile.am b/src/core/Makefile.am index a63ab7e77..13fb8c117 100644 --- a/src/core/Makefile.am +++ b/src/core/Makefile.am @@ -177,7 +177,6 @@ SOURCES_COMMON = \ backbone_router/multicast_listeners_table.cpp \ backbone_router/ndproxy_table.cpp \ border_router/infra_if.cpp \ - border_router/router_advertisement.cpp \ border_router/routing_manager.cpp \ coap/coap.cpp \ coap/coap_message.cpp \ @@ -259,6 +258,7 @@ SOURCES_COMMON = \ net/ip6_filter.cpp \ net/ip6_headers.cpp \ net/ip6_mpl.cpp \ + net/nd6.cpp \ net/nd_agent.cpp \ net/netif.cpp \ net/sntp_client.cpp \ @@ -416,7 +416,6 @@ HEADERS_COMMON = \ backbone_router/multicast_listeners_table.hpp \ backbone_router/ndproxy_table.hpp \ border_router/infra_if.hpp \ - border_router/router_advertisement.hpp \ border_router/routing_manager.hpp \ coap/coap.hpp \ coap/coap_message.hpp \ @@ -564,6 +563,7 @@ HEADERS_COMMON = \ net/ip6_headers.hpp \ net/ip6_mpl.hpp \ net/ip6_types.hpp \ + net/nd6.hpp \ net/nd_agent.hpp \ net/netif.hpp \ net/sntp_client.hpp \ diff --git a/src/core/border_router/routing_manager.cpp b/src/core/border_router/routing_manager.cpp index cf053a658..01bcf2bd4 100644 --- a/src/core/border_router/routing_manager.cpp +++ b/src/core/border_router/routing_manager.cpp @@ -816,9 +816,9 @@ bool RoutingManager::IsRouterSolicitationInProgress(void) const Error RoutingManager::SendRouterSolicitation(void) { - Ip6::Address destAddress; - RouterAdv::RouterSolicitMessage routerSolicit; - InfraIf::Icmp6Packet packet; + Ip6::Address destAddress; + Ip6::Nd::RouterSolicitMessage routerSolicit; + InfraIf::Icmp6Packet packet; OT_ASSERT(IsInitialized()); @@ -842,11 +842,11 @@ void RoutingManager::SendRouterAdvertisement(const OmrPrefixArray &aNewOmrPrefix if (mIsAdvertisingLocalOnLinkPrefix) { - RouterAdv::PrefixInfoOption *pio; + Ip6::Nd::PrefixInfoOption *pio; - OT_ASSERT(bufferLength + sizeof(RouterAdv::PrefixInfoOption) <= sizeof(buffer)); + OT_ASSERT(bufferLength + sizeof(Ip6::Nd::PrefixInfoOption) <= sizeof(buffer)); - pio = reinterpret_cast(buffer + bufferLength); + pio = reinterpret_cast(buffer + bufferLength); pio->Init(); pio->SetOnLinkFlag(); @@ -864,11 +864,11 @@ void RoutingManager::SendRouterAdvertisement(const OmrPrefixArray &aNewOmrPrefix } else if (mOnLinkPrefixDeprecateTimer.IsRunning()) { - RouterAdv::PrefixInfoOption *pio; + Ip6::Nd::PrefixInfoOption *pio; - OT_ASSERT(bufferLength + sizeof(RouterAdv::PrefixInfoOption) <= sizeof(buffer)); + OT_ASSERT(bufferLength + sizeof(Ip6::Nd::PrefixInfoOption) <= sizeof(buffer)); - pio = reinterpret_cast(buffer + bufferLength); + pio = reinterpret_cast(buffer + bufferLength); pio->Init(); pio->SetOnLinkFlag(); @@ -891,13 +891,13 @@ void RoutingManager::SendRouterAdvertisement(const OmrPrefixArray &aNewOmrPrefix { if (!aNewOmrPrefixes.ContainsMatching(advertisedOmrPrefix.GetPrefix())) { - RouterAdv::RouteInfoOption *rio; + Ip6::Nd::RouteInfoOption *rio; - OT_ASSERT(bufferLength + RouterAdv::RouteInfoOption::OptionSizeForPrefix( - advertisedOmrPrefix.GetPrefix().GetLength()) <= + OT_ASSERT(bufferLength + + Ip6::Nd::RouteInfoOption::OptionSizeForPrefix(advertisedOmrPrefix.GetPrefix().GetLength()) <= sizeof(buffer)); - rio = reinterpret_cast(buffer + bufferLength); + rio = reinterpret_cast(buffer + bufferLength); // Set zero route lifetime to immediately invalidate the advertised OMR prefix. rio->Init(); @@ -913,13 +913,12 @@ void RoutingManager::SendRouterAdvertisement(const OmrPrefixArray &aNewOmrPrefix for (const OmrPrefix &newOmrPrefix : aNewOmrPrefixes) { - RouterAdv::RouteInfoOption *rio; + Ip6::Nd::RouteInfoOption *rio; - OT_ASSERT(bufferLength + - RouterAdv::RouteInfoOption::OptionSizeForPrefix(newOmrPrefix.GetPrefix().GetLength()) <= + OT_ASSERT(bufferLength + Ip6::Nd::RouteInfoOption::OptionSizeForPrefix(newOmrPrefix.GetPrefix().GetLength()) <= sizeof(buffer)); - rio = reinterpret_cast(buffer + bufferLength); + rio = reinterpret_cast(buffer + bufferLength); rio->Init(); rio->SetRouteLifetime(kDefaultOmrPrefixLifetime); @@ -978,7 +977,7 @@ bool RoutingManager::IsValidOmrPrefix(const Ip6::Prefix &aOmrPrefix) (aOmrPrefix.mLength >= 3 && (aOmrPrefix.GetBytes()[0] & 0xE0) == 0x20); } -bool RoutingManager::IsValidOnLinkPrefix(const RouterAdv::PrefixInfoOption &aPio) +bool RoutingManager::IsValidOnLinkPrefix(const Ip6::Nd::PrefixInfoOption &aPio) { Ip6::Prefix prefix; @@ -1109,35 +1108,30 @@ void RoutingManager::HandleRouterAdvertisement(const InfraIf::Icmp6Packet &aPack OT_ASSERT(mIsRunning); OT_UNUSED_VARIABLE(aSrcAddress); - using RouterAdv::Option; - using RouterAdv::PrefixInfoOption; - using RouterAdv::RouteInfoOption; - using RouterAdv::RouterAdvMessage; + bool needReevaluate = false; + const uint8_t * optionsBegin; + uint16_t optionsLength; + const Ip6::Nd::Option * option; + const Ip6::Nd::RouterAdvMessage *routerAdvMessage; - bool needReevaluate = false; - const uint8_t * optionsBegin; - uint16_t optionsLength; - const Option * option; - const RouterAdvMessage *routerAdvMessage; - - VerifyOrExit(aPacket.GetLength() >= sizeof(RouterAdvMessage)); + VerifyOrExit(aPacket.GetLength() >= sizeof(Ip6::Nd::RouterAdvMessage)); LogInfo("Received Router Advertisement from %s on %s", aSrcAddress.ToString().AsCString(), mInfraIf.ToString().AsCString()); DumpDebg("[BR-CERT] direction=recv | type=RA |", aPacket.GetBytes(), aPacket.GetLength()); - routerAdvMessage = reinterpret_cast(aPacket.GetBytes()); - optionsBegin = aPacket.GetBytes() + sizeof(RouterAdvMessage); - optionsLength = aPacket.GetLength() - sizeof(RouterAdvMessage); + routerAdvMessage = reinterpret_cast(aPacket.GetBytes()); + optionsBegin = aPacket.GetBytes() + sizeof(Ip6::Nd::RouterAdvMessage); + optionsLength = aPacket.GetLength() - sizeof(Ip6::Nd::RouterAdvMessage); option = nullptr; - while ((option = Option::GetNextOption(option, optionsBegin, optionsLength)) != nullptr) + while ((option = Ip6::Nd::Option::GetNextOption(option, optionsBegin, optionsLength)) != nullptr) { switch (option->GetType()) { - case Option::Type::kPrefixInfo: + case Ip6::Nd::Option::Type::kPrefixInfo: { - const PrefixInfoOption *pio = static_cast(option); + const Ip6::Nd::PrefixInfoOption *pio = static_cast(option); if (pio->IsValid()) { @@ -1146,9 +1140,9 @@ void RoutingManager::HandleRouterAdvertisement(const InfraIf::Icmp6Packet &aPack } break; - case Option::Type::kRouteInfo: + case Ip6::Nd::Option::Type::kRouteInfo: { - const RouteInfoOption *rio = static_cast(option); + const Ip6::Nd::RouteInfoOption *rio = static_cast(option); if (rio->IsValid()) { @@ -1181,7 +1175,7 @@ exit: // Adds or deprecates a discovered on-link prefix (new external routes may be added // to the Thread network). Returns a boolean which indicates whether we need to do // routing policy evaluation. -bool RoutingManager::UpdateDiscoveredOnLinkPrefix(const RouterAdv::PrefixInfoOption &aPio) +bool RoutingManager::UpdateDiscoveredOnLinkPrefix(const Ip6::Nd::PrefixInfoOption &aPio) { Ip6::Prefix prefix; bool needReevaluate = false; @@ -1243,7 +1237,7 @@ exit: // Adds or removes a discovered OMR prefix (external route will be added to or removed // from the Thread network). -void RoutingManager::UpdateDiscoveredOmrPrefix(const RouterAdv::RouteInfoOption &aRio) +void RoutingManager::UpdateDiscoveredOmrPrefix(const Ip6::Nd::RouteInfoOption &aRio) { Ip6::Prefix prefix; ExternalPrefix omrPrefix; @@ -1398,9 +1392,9 @@ bool RoutingManager::NetworkDataContainsOmrPrefix(const Ip6::Prefix &aPrefix) co // Update the `mRouterAdvMessage` with given Router Advertisement message. // Returns a boolean which indicates whether there are changes of `mRouterAdvMessage`. -bool RoutingManager::UpdateRouterAdvMessage(const RouterAdv::RouterAdvMessage *aRouterAdvMessage) +bool RoutingManager::UpdateRouterAdvMessage(const Ip6::Nd::RouterAdvMessage *aRouterAdvMessage) { - RouterAdv::RouterAdvMessage oldRouterAdvMessage; + Ip6::Nd::RouterAdvMessage oldRouterAdvMessage; oldRouterAdvMessage = mRouterAdvMessage; @@ -1493,7 +1487,7 @@ void RoutingManager::ResetDiscoveredPrefixStaleTimer(void) //--------------------------------------------------------------------------------------------------------------------- // ExtneralPrefix -void RoutingManager::ExternalPrefix::InitFrom(const RouterAdv::PrefixInfoOption &aPio) +void RoutingManager::ExternalPrefix::InitFrom(const Ip6::Nd::PrefixInfoOption &aPio) { Clear(); aPio.GetPrefix(mPrefix); @@ -1503,7 +1497,7 @@ void RoutingManager::ExternalPrefix::InitFrom(const RouterAdv::PrefixInfoOption mLastUpdateTime = TimerMilli::GetNow(); } -void RoutingManager::ExternalPrefix::InitFrom(const RouterAdv::RouteInfoOption &aRio) +void RoutingManager::ExternalPrefix::InitFrom(const Ip6::Nd::RouteInfoOption &aRio) { Clear(); aRio.GetPrefix(mPrefix); diff --git a/src/core/border_router/routing_manager.hpp b/src/core/border_router/routing_manager.hpp index 7afc06715..2e92485c1 100644 --- a/src/core/border_router/routing_manager.hpp +++ b/src/core/border_router/routing_manager.hpp @@ -50,7 +50,6 @@ #include #include "border_router/infra_if.hpp" -#include "border_router/router_advertisement.hpp" #include "common/array.hpp" #include "common/error.hpp" #include "common/locator.hpp" @@ -58,6 +57,7 @@ #include "common/string.hpp" #include "common/timer.hpp" #include "net/ip6.hpp" +#include "net/nd6.hpp" #include "thread/network_data.hpp" namespace ot { @@ -248,8 +248,8 @@ private: class ExternalPrefix : private Clearable, public Unequatable { public: - void InitFrom(const RouterAdv::PrefixInfoOption &aPio); - void InitFrom(const RouterAdv::RouteInfoOption &aRio); + void InitFrom(const Ip6::Nd::PrefixInfoOption &aPio); + void InitFrom(const Ip6::Nd::RouteInfoOption &aRio); bool IsOnLinkPrefix(void) const { return mIsOnLinkPrefix; } const Ip6::Prefix &GetPrefix(void) const { return mPrefix; } const TimeMilli & GetLastUpdateTime(void) const { return mLastUpdateTime; } @@ -346,16 +346,16 @@ private: void DeprecateOnLinkPrefix(void); void HandleRouterSolicit(const InfraIf::Icmp6Packet &aPacket, const Ip6::Address &aSrcAddress); void HandleRouterAdvertisement(const InfraIf::Icmp6Packet &aPacket, const Ip6::Address &aSrcAddress); - bool UpdateDiscoveredOnLinkPrefix(const RouterAdv::PrefixInfoOption &aPio); - void UpdateDiscoveredOmrPrefix(const RouterAdv::RouteInfoOption &aRio); + bool UpdateDiscoveredOnLinkPrefix(const Ip6::Nd::PrefixInfoOption &aPio); + void UpdateDiscoveredOmrPrefix(const Ip6::Nd::RouteInfoOption &aRio); void InvalidateDiscoveredPrefixes(void); void InvalidateAllDiscoveredPrefixes(void); bool NetworkDataContainsOmrPrefix(const Ip6::Prefix &aPrefix) const; - bool UpdateRouterAdvMessage(const RouterAdv::RouterAdvMessage *aRouterAdvMessage); + bool UpdateRouterAdvMessage(const Ip6::Nd::RouterAdvMessage *aRouterAdvMessage); void ResetDiscoveredPrefixStaleTimer(void); static bool IsValidBrUlaPrefix(const Ip6::Prefix &aBrUlaPrefix); - static bool IsValidOnLinkPrefix(const RouterAdv::PrefixInfoOption &aPio); + static bool IsValidOnLinkPrefix(const Ip6::Nd::PrefixInfoOption &aPio); static bool IsValidOnLinkPrefix(const Ip6::Prefix &aOnLinkPrefix); // Indicates whether the Routing Manager is running (started). @@ -408,9 +408,9 @@ private: // The RA header and parameters for the infra interface. // This value is initialized with `RouterAdvMessage::SetToDefault` // and updated with RA messages initiated from infra interface. - RouterAdv::RouterAdvMessage mRouterAdvMessage; - TimeMilli mTimeRouterAdvMessageLastUpdate; - bool mLearntRouterAdvMessageFromHost; + Ip6::Nd::RouterAdvMessage mRouterAdvMessage; + TimeMilli mTimeRouterAdvMessageLastUpdate; + bool mLearntRouterAdvMessageFromHost; TimerMilli mDiscoveredPrefixInvalidTimer; TimerMilli mDiscoveredPrefixStaleTimer; diff --git a/src/core/border_router/router_advertisement.cpp b/src/core/net/nd6.cpp similarity index 87% rename from src/core/border_router/router_advertisement.cpp rename to src/core/net/nd6.cpp index c1c3c1d14..f9d06bcb8 100644 --- a/src/core/border_router/router_advertisement.cpp +++ b/src/core/net/nd6.cpp @@ -28,20 +28,18 @@ /** * @file - * This file includes implementations for ICMPv6 Router Advertisement. + * This file includes implementations for IPv6 Neighbor Discovery (ND). * */ -#include "border_router/router_advertisement.hpp" - -#if OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE +#include "nd6.hpp" #include "common/as_core_type.hpp" #include "common/code_utils.hpp" namespace ot { -namespace BorderRouter { -namespace RouterAdv { +namespace Ip6 { +namespace Nd { const Option *Option::GetNextOption(const Option *aCurOption, const uint8_t *aBuffer, uint16_t aBufferLength) { @@ -80,20 +78,20 @@ void PrefixInfoOption::Init(void) OT_UNUSED_VARIABLE(mReserved2); } -void PrefixInfoOption::SetPrefix(const Ip6::Prefix &aPrefix) +void PrefixInfoOption::SetPrefix(const Prefix &aPrefix) { mPrefixLength = aPrefix.mLength; mPrefix = AsCoreType(&aPrefix.mPrefix); } -void PrefixInfoOption::GetPrefix(Ip6::Prefix &aPrefix) const +void PrefixInfoOption::GetPrefix(Prefix &aPrefix) const { aPrefix.Set(mPrefix.GetBytes(), mPrefixLength); } bool PrefixInfoOption::IsValid(void) const { - return (GetSize() >= sizeof(*this)) && (mPrefixLength <= Ip6::Prefix::kMaxLength) && + return (GetSize() >= sizeof(*this)) && (mPrefixLength <= Prefix::kMaxLength) && (GetPreferredLifetime() <= GetValidLifetime()); } @@ -117,21 +115,21 @@ RoutePreference RouteInfoOption::GetPreference(void) const return NetworkData::RoutePreferenceFromValue((mResvdPrf & kPreferenceMask) >> kPreferenceOffset); } -void RouteInfoOption::SetPrefix(const Ip6::Prefix &aPrefix) +void RouteInfoOption::SetPrefix(const Prefix &aPrefix) { SetLength(OptionLengthForPrefix(aPrefix.mLength)); mPrefixLength = aPrefix.mLength; memcpy(GetPrefixBytes(), aPrefix.GetBytes(), aPrefix.GetBytesSize()); } -void RouteInfoOption::GetPrefix(Ip6::Prefix &aPrefix) const +void RouteInfoOption::GetPrefix(Prefix &aPrefix) const { aPrefix.Set(GetPrefixBytes(), mPrefixLength); } bool RouteInfoOption::IsValid(void) const { - return (GetSize() >= kMinSize) && (mPrefixLength <= Ip6::Prefix::kMaxLength) && + return (GetSize() >= kMinSize) && (mPrefixLength <= Prefix::kMaxLength) && (GetLength() >= OptionLengthForPrefix(mPrefixLength)) && NetworkData::IsRoutePreferenceValid(GetPreference()); } @@ -177,7 +175,7 @@ void RouterAdvMessage::SetToDefault(void) OT_UNUSED_VARIABLE(mRetransTimer); Clear(); - mType = Ip6::Icmp::Header::kTypeRouterAdvert; + mType = Icmp::Header::kTypeRouterAdvert; } RoutePreference RouterAdvMessage::GetDefaultRouterPreference(void) const @@ -197,11 +195,9 @@ void RouterAdvMessage::SetDefaultRouterPreference(RoutePreference aPreference) RouterSolicitMessage::RouterSolicitMessage(void) { mHeader.Clear(); - mHeader.SetType(Ip6::Icmp::Header::kTypeRouterSolicit); + mHeader.SetType(Icmp::Header::kTypeRouterSolicit); } -} // namespace RouterAdv -} // namespace BorderRouter +} // namespace Nd +} // namespace Ip6 } // namespace ot - -#endif // OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE diff --git a/src/core/border_router/router_advertisement.hpp b/src/core/net/nd6.hpp similarity index 93% rename from src/core/border_router/router_advertisement.hpp rename to src/core/net/nd6.hpp index c98f44072..61c8d1d9d 100644 --- a/src/core/border_router/router_advertisement.hpp +++ b/src/core/net/nd6.hpp @@ -28,19 +28,17 @@ /** * @file - * This file includes definitions for IPv6 Router Advertisement. + * This file includes definitions for IPv6 Neighbor Discovery (ND). * - * See RFC 4861: Neighbor Discovery for IP version 6 (https://tools.ietf.org/html/rfc4861). + * See RFC 4861 (https://tools.ietf.org/html/rfc4861) and RFC 4191 (https://tools.ietf.org/html/rfc4191). * */ -#ifndef ROUTER_ADVERTISEMENT_HPP_ -#define ROUTER_ADVERTISEMENT_HPP_ +#ifndef ND6_HPP_ +#define ND6_HPP_ #include "openthread-core-config.h" -#if OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE - #include #include @@ -57,8 +55,8 @@ using ot::Encoding::BigEndian::HostSwap16; using ot::Encoding::BigEndian::HostSwap32; namespace ot { -namespace BorderRouter { -namespace RouterAdv { +namespace Ip6 { +namespace Nd { typedef NetworkData::RoutePreference RoutePreference; ///< Route Preference @@ -256,15 +254,15 @@ public: * @param[in] aPrefix The prefix contained in this option. * */ - void SetPrefix(const Ip6::Prefix &aPrefix); + void SetPrefix(const Prefix &aPrefix); /** * This method gets the prefix in this option. * - * @param[out] aPrefix Reference to an `Ip6::Prefix` to return the prefix. + * @param[out] aPrefix Reference to a `Prefix` to return the prefix. * */ - void GetPrefix(Ip6::Prefix &aPrefix) const; + void GetPrefix(Prefix &aPrefix) const; /** * This method indicates whether or not the option is valid. @@ -303,12 +301,12 @@ private: static constexpr uint8_t kAutoConfigFlagMask = 0x40; // Autonomous address-configuration flag. static constexpr uint8_t kOnLinkFlagMask = 0x80; // On-link flag. - uint8_t mPrefixLength; // The prefix length in bits. - uint8_t mFlags; // The flags field. - uint32_t mValidLifetime; // The valid lifetime of the prefix. - uint32_t mPreferredLifetime; // The preferred lifetime of the prefix. - uint32_t mReserved2; // The reserved field. - Ip6::Address mPrefix; // The prefix. + uint8_t mPrefixLength; // The prefix length in bits. + uint8_t mFlags; // The flags field. + uint32_t mValidLifetime; // The valid lifetime of the prefix. + uint32_t mPreferredLifetime; // The preferred lifetime of the prefix. + uint32_t mReserved2; // The reserved field. + Address mPrefix; // The prefix. } OT_TOOL_PACKED_END; static_assert(sizeof(PrefixInfoOption) == 32, "invalid PrefixInfoOption structure"); @@ -370,15 +368,15 @@ public: * @param[in] aPrefix The prefix contained in this option. * */ - void SetPrefix(const Ip6::Prefix &aPrefix); + void SetPrefix(const Prefix &aPrefix); /** * This method gets the prefix in this option. * - * @param[out] aPrefix Reference to an `Ip6::Prefix` to return the prefix. + * @param[out] aPrefix Reference to a `Prefix` to return the prefix. * */ - void GetPrefix(Ip6::Prefix &aPrefix) const; + void GetPrefix(Prefix &aPrefix) const; /** * This method tells whether this option is valid. @@ -561,15 +559,13 @@ public: RouterSolicitMessage(void); private: - Ip6::Icmp::Header mHeader; // The common ICMPv6 header. + Icmp::Header mHeader; // The common ICMPv6 header. } OT_TOOL_PACKED_END; static_assert(sizeof(RouterSolicitMessage) == 8, "invalid RouterSolicitMessage structure"); -} // namespace RouterAdv -} // namespace BorderRouter +} // namespace Nd +} // namespace Ip6 } // namespace ot -#endif // OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE - -#endif // ROUTER_ADVERTISEMENT_HPP_ +#endif // ND6_HPP_ From b7c2c52d5c2312767bb4844daa351e3a042107e6 Mon Sep 17 00:00:00 2001 From: Simon Lin Date: Wed, 15 Jun 2022 11:11:27 +0800 Subject: [PATCH 67/80] [thci] wait for netdata to stabilize if `BORDER_ROUTING` is enabled (#7798) A BR with `BORDER_ROUTING` feature may add OMR prefix and external routes to the network data when Thread is started. The network data changes may incur additional Thread communications that may interfere with certification traffic. This commit enhances THCI to wait for the network data to stabilize after the BR is started. --- tools/harness-thci/OpenThread.py | 36 +++++++++++++++++++++++++++++ tools/harness-thci/OpenThread_BR.py | 27 ++++++++++++++++++++++ 2 files changed, 63 insertions(+) diff --git a/tools/harness-thci/OpenThread.py b/tools/harness-thci/OpenThread.py index d536c9c31..0d94f8292 100644 --- a/tools/harness-thci/OpenThread.py +++ b/tools/harness-thci/OpenThread.py @@ -1145,6 +1145,10 @@ class OpenThreadTHCI(object): self, timeout * 2)) else: return False + + if self.IsBorderRouter: + self._waitBorderRoutingStabilize() + return True @API @@ -1609,6 +1613,30 @@ class OpenThreadTHCI(object): else: return False + @watched + def getNetworkData(self): + lines = self.__executeCommand('netdata show') + prefixes, routes, services = [], [], [] + classify = None + + for line in lines: + if line == 'Prefixes:': + classify = prefixes + elif line == 'Routes:': + classify = routes + elif line == 'Services:': + classify = services + elif line == 'Done': + classify = None + else: + classify.append(line) + + return { + 'Prefixes': prefixes, + 'Routes': routes, + 'Services': services, + } + @API def setNetworkIDTimeout(self, iNwkIDTimeOut): """set networkid timeout for Thread device @@ -3112,6 +3140,14 @@ class OpenThreadTHCI(object): def setLeaderWeight(self, iWeight=72): self.__executeCommand('leaderweight %d' % iWeight) + @watched + def isBorderRoutingEnabled(self): + try: + self.__executeCommand('br omrprefix') + return True + except CommandError: + return False + def __detectZephyr(self): """Detect if the device is running Zephyr and adapt in that case""" diff --git a/tools/harness-thci/OpenThread_BR.py b/tools/harness-thci/OpenThread_BR.py index b8566fe49..ac6747972 100644 --- a/tools/harness-thci/OpenThread_BR.py +++ b/tools/harness-thci/OpenThread_BR.py @@ -695,3 +695,30 @@ class OpenThread_BR(OpenThreadTHCI, IThci): """ self.externalCommissioner.MLR([sAddr], 0) return True + + @watched + def _waitBorderRoutingStabilize(self): + """ + Wait for Network Data to stabilize if BORDER_ROUTING is enabled. + """ + if not self.isBorderRoutingEnabled(): + return + + MAX_TIMEOUT = 30 + MIN_TIMEOUT = 15 + CHECK_INTERVAL = 3 + + time.sleep(MIN_TIMEOUT) + + lastNetData = self.getNetworkData() + for i in range((MAX_TIMEOUT - MIN_TIMEOUT) // CHECK_INTERVAL): + time.sleep(CHECK_INTERVAL) + curNetData = self.getNetworkData() + + # Wait until the Network Data is not changing, and there is OMR Prefix and External Routes available + if curNetData == lastNetData and len(curNetData['Prefixes']) > 0 and len(curNetData['Routes']) > 0: + break + + lastNetData = curNetData + + return lastNetData From 49b98466328136c2e066b5b04de644a187c68307 Mon Sep 17 00:00:00 2001 From: Abtin Keshavarzian Date: Tue, 14 Jun 2022 21:58:35 -0700 Subject: [PATCH 68/80] [routing-manager] simplify RA message parsing and preparation (#7789) This commit adds a new class `RouterAdvertMessage` representing an RA message. It provides methods to append options (PIO or RIO) to the RA message or parse and iterate over the options in the RA message (using newly added `Option::Iterator` which allows use of range-based `for` loop). These method are used by `RoutingManager` and help simplify the code. --- src/core/border_router/routing_manager.cpp | 208 ++++++--------- src/core/border_router/routing_manager.hpp | 8 +- src/core/net/nd6.cpp | 129 ++++++++-- src/core/net/nd6.hpp | 280 +++++++++++++++------ 4 files changed, 382 insertions(+), 243 deletions(-) diff --git a/src/core/border_router/routing_manager.cpp b/src/core/border_router/routing_manager.cpp index 01bcf2bd4..4dfa0fcd3 100644 --- a/src/core/border_router/routing_manager.cpp +++ b/src/core/border_router/routing_manager.cpp @@ -828,128 +828,76 @@ Error RoutingManager::SendRouterSolicitation(void) return mInfraIf.Send(packet, destAddress); } -// This method sends Router Advertisement messages to advertise on-link prefix and route for OMR prefix. -// @param[in] aNewOmrPrefixes An array of the new OMR prefixes to be advertised. -// Empty array means we should stop advertising OMR prefixes. void RoutingManager::SendRouterAdvertisement(const OmrPrefixArray &aNewOmrPrefixes) { - uint8_t buffer[kMaxRouterAdvMessageLength]; - uint16_t bufferLength = 0; + uint8_t buffer[kMaxRouterAdvMessageLength]; + Ip6::Nd::RouterAdvertMessage raMsg(mRouterAdvertHeader, buffer); - static_assert(sizeof(mRouterAdvMessage) <= sizeof(buffer), "RA buffer too small"); - memcpy(buffer, &mRouterAdvMessage, sizeof(mRouterAdvMessage)); - bufferLength += sizeof(mRouterAdvMessage); + // Append PIO for local on-link prefix. Ensure it is either being + // advertised or deprecated. - if (mIsAdvertisingLocalOnLinkPrefix) + if (mIsAdvertisingLocalOnLinkPrefix || mOnLinkPrefixDeprecateTimer.IsRunning()) { - Ip6::Nd::PrefixInfoOption *pio; + uint32_t validLifetime = kDefaultOnLinkPrefixLifetime; + uint32_t preferredLifetime = kDefaultOnLinkPrefixLifetime; - OT_ASSERT(bufferLength + sizeof(Ip6::Nd::PrefixInfoOption) <= sizeof(buffer)); - - pio = reinterpret_cast(buffer + bufferLength); - - pio->Init(); - pio->SetOnLinkFlag(); - pio->SetAutoAddrConfigFlag(); - pio->SetValidLifetime(kDefaultOnLinkPrefixLifetime); - pio->SetPreferredLifetime(kDefaultOnLinkPrefixLifetime); - pio->SetPrefix(mLocalOnLinkPrefix); - - bufferLength += pio->GetSize(); - - LogInfo("Send on-link prefix %s in PIO (preferred lifetime = %u seconds, valid lifetime = %u seconds)", - mLocalOnLinkPrefix.ToString().AsCString(), pio->GetPreferredLifetime(), pio->GetValidLifetime()); - - mTimeAdvertisedOnLinkPrefix = TimerMilli::GetNow(); - } - else if (mOnLinkPrefixDeprecateTimer.IsRunning()) - { - Ip6::Nd::PrefixInfoOption *pio; - - OT_ASSERT(bufferLength + sizeof(Ip6::Nd::PrefixInfoOption) <= sizeof(buffer)); - - pio = reinterpret_cast(buffer + bufferLength); - - pio->Init(); - pio->SetOnLinkFlag(); - pio->SetAutoAddrConfigFlag(); - pio->SetValidLifetime(TimeMilli::MsecToSec(mOnLinkPrefixDeprecateTimer.GetFireTime() - TimerMilli::GetNow())); - - // Set zero preferred lifetime to immediately deprecate the advertised on-link prefix. - pio->SetPreferredLifetime(0); - pio->SetPrefix(mLocalOnLinkPrefix); - - bufferLength += pio->GetSize(); - - LogInfo("Send on-link prefix %s in PIO (preferred lifetime = %u seconds, valid lifetime = %u seconds)", - mLocalOnLinkPrefix.ToString().AsCString(), pio->GetPreferredLifetime(), pio->GetValidLifetime()); - } - - // Invalidate the advertised OMR prefixes if they are no longer in the new OMR prefix array. - - for (const OmrPrefix &advertisedOmrPrefix : mAdvertisedOmrPrefixes) - { - if (!aNewOmrPrefixes.ContainsMatching(advertisedOmrPrefix.GetPrefix())) + if (mOnLinkPrefixDeprecateTimer.IsRunning()) { - Ip6::Nd::RouteInfoOption *rio; + validLifetime = TimeMilli::MsecToSec(mOnLinkPrefixDeprecateTimer.GetFireTime() - TimerMilli::GetNow()); + preferredLifetime = 0; + } - OT_ASSERT(bufferLength + - Ip6::Nd::RouteInfoOption::OptionSizeForPrefix(advertisedOmrPrefix.GetPrefix().GetLength()) <= - sizeof(buffer)); + SuccessOrAssert(raMsg.AppendPrefixInfoOption(mLocalOnLinkPrefix, validLifetime, preferredLifetime)); - rio = reinterpret_cast(buffer + bufferLength); + if (mIsAdvertisingLocalOnLinkPrefix) + { + mTimeAdvertisedOnLinkPrefix = TimerMilli::GetNow(); + } - // Set zero route lifetime to immediately invalidate the advertised OMR prefix. - rio->Init(); - rio->SetRouteLifetime(0); - rio->SetPrefix(advertisedOmrPrefix.GetPrefix()); + LogInfo("RouterAdvert: Added PIO for %s (valid=%u, preferred=%u)", mLocalOnLinkPrefix.ToString().AsCString(), + validLifetime, preferredLifetime); + } - bufferLength += rio->GetSize(); + // Invalidate previously advertised OMR prefixes if they are no + // longer in the new OMR prefix array. - LogInfo("Stop advertising OMR prefix %s on %s", advertisedOmrPrefix.ToString().AsCString(), - mInfraIf.ToString().AsCString()); + for (const OmrPrefix &omrPrefix : mAdvertisedOmrPrefixes) + { + if (!aNewOmrPrefixes.ContainsMatching(omrPrefix.GetPrefix())) + { + SuccessOrAssert( + raMsg.AppendRouteInfoOption(omrPrefix.GetPrefix(), /* aRouteLifetime */ 0, omrPrefix.GetPreference())); + + LogInfo("RouterAdvert: Added RIO for %s (lifetime=0)", omrPrefix.ToString().AsCString()); } } - for (const OmrPrefix &newOmrPrefix : aNewOmrPrefixes) + for (const OmrPrefix &omrPrefix : aNewOmrPrefixes) { - Ip6::Nd::RouteInfoOption *rio; + SuccessOrAssert( + raMsg.AppendRouteInfoOption(omrPrefix.GetPrefix(), kDefaultOmrPrefixLifetime, omrPrefix.GetPreference())); - OT_ASSERT(bufferLength + Ip6::Nd::RouteInfoOption::OptionSizeForPrefix(newOmrPrefix.GetPrefix().GetLength()) <= - sizeof(buffer)); - - rio = reinterpret_cast(buffer + bufferLength); - - rio->Init(); - rio->SetRouteLifetime(kDefaultOmrPrefixLifetime); - rio->SetPreference(newOmrPrefix.GetPreference()); - rio->SetPrefix(newOmrPrefix.GetPrefix()); - - bufferLength += rio->GetSize(); - - LogInfo("Send OMR prefix %s in RIO (valid lifetime = %u seconds)", newOmrPrefix.ToString().AsCString(), + LogInfo("RouterAdvert: Added RIO for %s (lifetime=%u)", omrPrefix.ToString().AsCString(), kDefaultOmrPrefixLifetime); } - // Send the message only when there are options. - if (bufferLength > sizeof(mRouterAdvMessage)) + if (raMsg.ContainsAnyOptions()) { - Error error; - Ip6::Address destAddress; - InfraIf::Icmp6Packet packet; + Error error; + Ip6::Address destAddress; ++mRouterAdvertisementCount; - packet.Init(buffer, bufferLength); destAddress.SetToLinkLocalAllNodesMulticast(); - error = mInfraIf.Send(packet, destAddress); + error = mInfraIf.Send(raMsg.GetAsPacket(), destAddress); if (error == kErrorNone) { mLastRouterAdvertisementSendTime = TimerMilli::GetNow(); LogInfo("Sent Router Advertisement on %s", mInfraIf.ToString().AsCString()); - DumpDebg("[BR-CERT] direction=send | type=RA |", buffer, bufferLength); + DumpDebg("[BR-CERT] direction=send | type=RA |", raMsg.GetAsPacket().GetBytes(), + raMsg.GetAsPacket().GetLength()); } else { @@ -1052,7 +1000,7 @@ void RoutingManager::HandleRouterSolicitTimer(void) // Invalidate the learned RA message if it is not refreshed during Router Solicitation. if (mTimeRouterAdvMessageLastUpdate <= mTimeRouterSolicitStart) { - UpdateRouterAdvMessage(/* aRouterAdvMessage */ nullptr); + UpdateRouterAdvertHeader(/* aRouterAdvertMessage */ nullptr); } mRouterSolicitCount = 0; @@ -1105,51 +1053,30 @@ void RoutingManager::HandleRouterSolicit(const InfraIf::Icmp6Packet &aPacket, co void RoutingManager::HandleRouterAdvertisement(const InfraIf::Icmp6Packet &aPacket, const Ip6::Address &aSrcAddress) { - OT_ASSERT(mIsRunning); OT_UNUSED_VARIABLE(aSrcAddress); - bool needReevaluate = false; - const uint8_t * optionsBegin; - uint16_t optionsLength; - const Ip6::Nd::Option * option; - const Ip6::Nd::RouterAdvMessage *routerAdvMessage; + bool needReevaluate = false; + Ip6::Nd::RouterAdvertMessage routerAdvMessage(aPacket); - VerifyOrExit(aPacket.GetLength() >= sizeof(Ip6::Nd::RouterAdvMessage)); + OT_ASSERT(mIsRunning); + + VerifyOrExit(routerAdvMessage.IsValid()); LogInfo("Received Router Advertisement from %s on %s", aSrcAddress.ToString().AsCString(), mInfraIf.ToString().AsCString()); DumpDebg("[BR-CERT] direction=recv | type=RA |", aPacket.GetBytes(), aPacket.GetLength()); - routerAdvMessage = reinterpret_cast(aPacket.GetBytes()); - optionsBegin = aPacket.GetBytes() + sizeof(Ip6::Nd::RouterAdvMessage); - optionsLength = aPacket.GetLength() - sizeof(Ip6::Nd::RouterAdvMessage); - - option = nullptr; - while ((option = Ip6::Nd::Option::GetNextOption(option, optionsBegin, optionsLength)) != nullptr) + for (const Ip6::Nd::Option &option : routerAdvMessage) { - switch (option->GetType()) + switch (option.GetType()) { - case Ip6::Nd::Option::Type::kPrefixInfo: - { - const Ip6::Nd::PrefixInfoOption *pio = static_cast(option); + case Ip6::Nd::Option::kTypePrefixInfo: + needReevaluate |= UpdateDiscoveredOnLinkPrefix(static_cast(option)); + break; - if (pio->IsValid()) - { - needReevaluate |= UpdateDiscoveredOnLinkPrefix(*pio); - } - } - break; - - case Ip6::Nd::Option::Type::kRouteInfo: - { - const Ip6::Nd::RouteInfoOption *rio = static_cast(option); - - if (rio->IsValid()) - { - UpdateDiscoveredOmrPrefix(*rio); - } - } - break; + case Ip6::Nd::Option::kTypeRouteInfo: + UpdateDiscoveredOmrPrefix(static_cast(option)); + break; default: break; @@ -1160,7 +1087,7 @@ void RoutingManager::HandleRouterAdvertisement(const InfraIf::Icmp6Packet &aPack // initiated from the infra interface. if (mInfraIf.HasAddress(aSrcAddress)) { - needReevaluate |= UpdateRouterAdvMessage(routerAdvMessage); + needReevaluate |= UpdateRouterAdvertHeader(&routerAdvMessage); } if (needReevaluate) @@ -1182,6 +1109,8 @@ bool RoutingManager::UpdateDiscoveredOnLinkPrefix(const Ip6::Nd::PrefixInfoOptio ExternalPrefix onLinkPrefix; ExternalPrefix *existingPrefix = nullptr; + VerifyOrExit(aPio.IsValid()); + aPio.GetPrefix(prefix); if (!IsValidOnLinkPrefix(aPio)) @@ -1243,6 +1172,8 @@ void RoutingManager::UpdateDiscoveredOmrPrefix(const Ip6::Nd::RouteInfoOption &a ExternalPrefix omrPrefix; ExternalPrefix *existingPrefix = nullptr; + VerifyOrExit(aRio.IsValid()); + aRio.GetPrefix(prefix); if (!IsValidOmrPrefix(prefix)) @@ -1390,35 +1321,36 @@ bool RoutingManager::NetworkDataContainsOmrPrefix(const Ip6::Prefix &aPrefix) co return contain; } -// Update the `mRouterAdvMessage` with given Router Advertisement message. -// Returns a boolean which indicates whether there are changes of `mRouterAdvMessage`. -bool RoutingManager::UpdateRouterAdvMessage(const Ip6::Nd::RouterAdvMessage *aRouterAdvMessage) +bool RoutingManager::UpdateRouterAdvertHeader(const Ip6::Nd::RouterAdvertMessage *aRouterAdvertMessage) { - Ip6::Nd::RouterAdvMessage oldRouterAdvMessage; + // Updates the `mRouterAdvertHeader` from the given RA message. + // Returns a boolean which indicates whether there was any changes + // to `mRouterAdvertHeader`. - oldRouterAdvMessage = mRouterAdvMessage; + Ip6::Nd::RouterAdvertMessage::Header oldHeader; + oldHeader = mRouterAdvertHeader; mTimeRouterAdvMessageLastUpdate = TimerMilli::GetNow(); - if (aRouterAdvMessage == nullptr || aRouterAdvMessage->GetRouterLifetime() == 0) + if (aRouterAdvertMessage == nullptr || aRouterAdvertMessage->GetHeader().GetRouterLifetime() == 0) { - mRouterAdvMessage.SetToDefault(); + mRouterAdvertHeader.SetToDefault(); mLearntRouterAdvMessageFromHost = false; } else { - // The checksum is set to zero in `mRouterAdvMessage` + // The checksum is set to zero in `mRouterAdvertHeader` // which indicates to platform that it needs to do the // calculation and update it. - mRouterAdvMessage = *aRouterAdvMessage; - mRouterAdvMessage.SetChecksum(0); + mRouterAdvertHeader = aRouterAdvertMessage->GetHeader(); + mRouterAdvertHeader.SetChecksum(0); mLearntRouterAdvMessageFromHost = true; } ResetDiscoveredPrefixStaleTimer(); - return (mRouterAdvMessage != oldRouterAdvMessage); + return (mRouterAdvertHeader != oldHeader); } void RoutingManager::ResetDiscoveredPrefixStaleTimer(void) diff --git a/src/core/border_router/routing_manager.hpp b/src/core/border_router/routing_manager.hpp index 2e92485c1..f658eaae8 100644 --- a/src/core/border_router/routing_manager.hpp +++ b/src/core/border_router/routing_manager.hpp @@ -351,7 +351,7 @@ private: void InvalidateDiscoveredPrefixes(void); void InvalidateAllDiscoveredPrefixes(void); bool NetworkDataContainsOmrPrefix(const Ip6::Prefix &aPrefix) const; - bool UpdateRouterAdvMessage(const Ip6::Nd::RouterAdvMessage *aRouterAdvMessage); + bool UpdateRouterAdvertHeader(const Ip6::Nd::RouterAdvertMessage *aRouterAdvertMessage); void ResetDiscoveredPrefixStaleTimer(void); static bool IsValidBrUlaPrefix(const Ip6::Prefix &aBrUlaPrefix); @@ -408,9 +408,9 @@ private: // The RA header and parameters for the infra interface. // This value is initialized with `RouterAdvMessage::SetToDefault` // and updated with RA messages initiated from infra interface. - Ip6::Nd::RouterAdvMessage mRouterAdvMessage; - TimeMilli mTimeRouterAdvMessageLastUpdate; - bool mLearntRouterAdvMessageFromHost; + Ip6::Nd::RouterAdvertMessage::Header mRouterAdvertHeader; + TimeMilli mTimeRouterAdvMessageLastUpdate; + bool mLearntRouterAdvMessageFromHost; TimerMilli mDiscoveredPrefixInvalidTimer; TimerMilli mDiscoveredPrefixStaleTimer; diff --git a/src/core/net/nd6.cpp b/src/core/net/nd6.cpp index f9d06bcb8..5b57e1be9 100644 --- a/src/core/net/nd6.cpp +++ b/src/core/net/nd6.cpp @@ -28,7 +28,7 @@ /** * @file - * This file includes implementations for IPv6 Neighbor Discovery (ND). + * This file includes implementations for IPv6 Neighbor Discovery (ND6). * */ @@ -41,29 +41,44 @@ namespace ot { namespace Ip6 { namespace Nd { -const Option *Option::GetNextOption(const Option *aCurOption, const uint8_t *aBuffer, uint16_t aBufferLength) +//---------------------------------------------------------------------------------------------------------------------- +// Option::Iterator + +Option::Iterator::Iterator(void) + : mOption(nullptr) + , mEnd(nullptr) { - const uint8_t *nextOption = nullptr; - const uint8_t *bufferEnd = aBuffer + aBufferLength; + // An empty iterator (used to indicate `end()` of list). +} - VerifyOrExit(aBuffer != nullptr, nextOption = nullptr); +Option::Iterator::Iterator(const void *aStart, const void *aEnd) + : mOption(nullptr) + , mEnd(reinterpret_cast(aEnd)) +{ + // Note that `Validate()` uses `mEnd` so can only be called after + // `mEnd` is set. - if (aCurOption == nullptr) - { - nextOption = aBuffer; - } - else - { - nextOption = reinterpret_cast(aCurOption) + aCurOption->GetSize(); - } + mOption = Validate(reinterpret_cast(aStart)); +} - VerifyOrExit(nextOption + sizeof(Option) <= bufferEnd, nextOption = nullptr); - VerifyOrExit(reinterpret_cast(nextOption)->GetSize() > 0, nextOption = nullptr); - VerifyOrExit(nextOption + reinterpret_cast(nextOption)->GetSize() <= bufferEnd, - nextOption = nullptr); +const Option *Option::Iterator::Next(const Option *aOption) +{ + return reinterpret_cast(reinterpret_cast(aOption) + aOption->GetSize()); +} -exit: - return reinterpret_cast(nextOption); +void Option::Iterator::Advance(void) +{ + mOption = (mOption != nullptr) ? Validate(Next(mOption)) : nullptr; +} + +const Option *Option::Iterator::Validate(const Option *aOption) const +{ + // Check if `aOption` is well-formed and fits in the range + // up to `mEnd`. Returns `aOption` if it is valid, `nullptr` + // otherwise. + + return ((aOption != nullptr) && ((aOption + 1) <= mEnd) && aOption->IsValid() && (Next(aOption) <= mEnd)) ? aOption + : nullptr; } //---------------------------------------------------------------------------------------------------------------------- @@ -72,7 +87,7 @@ exit: void PrefixInfoOption::Init(void) { Clear(); - SetType(Type::kPrefixInfo); + SetType(kTypePrefixInfo); SetSize(sizeof(PrefixInfoOption)); OT_UNUSED_VARIABLE(mReserved2); @@ -101,7 +116,7 @@ bool PrefixInfoOption::IsValid(void) const void RouteInfoOption::Init(void) { Clear(); - SetType(Type::kRouteInfo); + SetType(kTypeRouteInfo); } void RouteInfoOption::SetPreference(RoutePreference aPreference) @@ -165,9 +180,9 @@ uint8_t RouteInfoOption::OptionLengthForPrefix(uint8_t aPrefixLength) } //---------------------------------------------------------------------------------------------------------------------- -// RouterAdvMessage +// RouterAdverMessage::Header -void RouterAdvMessage::SetToDefault(void) +void RouterAdvertMessage::Header::SetToDefault(void) { OT_UNUSED_VARIABLE(mCode); OT_UNUSED_VARIABLE(mCurHopLimit); @@ -178,17 +193,81 @@ void RouterAdvMessage::SetToDefault(void) mType = Icmp::Header::kTypeRouterAdvert; } -RoutePreference RouterAdvMessage::GetDefaultRouterPreference(void) const +RoutePreference RouterAdvertMessage::Header::GetDefaultRouterPreference(void) const { return NetworkData::RoutePreferenceFromValue((mFlags & kPreferenceMask) >> kPreferenceOffset); } -void RouterAdvMessage::SetDefaultRouterPreference(RoutePreference aPreference) +void RouterAdvertMessage::Header::SetDefaultRouterPreference(RoutePreference aPreference) { mFlags &= ~kPreferenceMask; mFlags |= (NetworkData::RoutePreferenceToValue(aPreference) << kPreferenceOffset) & kPreferenceMask; } +//---------------------------------------------------------------------------------------------------------------------- +// RouterAdverMessage + +Option *RouterAdvertMessage::AppendOption(uint16_t aOptionSize) +{ + // This method appends an option with a given size to the RA + // message by reserving space in the data buffer if there is + // room. On success returns pointer to the option, on failure + // returns `nullptr`. The returned option needs to be + // initialized and populated by the caller. + + Option * option = nullptr; + uint32_t newLength = mData.GetLength(); + + newLength += aOptionSize; + VerifyOrExit(newLength <= mMaxLength); + + option = reinterpret_cast