From 95fa6220d7664bd5b87109bbb352aa4ba1536ce5 Mon Sep 17 00:00:00 2001 From: Abtin Keshavarzian Date: Fri, 17 Sep 2021 08:04:44 -0700 Subject: [PATCH] [tmf] add `AnycastLocator` module (#6513) This commit adds a new class `AnycastLocator` which can be used to locate the closest destination of an anycast IPv6 address (i.e., find the related mesh local EID and RLOC16). The closest destination is determined based on the the current routing table and path costs within the Thread mesh. The implementation uses a CoAP confirmable post request to a newly added URI path ("a/yl"). The destination IPv6 address of such as request message is set to the anycast address to be located. The receiver of the request message sends a CoAP response which includes the "Mesh Local EID" and "Thread RLOC16" TLVs. This commit also adds support this new feature in CLI (adding a new `locate ` command). Finally this commit adds `test_anycast_locator.py` to test behavior of the new feature. --- Android.mk | 1 + etc/cmake/options.cmake | 5 + etc/gn/openthread.gni | 3 + examples/Makefile-simulation | 1 + examples/README.md | 1 + examples/common-switches.mk | 4 + include/openthread/instance.h | 2 +- include/openthread/thread.h | 52 ++++++ script/check-scan-build | 1 + script/check-simulation-build-autotools | 1 + script/check-size | 1 + script/cmake-build | 1 + script/make-pretty | 1 + script/test | 2 + src/cli/README.md | 55 ++++++ src/cli/cli.cpp | 66 ++++++- src/cli/cli.hpp | 17 +- src/core/BUILD.gn | 6 + src/core/CMakeLists.txt | 1 + src/core/Makefile.am | 2 + src/core/api/thread_api.cpp | 15 ++ src/core/common/instance.hpp | 7 + src/core/config/tmf.h | 25 +++ src/core/thread/anycast_locator.cpp | 168 ++++++++++++++++++ src/core/thread/anycast_locator.hpp | 118 ++++++++++++ src/core/thread/thread_netif.cpp | 3 + src/core/thread/thread_netif.hpp | 4 + src/core/thread/tmf.hpp | 2 + src/core/thread/uri_paths.cpp | 1 + src/core/thread/uri_paths.hpp | 1 + src/posix/Makefile-posix | 1 + tests/scripts/thread-cert/Makefile.am | 2 + tests/scripts/thread-cert/node.py | 16 ++ .../thread-cert/test_anycast_locator.py | 166 +++++++++++++++++ tests/toranj/openthread-core-toranj-config.h | 8 + 35 files changed, 757 insertions(+), 3 deletions(-) create mode 100644 src/core/thread/anycast_locator.cpp create mode 100644 src/core/thread/anycast_locator.hpp create mode 100755 tests/scripts/thread-cert/test_anycast_locator.py diff --git a/Android.mk b/Android.mk index bad1ea5c1..a00dc6db8 100644 --- a/Android.mk +++ b/Android.mk @@ -304,6 +304,7 @@ LOCAL_SRC_FILES := \ src/core/thread/address_resolver.cpp \ src/core/thread/announce_begin_server.cpp \ src/core/thread/announce_sender.cpp \ + src/core/thread/anycast_locator.cpp \ src/core/thread/child_table.cpp \ src/core/thread/csl_tx_scheduler.cpp \ src/core/thread/discover_scanner.cpp \ diff --git a/etc/cmake/options.cmake b/etc/cmake/options.cmake index 268594d92..9d31ecac0 100644 --- a/etc/cmake/options.cmake +++ b/etc/cmake/options.cmake @@ -34,6 +34,11 @@ option(OT_FTD "enable FTD" ON) option(OT_MTD "enable MTD" ON) option(OT_RCP "enable RCP" ON) +option(OT_ANYCAST_LOCATOR "enable anycast locator support") +if(OT_ANYCAST_LOCATOR) + target_compile_definitions(ot-config INTERFACE "OPENTHREAD_CONFIG_TMF_ANYCAST_LOCATOR_ENABLE=1") +endif() + option(OT_ASSERT "enable assert function OT_ASSERT()" ON) if(OT_ASSERT) target_compile_definitions(ot-config INTERFACE "OPENTHREAD_CONFIG_ASSERT_ENABLE=1") diff --git a/etc/gn/openthread.gni b/etc/gn/openthread.gni index 1bd07dd39..1a8f285ee 100644 --- a/etc/gn/openthread.gni +++ b/etc/gn/openthread.gni @@ -78,6 +78,9 @@ if (openthread_enable_core_config_args) { # Enable assertions. openthread_config_assert_enable = true + # Enable anycast locator functionality + openthread_config_anycast_locator_enable = false + # Enable backbone router functionality openthread_config_backbone_router_enable = false diff --git a/examples/Makefile-simulation b/examples/Makefile-simulation index 8bdcb3fe3..0df78dc68 100644 --- a/examples/Makefile-simulation +++ b/examples/Makefile-simulation @@ -35,6 +35,7 @@ DEBUG ?= 0 # Enable most features by default to cover most code +ANYCAST_LOCATOR ?= 1 BORDER_AGENT ?= 1 BORDER_ROUTER ?= 1 COAP ?= 1 diff --git a/examples/README.md b/examples/README.md index cff5d4c5d..9a1861dcf 100644 --- a/examples/README.md +++ b/examples/README.md @@ -6,6 +6,7 @@ This page lists the available common switches with description. Unless stated ot | Makefile switch | CMake switch | Description | | --- | --- | --- | +| ANYCAST_LOCATOR | OT_ANYCAST_LOCATOR | Enables anycast locator functionality. | | BACKBONE_ROUTER | OT_BACKBONE_ROUTER | Enables Backbone Router functionality for Thread 1.2. | | BIG_ENDIAN | OT_BIG_ENDIAN | Allows the host platform to use big-endian byte order. | | BORDER_AGENT | OT_BORDER_AGENT | Enables support for border agent. In most cases, enable this switch if you are building On-mesh Commissioner or Border Router with External Commissioning support. | diff --git a/examples/common-switches.mk b/examples/common-switches.mk index 6fbb4f8e0..33687fecc 100644 --- a/examples/common-switches.mk +++ b/examples/common-switches.mk @@ -28,6 +28,7 @@ # OpenThread Features (Makefile default configuration). +ANYCAST_LOCATOR ?= 0 BACKBONE_ROUTER ?= 0 BIG_ENDIAN ?= 0 BORDER_AGENT ?= 0 @@ -90,6 +91,9 @@ UDP_FORWARD ?= 0 UPTIME ?= 0 RCP_RESTORATION_MAX_COUNT ?= 0 +ifeq ($(ANYCAST_LOCATOR),1) +COMMONCFLAGS += -DOPENTHREAD_CONFIG_TMF_ANYCAST_LOCATOR_ENABLE=1 +endif ifeq ($(BACKBONE_ROUTER),1) COMMONCFLAGS += -DOPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE=1 diff --git a/include/openthread/instance.h b/include/openthread/instance.h index 2d9318bad..ab22b89d2 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 (164) +#define OPENTHREAD_API_VERSION (165) /** * @addtogroup api-instance diff --git a/include/openthread/thread.h b/include/openthread/thread.h index b2cff9c6f..cbac610ec 100644 --- a/include/openthread/thread.h +++ b/include/openthread/thread.h @@ -925,6 +925,58 @@ void otThreadSetDiscoveryRequestCallback(otInstance * aInsta otThreadDiscoveryRequestCallback aCallback, void * aContext); +/** + * This function pointer type defines the callback to notify the outcome of a `otThreadLocateAnycastDestination()` + * request. + * + * @param[in] aContext A pointer to an arbitrary context (provided when callback is registered). + * @param[in] aError The error when handling the request. OT_ERROR_NONE indicates success. + * OT_ERROR_RESPONSE_TIMEOUT indicates a destination could not be found. + * OT_ERROR_ABORT indicates the request was aborted. + * @param[in] aMeshLocalAddress A pointer to the mesh-local EID of the closest destination of the anycast address + * when @p aError is OT_ERROR_NONE, NULL otherwise. + * @param[in] aRloc16 The RLOC16 of the destination if found, otherwise invalid RLOC16 (0xfffe). + * + */ +typedef void (*otThreadAnycastLocatorCallback)(void * aContext, + otError aError, + const otIp6Address *aMeshLocalAddress, + uint16_t aRloc16); + +/** + * This function requests the closest destination of a given anycast address to be located. + * + * This function is only available when `OPENTHREAD_CONFIG_TMF_ANYCAST_LOCATOR_ENABLE` is enabled. + * + * If a previous request is ongoing, a subsequent call to this function will cancel and replace the earlier request. + * + * @param[in] aInstance A pointer to an OpenThread instance. + * @param[in] aAnycastAddress The anycast address to locate. MUST NOT be NULL. + * @param[in] aCallback The callback function to report the result. + * @param[in] aContext An arbitrary context used with @p aCallback. + * + * @retval OT_ERROR_NONE The request started successfully. @p aCallback will be invoked to report the result. + * @retval OT_ERROR_INVALID_ARGS The @p aAnycastAddress is not a valid anycast address or @p aCallback is NULL. + * @retval OT_ERROR_NO_BUFS Out of buffer to prepare and send the request message. + * + */ +otError otThreadLocateAnycastDestination(otInstance * aInstance, + const otIp6Address * aAnycastAddress, + otThreadAnycastLocatorCallback aCallback, + void * aContext); + +/** + * This function indicates whether an anycast locate request is currently in progress. + * + * This function is only available when `OPENTHREAD_CONFIG_TMF_ANYCAST_LOCATOR_ENABLE` is enabled. + * + * @param[in] aInstance A pointer to an OpenThread instance. + * + * @returns TRUE if an anycast locate request is currently in progress, FALSE otherwise. + * + */ +bool otThreadIsAnycastLocateInProgress(otInstance *aInstance); + /** * This function sends a Proactive Address Notification (ADDR_NTF.ntf) message. * diff --git a/script/check-scan-build b/script/check-scan-build index 57681f6ef..7ae01d8c6 100755 --- a/script/check-scan-build +++ b/script/check-scan-build @@ -32,6 +32,7 @@ set -euxo pipefail readonly OT_SRCDIR="$(pwd)" readonly OT_BUILD_OPTIONS=( + "-DOT_ANYCAST_LOCATOR=ON" "-DOT_BUILD_EXECUTABLES=OFF" "-DOT_BORDER_AGENT=ON" "-DOT_BORDER_ROUTER=ON" diff --git a/script/check-simulation-build-autotools b/script/check-simulation-build-autotools index 4b17fe667..5f86d22fe 100755 --- a/script/check-simulation-build-autotools +++ b/script/check-simulation-build-autotools @@ -40,6 +40,7 @@ build_all_features() { local options=( "-DOPENTHREAD_CONFIG_ANNOUNCE_SENDER_ENABLE=1" + "-DOPENTHREAD_CONFIG_ANYCAST_LOCATOR=1" "-DOPENTHREAD_CONFIG_BORDER_AGENT_ENABLE=1" "-DOPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE=1" "-DOPENTHREAD_CONFIG_CHANNEL_MANAGER_ENABLE=1" diff --git a/script/check-size b/script/check-size index 1eb1bda44..2163d0576 100755 --- a/script/check-size +++ b/script/check-size @@ -114,6 +114,7 @@ nm_size() size_nrf52840_version() { local options=( + "-DOT_ANYCAST_LOCATOR=ON" "-DOT_BORDER_AGENT=ON" "-DOT_BORDER_ROUTER=ON" "-DOT_CHANNEL_MANAGER=ON" diff --git a/script/cmake-build b/script/cmake-build index 1f3b65d45..d3ccf0d41 100755 --- a/script/cmake-build +++ b/script/cmake-build @@ -66,6 +66,7 @@ OT_CMAKE_NINJA_TARGET=${OT_CMAKE_NINJA_TARGET:-} readonly OT_SRCDIR="$(pwd)" readonly OT_PLATFORMS=(cc2538 simulation posix) readonly OT_POSIX_SIM_COMMON_OPTIONS=( + "-DOT_ANYCAST_LOCATOR=ON" "-DOT_BORDER_AGENT=ON" "-DOT_BORDER_ROUTER=ON" "-DOT_COAP=ON" diff --git a/script/make-pretty b/script/make-pretty index ad3a7b1b2..06477d875 100755 --- a/script/make-pretty +++ b/script/make-pretty @@ -74,6 +74,7 @@ readonly OT_PYTHON_SOURCES=('*.py') readonly OT_CLANG_TIDY_FIX_DIRS=('examples' 'include' 'src' 'tests') readonly OT_CLANG_TIDY_BUILD_OPTS=( '-DCMAKE_EXPORT_COMPILE_COMMANDS=ON' + '-DOT_ANYCAST_LOCATOR=ON' '-DOT_APP_RCP=OFF' '-DOT_MTD=OFF' '-DOT_RCP=OFF' diff --git a/script/test b/script/test index c076b31e4..bae466ae4 100755 --- a/script/test +++ b/script/test @@ -56,6 +56,7 @@ build_simulation() local version="$1" local options=( "-DBUILD_TESTING=ON" + "-DOT_ANYCAST_LOCATOR=ON" "-DOT_DNS_CLIENT=ON" "-DOT_DNSSD_SERVER=ON" "-DOT_ECDSA=ON" @@ -268,6 +269,7 @@ do_build_otbr_docker() local otdir local otbrdir local otbr_options=( + "-DOT_ANYCAST_LOCATOR=ON" "-DOT_COVERAGE=ON" "-DOT_DNS_CLIENT=ON" "-DOT_DUA=ON" diff --git a/src/cli/README.md b/src/cli/README.md index 5661411c3..707cf74ec 100644 --- a/src/cli/README.md +++ b/src/cli/README.md @@ -63,6 +63,7 @@ Done - [leaderweight](#leaderweight) - [linkmetrics](#linkmetrics-mgmt-ipaddr-enhanced-ack-clear) - [linkquality](#linkquality-extaddr) +- [locate](#locate) - [log](#log-filename-filename) - [mac](#mac-retries-direct) - [macfilter](#macfilter) @@ -1614,6 +1615,60 @@ Set the link quality on the link to a given extended address. Done ``` +### locate + +Gets the current state (`In Progress` or `Idle`) of anycast locator. + +`OPENTHREAD_CONFIG_TMF_ANYCAST_LOCATOR_ENABLE` is required. + +```bash +> locate +Idle +Done + +> locate fdde:ad00:beef:0:0:ff:fe00:fc10 + +> locate +In Progress +Done +``` + +### locate \ + +Locate the closest destination of an anycast address (i.e., find the destination's mesh local EID and RLOC16). + +`OPENTHREAD_CONFIG_TMF_ANYCAST_LOCATOR_ENABLE` is required. + +The closest destination is determined based on the the current routing table and path costs within the Thread mesh. + +Locate the leader using its anycast address: + +```bash +> locate fdde:ad00:beef:0:0:ff:fe00:fc00 +fdde:ad00:beef:0:d9d3:9000:16b:d03b 0xc800 +Done +``` + +Locate the closest destination of a service anycast address: + +```bash + +> srp server enable +Done + +> netdata show +Prefixes: +Routes: +Services: +44970 5d c002 s c800 +44970 5d c002 s cc00 +Done + +> locate fdde:ad00:beef:0:0:ff:fe00:fc10 +fdde:ad00:beef:0:a477:dc98:a4e4:71ea 0xcc00 +done +``` + ### log filename \ - Note: Simulation Only, ie: `OPENTHREAD_EXAMPLES_SIMULATION` diff --git a/src/cli/cli.cpp b/src/cli/cli.cpp index 3a545ad76..6efff36da 100644 --- a/src/cli/cli.cpp +++ b/src/cli/cli.cpp @@ -140,6 +140,9 @@ Interpreter::Interpreter(Instance *aInstance, otCliOutputCallback aCallback, voi , mOutputLength(0) , mIsLogging(false) #endif +#if OPENTHREAD_CONFIG_TMF_ANYCAST_LOCATOR_ENABLE + , mLocateInProgress(false) +#endif { #if OPENTHREAD_FTD otThreadSetDiscoveryRequestCallback(mInstance, &Interpreter::HandleDiscoveryRequest, this); @@ -2554,6 +2557,57 @@ exit: } #endif // OPENTHREAD_CONFIG_MLE_LINK_METRICS_INITIATOR_ENABLE +#if OPENTHREAD_CONFIG_TMF_ANYCAST_LOCATOR_ENABLE + +otError Interpreter::ProcessLocate(Arg aArgs[]) +{ + otError error = OT_ERROR_INVALID_ARGS; + otIp6Address anycastAddress; + + if (aArgs[0].IsEmpty()) + { + OutputLine(otThreadIsAnycastLocateInProgress(mInstance) ? "In Progress" : "Idle"); + ExitNow(error = OT_ERROR_NONE); + } + + SuccessOrExit(error = aArgs[0].ParseAsIp6Address(anycastAddress)); + SuccessOrExit(error = otThreadLocateAnycastDestination(mInstance, &anycastAddress, HandleLocateResult, this)); + SetCommandTimeout(kLocateTimeoutMsecs); + mLocateInProgress = true; + error = OT_ERROR_PENDING; + +exit: + return error; +} + +void Interpreter::HandleLocateResult(void * aContext, + otError aError, + const otIp6Address *aMeshLocalAddress, + uint16_t aRloc16) +{ + static_cast(aContext)->HandleLocateResult(aError, aMeshLocalAddress, aRloc16); +} + +void Interpreter::HandleLocateResult(otError aError, const otIp6Address *aMeshLocalAddress, uint16_t aRloc16) +{ + VerifyOrExit(mLocateInProgress); + + mLocateInProgress = false; + + if (aError == OT_ERROR_NONE) + { + OutputIp6Address(*aMeshLocalAddress); + OutputLine(" 0x%04x", aRloc16); + } + + OutputResult(aError); + +exit: + return; +} + +#endif // OPENTHREAD_CONFIG_TMF_ANYCAST_LOCATOR_ENABLE + #if OPENTHREAD_FTD otError Interpreter::ProcessPskc(Arg aArgs[]) { @@ -5191,7 +5245,17 @@ void Interpreter::HandleTimer(Timer &aTimer) void Interpreter::HandleTimer(void) { - OutputResult(kErrorNone); +#if OPENTHREAD_CONFIG_TMF_ANYCAST_LOCATOR_ENABLE + if (mLocateInProgress) + { + mLocateInProgress = false; + OutputResult(OT_ERROR_RESPONSE_TIMEOUT); + } + else +#endif + { + OutputResult(OT_ERROR_NONE); + } } void Interpreter::SetCommandTimeout(uint32_t aTimeoutMilli) diff --git a/src/cli/cli.hpp b/src/cli/cli.hpp index c8000bc04..3ad0ca585 100644 --- a/src/cli/cli.hpp +++ b/src/cli/cli.hpp @@ -337,6 +337,7 @@ private: }; static constexpr uint32_t kNetworkDiagnosticTimeoutMsecs = 5000; + static constexpr uint32_t kLocateTimeoutMsecs = 2500; struct Command { @@ -555,6 +556,14 @@ private: otError ProcessLinkMetricsProbe(Arg aArgs[]); otError ParseLinkMetricsFlags(otLinkMetrics &aLinkMetrics, const Arg &aFlags); #endif +#if OPENTHREAD_CONFIG_TMF_ANYCAST_LOCATOR_ENABLE + otError ProcessLocate(Arg aArgs[]); + static void HandleLocateResult(void * aContext, + otError aError, + const otIp6Address *aMeshLocalAddress, + uint16_t aRloc16); + void HandleLocateResult(otError aError, const otIp6Address *aMeshLocalAddress, uint16_t aRloc16); +#endif #if OPENTHREAD_FTD && OPENTHREAD_CONFIG_TMF_PROXY_MLR_ENABLE && OPENTHREAD_CONFIG_COMMISSIONER_ENABLE otError ProcessMlr(Arg aArgs[]); @@ -867,6 +876,9 @@ private: #endif #if OPENTHREAD_CONFIG_MLE_LINK_METRICS_INITIATOR_ENABLE {"linkmetrics", &Interpreter::ProcessLinkMetrics}, +#endif +#if OPENTHREAD_CONFIG_TMF_ANYCAST_LOCATOR_ENABLE + {"locate", &Interpreter::ProcessLocate}, #endif {"log", &Interpreter::ProcessLog}, {"mac", &Interpreter::ProcessMac}, @@ -1025,7 +1037,10 @@ private: bool mIsLogging; #endif #if OPENTHREAD_CONFIG_PING_SENDER_ENABLE - bool mPingIsAsync; + bool mPingIsAsync : 1; +#endif +#if OPENTHREAD_CONFIG_TMF_ANYCAST_LOCATOR_ENABLE + bool mLocateInProgress : 1; #endif }; diff --git a/src/core/BUILD.gn b/src/core/BUILD.gn index 222d99701..d7f95e74e 100644 --- a/src/core/BUILD.gn +++ b/src/core/BUILD.gn @@ -62,6 +62,10 @@ if (openthread_enable_core_config_args) { defines += [ "OPENTHREAD_CONFIG_ASSERT_ENABLE=0" ] } + if (openthread_config_anycast_locator_enable) { + defines += [ "OPENTHREAD_CONFIG_ANYCAST_LOCATOR_ENABLE=1" ] + } + if (openthread_config_backbone_router_enable) { defines += [ "OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE=1" ] } @@ -558,6 +562,8 @@ openthread_core_files = [ "thread/announce_begin_server.hpp", "thread/announce_sender.cpp", "thread/announce_sender.hpp", + "thread/anycast_locator.cpp", + "thread/anycast_locator.hpp", "thread/child_mask.hpp", "thread/child_table.cpp", "thread/child_table.hpp", diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index e18408560..6bdca8fea 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -177,6 +177,7 @@ set(COMMON_SOURCES thread/address_resolver.cpp thread/announce_begin_server.cpp thread/announce_sender.cpp + thread/anycast_locator.cpp thread/child_table.cpp thread/csl_tx_scheduler.cpp thread/discover_scanner.cpp diff --git a/src/core/Makefile.am b/src/core/Makefile.am index 0ab57f578..245845ee8 100644 --- a/src/core/Makefile.am +++ b/src/core/Makefile.am @@ -254,6 +254,7 @@ SOURCES_COMMON = \ thread/address_resolver.cpp \ thread/announce_begin_server.cpp \ thread/announce_sender.cpp \ + thread/anycast_locator.cpp \ thread/child_table.cpp \ thread/csl_tx_scheduler.cpp \ thread/discover_scanner.cpp \ @@ -529,6 +530,7 @@ HEADERS_COMMON = \ thread/address_resolver.hpp \ thread/announce_begin_server.hpp \ thread/announce_sender.hpp \ + thread/anycast_locator.hpp \ thread/child_mask.hpp \ thread/child_table.hpp \ thread/csl_tx_scheduler.hpp \ diff --git a/src/core/api/thread_api.cpp b/src/core/api/thread_api.cpp index 0d4f71b0a..9ebafa9d7 100644 --- a/src/core/api/thread_api.cpp +++ b/src/core/api/thread_api.cpp @@ -486,4 +486,19 @@ void otThreadRegisterParentResponseCallback(otInstance * aInst AsCoreType(aInstance).Get().RegisterParentResponseStatsCallback(aCallback, aContext); } +#if OPENTHREAD_CONFIG_TMF_ANYCAST_LOCATOR_ENABLE +otError otThreadLocateAnycastDestination(otInstance * aInstance, + const otIp6Address * aAnycastAddress, + otThreadAnycastLocatorCallback aCallback, + void * aContext) +{ + return AsCoreType(aInstance).Get().Locate(AsCoreType(aAnycastAddress), aCallback, aContext); +} + +bool otThreadIsAnycastLocateInProgress(otInstance *aInstance) +{ + return AsCoreType(aInstance).Get().IsInProgress(); +} +#endif + #endif // OPENTHREAD_FTD || OPENTHREAD_MTD diff --git a/src/core/common/instance.hpp b/src/core/common/instance.hpp index 5ead9914e..7b6456a62 100644 --- a/src/core/common/instance.hpp +++ b/src/core/common/instance.hpp @@ -639,6 +639,13 @@ template <> inline PanIdQueryServer &Instance::Get(void) return mThreadNetif.mPanIdQuery; } +#if OPENTHREAD_CONFIG_TMF_ANYCAST_LOCATOR_ENABLE +template <> inline AnycastLocator &Instance::Get(void) +{ + return mThreadNetif.mAnycastLocator; +} +#endif + #if OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE || OPENTHREAD_CONFIG_TMF_NETDATA_SERVICE_ENABLE template <> inline NetworkData::Local &Instance::Get(void) { diff --git a/src/core/config/tmf.h b/src/core/config/tmf.h index 2bff1426c..cf3c06216 100644 --- a/src/core/config/tmf.h +++ b/src/core/config/tmf.h @@ -172,6 +172,31 @@ #define OPENTHREAD_CONFIG_TMF_NETWORK_DIAG_MTD_ENABLE 0 #endif +/** + * @def OPENTHREAD_CONFIG_TMF_ANYCAST_LOCATOR_ENABLE + * + * Define to 1 to enable TMF anycast locator functionality. + * + * This feature allows a device to determine the mesh local EID and RLOC16 of the closest destination of an anycast + * address (if any) through sending `TMF_ANYCAST_LOCATE` requests. + * + */ +#ifndef OPENTHREAD_CONFIG_TMF_ANYCAST_LOCATOR_ENABLE +#define OPENTHREAD_CONFIG_TMF_ANYCAST_LOCATOR_ENABLE 0 +#endif + +/** + * @def OPENTHREAD_CONFIG_TMF_ANYCAST_LOCATOR_SEND_RESPONSE + * + * Define to 1 to require the device to listen and respond to `TMF_ANYCAST_LOCATE` requests. + * + * This config is used only when `OPENTHREAD_CONFIG_TMF_ANYCAST_LOCATOR_ENABLE` is enabled. It is enabled by default. + * + */ +#ifndef OPENTHREAD_CONFIG_TMF_ANYCAST_LOCATOR_SEND_RESPONSE +#define OPENTHREAD_CONFIG_TMF_ANYCAST_LOCATOR_SEND_RESPONSE 1 +#endif + /** * @def OPENTHREAD_CONFIG_TMF_PROXY_DUA_ENABLE * diff --git a/src/core/thread/anycast_locator.cpp b/src/core/thread/anycast_locator.cpp new file mode 100644 index 000000000..342c9c19c --- /dev/null +++ b/src/core/thread/anycast_locator.cpp @@ -0,0 +1,168 @@ +/* + * Copyright (c) 2021, 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 Anycast Locator functionality. + */ + +#include "anycast_locator.hpp" + +#if OPENTHREAD_CONFIG_TMF_ANYCAST_LOCATOR_ENABLE + +#include "common/code_utils.hpp" +#include "common/instance.hpp" +#include "common/locator_getters.hpp" +#include "thread/thread_tlvs.hpp" +#include "thread/uri_paths.hpp" + +namespace ot { + +AnycastLocator::AnycastLocator(Instance &aInstance) + : InstanceLocator(aInstance) + , mCallback(nullptr) + , mContext(nullptr) +#if OPENTHREAD_CONFIG_TMF_ANYCAST_LOCATOR_SEND_RESPONSE + , mAnycastLocate(UriPath::kAnycastLocate, HandleAnycastLocate, this) +#endif + +{ +#if OPENTHREAD_CONFIG_TMF_ANYCAST_LOCATOR_SEND_RESPONSE + Get().AddResource(mAnycastLocate); +#endif +} + +Error AnycastLocator::Locate(const Ip6::Address &aAnycastAddress, Callback aCallback, void *aContext) +{ + Error error = kErrorNone; + Coap::Message * message = nullptr; + Ip6::MessageInfo messageInfo; + + VerifyOrExit((aCallback != nullptr) && Get().IsAnycastLocator(aAnycastAddress), + error = kErrorInvalidArgs); + + message = Get().NewMessage(); + VerifyOrExit(message != nullptr, error = kErrorNoBufs); + + SuccessOrExit(error = message->InitAsConfirmablePost(UriPath::kAnycastLocate)); + SuccessOrExit(error = message->SetPayloadMarker()); + + if (mCallback != nullptr) + { + IgnoreError(Get().AbortTransaction(HandleResponse, this)); + } + + messageInfo.SetSockAddr(Get().GetMeshLocal16()); + messageInfo.SetPeerAddr(aAnycastAddress); + messageInfo.SetPeerPort(Tmf::kUdpPort); + + SuccessOrExit(error = Get().SendMessage(*message, messageInfo, HandleResponse, this)); + + mCallback = aCallback; + mContext = aContext; + +exit: + FreeMessageOnError(message, error); + return error; +} + +void AnycastLocator::HandleResponse(void * aContext, + otMessage * aMessage, + const otMessageInfo *aMessageInfo, + Error aError) +{ + static_cast(aContext)->HandleResponse( + static_cast(aMessage), static_cast(aMessageInfo), aError); +} + +void AnycastLocator::HandleResponse(Coap::Message *aMessage, const Ip6::MessageInfo *aMessageInfo, Error aError) +{ + OT_UNUSED_VARIABLE(aMessageInfo); + + uint16_t rloc16 = Mac::kShortAddrInvalid; + const Ip6::Address *address = nullptr; + Ip6::Address meshLocalAddress; + + SuccessOrExit(aError); + OT_ASSERT(aMessage != nullptr); + + meshLocalAddress.SetPrefix(Get().GetMeshLocalPrefix()); + SuccessOrExit(Tlv::Find(*aMessage, meshLocalAddress.GetIid())); + SuccessOrExit(Tlv::Find(*aMessage, rloc16)); + +#if OPENTHREAD_FTD + Get().UpdateSnoopedCacheEntry(meshLocalAddress, rloc16, Get().GetShortAddress()); +#endif + + address = &meshLocalAddress; + +exit: + if (mCallback != nullptr) + { + Callback callback = mCallback; + + mCallback = nullptr; + callback(mContext, aError, address, rloc16); + } +} + +#if OPENTHREAD_CONFIG_TMF_ANYCAST_LOCATOR_SEND_RESPONSE + +void AnycastLocator::HandleAnycastLocate(void *aContext, otMessage *aMessage, const otMessageInfo *aMessageInfo) +{ + static_cast(aContext)->HandleAnycastLocate(*static_cast(aMessage), + *static_cast(aMessageInfo)); +} + +void AnycastLocator::HandleAnycastLocate(const Coap::Message &aRequest, const Ip6::MessageInfo &aMessageInfo) +{ + Coap::Message *message = nullptr; + + VerifyOrExit(aRequest.IsConfirmablePostRequest()); + + message = Get().NewMessage(); + VerifyOrExit(message != nullptr); + + SuccessOrExit(message->SetDefaultResponseHeader(aRequest)); + SuccessOrExit(message->SetPayloadMarker()); + + SuccessOrExit(Tlv::Append(*message, Get().GetMeshLocal64().GetIid())); + SuccessOrExit(Tlv::Append(*message, Get().GetRloc16())); + + SuccessOrExit(Get().SendMessage(*message, aMessageInfo)); + message = nullptr; + +exit: + FreeMessage(message); +} + +#endif // OPENTHREAD_CONFIG_TMF_ANYCAST_LOCATOR_SEND_RESPONSE + +} // namespace ot + +#endif // OPENTHREAD_CONFIG_TMF_ANYCAST_LOCATOR_ENABLE diff --git a/src/core/thread/anycast_locator.hpp b/src/core/thread/anycast_locator.hpp new file mode 100644 index 000000000..5ae937f45 --- /dev/null +++ b/src/core/thread/anycast_locator.hpp @@ -0,0 +1,118 @@ +/* + * Copyright (c) 2021, 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 Anycast Locator functionality. + */ + +#ifndef ANYCAST_LOCATOR_HPP_ +#define ANYCAST_LOCATOR_HPP_ + +#include "openthread-core-config.h" + +#if OPENTHREAD_CONFIG_TMF_ANYCAST_LOCATOR_ENABLE + +#include "coap/coap.hpp" +#include "common/locator.hpp" +#include "common/non_copyable.hpp" +#include "net/ip6_address.hpp" + +namespace ot { + +/** + * This class implements Anycast Locator functionality which allows caller to determine the mesh local EID and RLOC16 + * of the closest destination of an anycast address (if any). + * + * The closest destination is determined based on the current routing table and path costs within the Thread mesh. + * + */ +class AnycastLocator : public InstanceLocator, private NonCopyable +{ +public: + /** + * This function pointer type defines the callback to notify the outcome of a request. + * + */ + typedef otThreadAnycastLocatorCallback Callback; + + /** + * This constructor initializes the `AnycastLocator` object. + * + * @param[in] aInstance A reference to the OpenThread instance. + * + */ + explicit AnycastLocator(Instance &aInstance); + + /** + * This method requests the closest destination of a given anycast address to be located. + * + * If a previous `Locate()` request is ongoing, a subsequent call to this method will cancel and replace the + * earlier request. + * + * @param[in] aAnycastAddress The anycast address to locate the closest destination of. + * @param[in] aCallback The callback handler to report the result. + * @param[in] aContext An arbitrary context used with @p aCallback. + * + * @retval kErrorNone The request started successfully. @p aCallback will be invoked to report the result. + * @retval kErrorNoBufs Out of buffers to prepare and send the request message. + * @retval kErrorInvalidArgs The @p aAnycastAddress is not a valid anycast address or @p aCallback is `nullptr`. + * + */ + Error Locate(const Ip6::Address &aAnycastAddress, Callback aCallback, void *aContext); + + /** + * This method indicates whether an earlier request is in progress. + * + * @returns TRUE if an earlier request is in progress, FALSE otherwise. + * + */ + bool IsInProgress(void) const { return (mCallback != nullptr); } + +private: + static void HandleResponse(void *aContext, otMessage *aMessage, const otMessageInfo *aMessageInfo, Error aError); + + void HandleResponse(Coap::Message *aMessage, const Ip6::MessageInfo *aMessageInfo, Error aError); + +#if OPENTHREAD_CONFIG_TMF_ANYCAST_LOCATOR_SEND_RESPONSE + static void HandleAnycastLocate(void *aContext, otMessage *aMessage, const otMessageInfo *aMessageInfo); + void HandleAnycastLocate(const Coap::Message &aRequest, const Ip6::MessageInfo &aMessageInfo); +#endif + + Callback mCallback; + void * mContext; +#if OPENTHREAD_CONFIG_TMF_ANYCAST_LOCATOR_SEND_RESPONSE + Coap::Resource mAnycastLocate; +#endif +}; + +} // namespace ot + +#endif // OPENTHREAD_CONFIG_TMF_ANYCAST_LOCATOR_ENABLE + +#endif // ANYCAST_LOCATOR_HPP_ diff --git a/src/core/thread/thread_netif.cpp b/src/core/thread/thread_netif.cpp index 8d4768aa8..b698e34c2 100644 --- a/src/core/thread/thread_netif.cpp +++ b/src/core/thread/thread_netif.cpp @@ -151,6 +151,9 @@ ThreadNetif::ThreadNetif(Instance &aInstance) , mAnnounceBegin(aInstance) , mPanIdQuery(aInstance) , mEnergyScan(aInstance) +#if OPENTHREAD_CONFIG_TMF_ANYCAST_LOCATOR_ENABLE + , mAnycastLocator(aInstance) +#endif #if OPENTHREAD_CONFIG_TIME_SYNC_ENABLE , mTimeSync(aInstance) #endif diff --git a/src/core/thread/thread_netif.hpp b/src/core/thread/thread_netif.hpp index df6ae6fe0..15e9d7684 100644 --- a/src/core/thread/thread_netif.hpp +++ b/src/core/thread/thread_netif.hpp @@ -88,6 +88,7 @@ #include "net/srp_client.hpp" #include "thread/address_resolver.hpp" #include "thread/announce_begin_server.hpp" +#include "thread/anycast_locator.hpp" #include "thread/discover_scanner.hpp" #include "thread/energy_scan_server.hpp" #include "thread/key_manager.hpp" @@ -301,6 +302,9 @@ private: AnnounceBeginServer mAnnounceBegin; PanIdQueryServer mPanIdQuery; EnergyScanServer mEnergyScan; +#if OPENTHREAD_CONFIG_TMF_ANYCAST_LOCATOR_ENABLE + AnycastLocator mAnycastLocator; +#endif #if OPENTHREAD_CONFIG_TIME_SYNC_ENABLE TimeSync mTimeSync; diff --git a/src/core/thread/tmf.hpp b/src/core/thread/tmf.hpp index dd5d4998a..738f665e7 100644 --- a/src/core/thread/tmf.hpp +++ b/src/core/thread/tmf.hpp @@ -34,6 +34,8 @@ #ifndef OT_CORE_THREAD_TMF_HPP_ #define OT_CORE_THREAD_TMF_HPP_ +#include "openthread-core-config.h" + #include "coap/coap.hpp" namespace ot { diff --git a/src/core/thread/uri_paths.cpp b/src/core/thread/uri_paths.cpp index f5a794da5..9f72a6aef 100644 --- a/src/core/thread/uri_paths.cpp +++ b/src/core/thread/uri_paths.cpp @@ -40,6 +40,7 @@ const char UriPath::kAddressNotify[] = "a/an"; const char UriPath::kAddressError[] = "a/ae"; const char UriPath::kAddressRelease[] = "a/ar"; const char UriPath::kAddressSolicit[] = "a/as"; +const char UriPath::kAnycastLocate[] = "a/yl"; const char UriPath::kActiveGet[] = "c/ag"; const char UriPath::kActiveSet[] = "c/as"; const char UriPath::kDatasetChanged[] = "c/dc"; diff --git a/src/core/thread/uri_paths.hpp b/src/core/thread/uri_paths.hpp index ef5bb193c..30c7999e2 100644 --- a/src/core/thread/uri_paths.hpp +++ b/src/core/thread/uri_paths.hpp @@ -50,6 +50,7 @@ struct UriPath static const char kAddressError[]; ///< The URI Path for Address Error ("a/ae"). static const char kAddressRelease[]; ///< The URI Path for Address Release ("a/ar"). static const char kAddressSolicit[]; ///< The URI Path for Address Solicit ("a/as"). + static const char kAnycastLocate[]; ///< The URI Path for Anycast Locate ("a/yl") static const char kActiveGet[]; ///< The URI Path for MGMT_ACTIVE_GE ("c/ag")T static const char kActiveSet[]; ///< The URI Path for MGMT_ACTIVE_SET ("c/as"). static const char kDatasetChanged[]; ///< The URI Path for MGMT_DATASET_CHANGED ("c/dc"). diff --git a/src/posix/Makefile-posix b/src/posix/Makefile-posix index 9740197eb..24c7a83fd 100644 --- a/src/posix/Makefile-posix +++ b/src/posix/Makefile-posix @@ -35,6 +35,7 @@ DEBUG ?= 0 # Enable most features by default to cover most code +ANYCAST_LOCATOR ?= 1 BORDER_AGENT ?= 1 BORDER_ROUTER ?= 1 COAP ?= 1 diff --git a/tests/scripts/thread-cert/Makefile.am b/tests/scripts/thread-cert/Makefile.am index 07b3908ac..43356a1eb 100644 --- a/tests/scripts/thread-cert/Makefile.am +++ b/tests/scripts/thread-cert/Makefile.am @@ -152,6 +152,7 @@ EXTRA_DIST = \ sniffer.py \ sniffer_transport.py \ test_anycast.py \ + test_anycast_locator.py \ test_coap.py \ test_coap_block.py \ test_coap_observe.py \ @@ -222,6 +223,7 @@ check_PROGRAMS = \ check_SCRIPTS = \ test_anycast.py \ + test_anycast_locator.py \ test_coap.py \ test_coap_block.py \ test_coap_observe.py \ diff --git a/tests/scripts/thread-cert/node.py b/tests/scripts/thread-cert/node.py index 03c0b4d26..4c5811c4c 100755 --- a/tests/scripts/thread-cert/node.py +++ b/tests/scripts/thread-cert/node.py @@ -1082,6 +1082,22 @@ class NodeImpl: values = [key_value[1].strip('"') for key_value in key_values] return dict(zip(keys, values)) + def locate(self, anycast_addr): + cmd = 'locate ' + anycast_addr + self.send_command(cmd) + self.simulator.go(5) + return self._parse_locate_result(self._expect_command_output(cmd)[0]) + + def _parse_locate_result(self, line: str): + """Parse anycast locate result as list of ml-eid and rloc16. + + Example output for input + 'fd00:db8:0:0:acf9:9d0:7f3c:b06e 0xa800' + + [ 'fd00:db8:0:0:acf9:9d0:7f3c:b06e', '0xa800' ] + """ + return line.split(' ') + def enable_backbone_router(self): cmd = 'bbr enable' self.send_command(cmd) diff --git a/tests/scripts/thread-cert/test_anycast_locator.py b/tests/scripts/thread-cert/test_anycast_locator.py new file mode 100755 index 000000000..c45da666b --- /dev/null +++ b/tests/scripts/thread-cert/test_anycast_locator.py @@ -0,0 +1,166 @@ +#!/usr/bin/env python3 +# +# Copyright (c) 2021, 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 config +import ipaddress +import unittest + +import command +import thread_cert + +# Test description: +# This test verifies Anycast Locator functionality +# +# Topology: +# +# LEADER -- ROUTER1 -- ROUTER2 -- ROUTER3 -- ROUTER4 +# + +LEADER = 1 +ROUTER1 = 2 +ROUTER2 = 3 +ROUTER3 = 4 +ROUTER4 = 5 + +SRV_ENT_NUMBER = '44970' +SRV_SERVICE_DATA = '571234' +SRV_SERVER_DATA = '00' + + +class AnycastLocator(thread_cert.TestCase): + USE_MESSAGE_FACTORY = False + SUPPORT_NCP = False + + TOPOLOGY = { + LEADER: { + 'name': 'LEADER', + 'mode': 'rdn', + 'allowlist': [ROUTER1] + }, + ROUTER1: { + 'name': 'ROUTER1', + 'mode': 'rdn', + 'allowlist': [LEADER, ROUTER2] + }, + ROUTER2: { + 'name': 'ROUTER2', + 'mode': 'rdn', + 'allowlist': [ROUTER1, ROUTER3] + }, + ROUTER3: { + 'name': 'ROUTER3', + 'mode': 'rdn', + 'allowlist': [ROUTER2, ROUTER4] + }, + ROUTER4: { + 'name': 'ROUTER4', + 'mode': 'rdn', + 'allowlist': [ROUTER3] + }, + } + + def test(self): + leader = self.nodes[LEADER] + router1 = self.nodes[ROUTER1] + router2 = self.nodes[ROUTER2] + router3 = self.nodes[ROUTER3] + router4 = self.nodes[ROUTER4] + nodes = [leader, router1, router2, router3, router4] + + # + # 0. Start the leader and routers + # + + leader.start() + self.simulator.go(5) + self.assertEqual(leader.get_state(), 'leader') + + for node in nodes[1:]: + node.start() + self.simulator.go(5) + self.assertEqual(node.get_state(), 'router') + + # + # 1. Locate leader ALOC from all nodes + # + + leader_aloc = leader.get_addr_leader_aloc() + + for node in nodes: + result = node.locate(leader_aloc) + self.assertEqual(result[0], leader.get_mleid()) + self.assertEqual(int(result[1], 0), leader.get_addr16()) + + # + # 2. Add a service on router4 and locate its ALOC + # + + router4.add_service(SRV_ENT_NUMBER, SRV_SERVICE_DATA, SRV_SERVER_DATA) + router4.register_netdata() + self.simulator.go(5) + services = leader.get_services() + self.assertEqual(len(services), 1) + + service_aloc = router4.get_ip6_address(config.ADDRESS_TYPE.ALOC)[0] + + for node in nodes: + result = node.locate(service_aloc) + self.assertEqual(result[0], router4.get_mleid()) + self.assertEqual(int(result[1], 0), router4.get_addr16()) + + # + # 3. Add same service on leader and ensure we locate the closest ALOC destination + # + + leader.add_service(SRV_ENT_NUMBER, SRV_SERVICE_DATA, SRV_SERVER_DATA) + leader.register_netdata() + self.simulator.go(5) + services = leader.get_services() + self.assertEqual(len(services), 2) + + # leader and router1 should locate leader as closest service ALOC, + # router3 and router4 should locate router4. router2 is in middle + # and can locate either one. + for node in [leader, router1]: + result = node.locate(service_aloc) + self.assertEqual(result[0], leader.get_mleid()) + self.assertEqual(int(result[1], 0), leader.get_addr16()) + + for node in [router3, router4]: + result = node.locate(service_aloc) + self.assertEqual(result[0], router4.get_mleid()) + self.assertEqual(int(result[1], 0), router4.get_addr16()) + + result = router2.locate(service_aloc) + self.assertTrue(result[0] in [leader.get_mleid(), router4.get_mleid()]) + self.assertTrue(int(result[1], 0) in [leader.get_addr16(), router4.get_addr16()]) + + +if __name__ == '__main__': + unittest.main() diff --git a/tests/toranj/openthread-core-toranj-config.h b/tests/toranj/openthread-core-toranj-config.h index daf58df6c..d0b718aca 100644 --- a/tests/toranj/openthread-core-toranj-config.h +++ b/tests/toranj/openthread-core-toranj-config.h @@ -119,6 +119,14 @@ */ #define OPENTHREAD_CONFIG_NETDATA_PUBLISHER_ENABLE 1 +/** + * @def OPENTHREAD_CONFIG_TMF_ANYCAST_LOCATOR_ENABLE + * + * Define to 1 to enable TMF anycast locator functionality. + * + */ +#define OPENTHREAD_CONFIG_TMF_ANYCAST_LOCATOR_ENABLE 1 + /** * @def OPENTHREAD_CONFIG_LEGACY_ENABLE *