[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 <anycast-addr>` command).

Finally this commit adds `test_anycast_locator.py` to test behavior of
the new feature.
This commit is contained in:
Abtin Keshavarzian
2021-09-17 08:04:44 -07:00
committed by GitHub
parent 961fd9ae95
commit 95fa6220d7
35 changed files with 757 additions and 3 deletions
+1
View File
@@ -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 \
+5
View File
@@ -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")
+3
View File
@@ -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
+1
View File
@@ -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
+1
View File
@@ -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. |
+4
View File
@@ -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
+1 -1
View File
@@ -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
+52
View File
@@ -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.
*
+1
View File
@@ -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"
+1
View File
@@ -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"
+1
View File
@@ -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"
+1
View File
@@ -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"
+1
View File
@@ -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'
+2
View File
@@ -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"
+55
View File
@@ -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 \<anycastaddr\>
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 \<filename\>
- Note: Simulation Only, ie: `OPENTHREAD_EXAMPLES_SIMULATION`
+65 -1
View File
@@ -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<Interpreter *>(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)
+16 -1
View File
@@ -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
};
+6
View File
@@ -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",
+1
View File
@@ -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
+2
View File
@@ -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 \
+15
View File
@@ -486,4 +486,19 @@ void otThreadRegisterParentResponseCallback(otInstance * aInst
AsCoreType(aInstance).Get<Mle::MleRouter>().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<AnycastLocator>().Locate(AsCoreType(aAnycastAddress), aCallback, aContext);
}
bool otThreadIsAnycastLocateInProgress(otInstance *aInstance)
{
return AsCoreType(aInstance).Get<AnycastLocator>().IsInProgress();
}
#endif
#endif // OPENTHREAD_FTD || OPENTHREAD_MTD
+7
View File
@@ -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)
{
+25
View File
@@ -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
*
+168
View File
@@ -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<Tmf::Agent>().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<Mle::Mle>().IsAnycastLocator(aAnycastAddress),
error = kErrorInvalidArgs);
message = Get<Tmf::Agent>().NewMessage();
VerifyOrExit(message != nullptr, error = kErrorNoBufs);
SuccessOrExit(error = message->InitAsConfirmablePost(UriPath::kAnycastLocate));
SuccessOrExit(error = message->SetPayloadMarker());
if (mCallback != nullptr)
{
IgnoreError(Get<Tmf::Agent>().AbortTransaction(HandleResponse, this));
}
messageInfo.SetSockAddr(Get<Mle::MleRouter>().GetMeshLocal16());
messageInfo.SetPeerAddr(aAnycastAddress);
messageInfo.SetPeerPort(Tmf::kUdpPort);
SuccessOrExit(error = Get<Tmf::Agent>().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<AnycastLocator *>(aContext)->HandleResponse(
static_cast<Coap::Message *>(aMessage), static_cast<const Ip6::MessageInfo *>(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<Mle::Mle>().GetMeshLocalPrefix());
SuccessOrExit(Tlv::Find<ThreadMeshLocalEidTlv>(*aMessage, meshLocalAddress.GetIid()));
SuccessOrExit(Tlv::Find<ThreadRloc16Tlv>(*aMessage, rloc16));
#if OPENTHREAD_FTD
Get<AddressResolver>().UpdateSnoopedCacheEntry(meshLocalAddress, rloc16, Get<Mac::Mac>().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<AnycastLocator *>(aContext)->HandleAnycastLocate(*static_cast<const Coap::Message *>(aMessage),
*static_cast<const Ip6::MessageInfo *>(aMessageInfo));
}
void AnycastLocator::HandleAnycastLocate(const Coap::Message &aRequest, const Ip6::MessageInfo &aMessageInfo)
{
Coap::Message *message = nullptr;
VerifyOrExit(aRequest.IsConfirmablePostRequest());
message = Get<Tmf::Agent>().NewMessage();
VerifyOrExit(message != nullptr);
SuccessOrExit(message->SetDefaultResponseHeader(aRequest));
SuccessOrExit(message->SetPayloadMarker());
SuccessOrExit(Tlv::Append<ThreadMeshLocalEidTlv>(*message, Get<Mle::Mle>().GetMeshLocal64().GetIid()));
SuccessOrExit(Tlv::Append<ThreadRloc16Tlv>(*message, Get<Mle::Mle>().GetRloc16()));
SuccessOrExit(Get<Tmf::Agent>().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
+118
View File
@@ -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_
+3
View File
@@ -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
+4
View File
@@ -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;
+2
View File
@@ -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 {
+1
View File
@@ -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";
+1
View File
@@ -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").
+1
View File
@@ -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
+2
View File
@@ -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 \
+16
View File
@@ -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)
+166
View File
@@ -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()
@@ -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
*