mirror of
https://github.com/espressif/openthread.git
synced 2026-06-05 21:14:49 +00:00
[meshcop] add BorderAgentTracker to discover Border Agents (#11985)
Introduces a new `BorderAgentTracker` module to discover and track Border Agents on the infrastructure link. The tracker browses for the `_meshcop._udp` mDNS service and maintains a list of discovered Border Agents. For each discovered service, it resolves the port, host name, TXT record, and host addresses. This change also adds new public otBorderAgentTracker APIs, corresponding `batracker` CLI commands, and a new Nexus test case to validate the behavior.
This commit is contained in:
committed by
GitHub
parent
2596c9486b
commit
2af369e844
@@ -104,6 +104,7 @@
|
||||
*
|
||||
* @defgroup api-backbone-router Backbone Router
|
||||
* @defgroup api-border-agent Border Agent
|
||||
* @defgroup api-border-agent-tracker Border Agent Tracker
|
||||
* @defgroup api-border-router Border Router
|
||||
* @defgroup api-border-routing Border Routing Manager
|
||||
* @defgroup api-commissioner Commissioner
|
||||
|
||||
@@ -177,6 +177,7 @@ ot_option(OT_BORDER_AGENT OPENTHREAD_CONFIG_BORDER_AGENT_ENABLE "border agent")
|
||||
ot_option(OT_BORDER_AGENT_EPSKC OPENTHREAD_CONFIG_BORDER_AGENT_EPHEMERAL_KEY_ENABLE "border agent ephemeral PSKc")
|
||||
ot_option(OT_BORDER_AGENT_ID OPENTHREAD_CONFIG_BORDER_AGENT_ID_ENABLE "create and save border agent ID")
|
||||
ot_option(OT_BORDER_AGENT_MESHCOP_SERVICE OPENTHREAD_CONFIG_BORDER_AGENT_MESHCOP_SERVICE_ENABLE "border agent meshcop service")
|
||||
ot_option(OT_BORDER_AGENT_TRACKER OPENTHREAD_CONFIG_BORDER_AGENT_TRACKER_ENABLE "border agent tracker")
|
||||
ot_option(OT_BORDER_ROUTER OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE "border router")
|
||||
ot_option(OT_BORDER_ROUTING OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE "border routing")
|
||||
ot_option(OT_BORDER_ROUTING_DHCP6_PD OPENTHREAD_CONFIG_BORDER_ROUTING_DHCP6_PD_ENABLE "dhcpv6 pd support in border routing")
|
||||
|
||||
@@ -43,6 +43,7 @@ source_set("openthread") {
|
||||
"backbone_router_ftd.h",
|
||||
"ble_secure.h",
|
||||
"border_agent.h",
|
||||
"border_agent_tracker.h",
|
||||
"border_router.h",
|
||||
"border_routing.h",
|
||||
"channel_manager.h",
|
||||
|
||||
@@ -0,0 +1,162 @@
|
||||
/*
|
||||
* Copyright (c) 2025, 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
|
||||
* @brief
|
||||
* This file defines the OpenThread Border Agent Tracker APIs.
|
||||
*/
|
||||
|
||||
#ifndef OPENTHREAD_BORDER_AGENT_TRACKER_H_
|
||||
#define OPENTHREAD_BORDER_AGENT_TRACKER_H_
|
||||
|
||||
#include <openthread/error.h>
|
||||
#include <openthread/instance.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @addtogroup api-border-agent-tracker
|
||||
*
|
||||
* @brief
|
||||
* This module includes APIs for the Border Agent Tracker.
|
||||
*
|
||||
* The Border Agent Tracker discovers and tracks Border Agents on the infrastructure link by browsing for the
|
||||
* `_meshcop._udp` mDNS service.
|
||||
*
|
||||
* @{
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* Represents an iterator to iterate through the discovered Border Agents.
|
||||
*
|
||||
* The fields in this struct are for OpenThread internal use only and MUST NOT be accessed or modified by the caller.
|
||||
*
|
||||
* An iterator MUST be initialized using `otBorderAgentTrackerInitIterator()` before it is used.
|
||||
*/
|
||||
typedef struct otBorderAgentTrackerIterator
|
||||
{
|
||||
const void *mPtr;
|
||||
uint64_t mData;
|
||||
} otBorderAgentTrackerIterator;
|
||||
|
||||
/**
|
||||
* Represents information about a discovered Border Agent.
|
||||
*
|
||||
* To ensure consistent `mMsecSinceDiscovered` and `mMsecSinceLastChange` time calculations, the iterator's
|
||||
* initialization time is stored within the iterator when `otBorderAgentTrackerInitIterator()` is called. The time
|
||||
* values in this struct are calculated relative to the iterator's initialization time.
|
||||
*/
|
||||
typedef struct otBorderAgentTrackerAgentInfo
|
||||
{
|
||||
const char *mServiceName; ///< The service name.
|
||||
const char *mHostName; ///< The host name. May be NULL if not known yet.
|
||||
uint16_t mPort; ///< The port number. Can be zero if not known yet.
|
||||
const uint8_t *mTxtData; ///< The TXT data. May be NULL if not known yet.
|
||||
uint16_t mTxtDataLength; ///< The TXT data length.
|
||||
const otIp6Address *mAddresses; ///< Array of IPv6 addresses of the host. May be NULL if not known yet.
|
||||
uint16_t mNumAddresses; ///< Number of addresses in the `mAddresses` array.
|
||||
uint64_t mMsecSinceDiscovered; ///< Milliseconds since the service was discovered.
|
||||
uint64_t mMsecSinceLastChange; ///< Milliseconds since the last change (port, TXT, or addresses).
|
||||
} otBorderAgentTrackerAgentInfo;
|
||||
|
||||
/**
|
||||
* Enables or disables the Border Agent Tracker.
|
||||
*
|
||||
* Requires `OPENTHREAD_CONFIG_BORDER_AGENT_TRACKER_ENABLE`.
|
||||
*
|
||||
* When enabled, the tracker browses for the `_meshcop._udp` mDNS service to discover and track Border Agents on
|
||||
* the infra-if network.
|
||||
*
|
||||
* @param[in] aInstance A pointer to an OpenThread instance.
|
||||
* @param[in] aEnable TRUE to enable the Border Agent Tracker, FALSE to disable it.
|
||||
*/
|
||||
void otBorderAgentTrackerSetEnabled(otInstance *aInstance, bool aEnable);
|
||||
|
||||
/**
|
||||
* Indicates whether the Border Agent Tracker is running.
|
||||
*
|
||||
* Requires `OPENTHREAD_CONFIG_BORDER_AGENT_TRACKER_ENABLE`.
|
||||
*
|
||||
* The tracker can be enabled by the user (via `otBorderAgentTrackerSetEnabled()`) or by the OpenThread stack
|
||||
* itself. The tracker is considered running if it is enabled by either entity AND the underlying DNS-SD (mDNS)
|
||||
* is ready. This means that `otBorderAgentTrackerIsRunning()` may not return `TRUE` immediately after a call
|
||||
* to `otBorderAgentTrackerSetEnabled(true)`.
|
||||
*
|
||||
* @param[in] aInstance A pointer to an OpenThread instance.
|
||||
*
|
||||
* @retval TRUE If the tracker is running.
|
||||
* @retval FALSE If the tracker is not running.
|
||||
*/
|
||||
bool otBorderAgentTrackerIsRunning(otInstance *aInstance);
|
||||
|
||||
/**
|
||||
* Initializes a Border Agent Tracker iterator.
|
||||
*
|
||||
* Requires `OPENTHREAD_CONFIG_BORDER_AGENT_TRACKER_ENABLE`.
|
||||
*
|
||||
* An iterator MUST be initialized before being used.
|
||||
*
|
||||
* @param[in] aInstance A pointer to an OpenThread instance.
|
||||
* @param[in] aIterator A pointer to the iterator to initialize.
|
||||
*/
|
||||
void otBorderAgentTrackerInitIterator(otInstance *aInstance, otBorderAgentTrackerIterator *aIterator);
|
||||
|
||||
/**
|
||||
* Gets the information for the next discovered Border Agent.
|
||||
*
|
||||
* Requires `OPENTHREAD_CONFIG_BORDER_AGENT_TRACKER_ENABLE`.
|
||||
*
|
||||
* The iterator initialization time is used to determine the `mMsecSinceDiscovered` and `mMsecSinceLastChange` in the
|
||||
* `otBorderAgentTrackerAgentInfo`.
|
||||
*
|
||||
* @param[in] aInstance A pointer to an OpenThread instance.
|
||||
* @param[in,out] aIterator A pointer to the iterator. An iterator MUST be initialized using
|
||||
* `otBorderAgentTrackerInitIterator()` before it is used.
|
||||
* @param[out] aAgentInfo A pointer to an `otBorderAgentTrackerAgentInfo` struct to populate.
|
||||
*
|
||||
* @retval OT_ERROR_NONE Successfully retrieved the information for the next agent.
|
||||
* @retval OT_ERROR_NOT_FOUND No more agents were found.
|
||||
*/
|
||||
otError otBorderAgentTrackerGetNextAgent(otInstance *aInstance,
|
||||
otBorderAgentTrackerIterator *aIterator,
|
||||
otBorderAgentTrackerAgentInfo *aAgentInfo);
|
||||
|
||||
/**
|
||||
* @}
|
||||
*
|
||||
*/
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
||||
#endif // OPENTHREAD_BORDER_AGENT_TRACKER_H_
|
||||
@@ -52,7 +52,7 @@ extern "C" {
|
||||
*
|
||||
* @note This number versions both OpenThread platform and user APIs.
|
||||
*/
|
||||
#define OPENTHREAD_API_VERSION (539)
|
||||
#define OPENTHREAD_API_VERSION (540)
|
||||
|
||||
/**
|
||||
* @addtogroup api-instance
|
||||
|
||||
@@ -23,6 +23,7 @@ Done
|
||||
|
||||
- [attachtime](#attachtime)
|
||||
- [ba](#ba)
|
||||
- [batracker](#batracker-enable)
|
||||
- [bbr](#bbr)
|
||||
- [br](README_BR.md)
|
||||
- [bufferinfo](#bufferinfo)
|
||||
@@ -609,6 +610,81 @@ mgmtPendingGet: 0
|
||||
Done
|
||||
```
|
||||
|
||||
### batracker enable
|
||||
|
||||
Enables Border Agent Tracker.
|
||||
|
||||
Requires `OPENTHREAD_CONFIG_BORDER_AGENT_TRACKER_ENABLE`.
|
||||
|
||||
When enabled, the tracker browses for the `_meshcop._udp` mDNS service to discover and track Border Agents on the infra-if network.
|
||||
|
||||
```bash
|
||||
> batracker enable
|
||||
Done
|
||||
```
|
||||
|
||||
### batracker disable
|
||||
|
||||
Disables Border Agent Tracker.
|
||||
|
||||
Requires `OPENTHREAD_CONFIG_BORDER_AGENT_TRACKER_ENABLE`.
|
||||
|
||||
```
|
||||
> batracker disable
|
||||
Done
|
||||
```
|
||||
|
||||
### batracker state
|
||||
|
||||
Requires `OPENTHREAD_CONFIG_BORDER_AGENT_TRACKER_ENABLE`.
|
||||
|
||||
Shows the state of Border Agent Tracker, `running` or `inactive`.
|
||||
|
||||
The tracker can be enabled by the user (e.g., via `batracker enable`) or by the OpenThread stack itself. The tracker is considered running if it is enabled by either entity and the underlying DNS-SD (mDNS) is ready.
|
||||
|
||||
```bash
|
||||
> batracker state
|
||||
running
|
||||
Done
|
||||
```
|
||||
|
||||
### batracker agents
|
||||
|
||||
Requires `OPENTHREAD_CONFIG_BORDER_AGENT_TRACKER_ENABLE`.
|
||||
|
||||
Outputs the list of discovered Border Agents. Information per Agent:
|
||||
|
||||
- Service name
|
||||
- Port number
|
||||
- Host name
|
||||
- TXT data (key/value pairs per line)
|
||||
- Host addresses
|
||||
- Milliseconds since agent was first discovered
|
||||
- Milliseconds since the last change to agent info (port, addresses, TXT data)
|
||||
|
||||
```bash
|
||||
> batracker agents
|
||||
ServiceName: OTBR-by-Google-be345eefb12f7f9c
|
||||
Port: 49152
|
||||
Host: otbe345eefb12f7f9c
|
||||
TxtData:
|
||||
id=4b21d3f4a431725048380698f3073a4b
|
||||
rv=31
|
||||
nn=4f70656e546872656164
|
||||
xp=dead00beef00cafe
|
||||
tv=312e342e30
|
||||
xa=be345eefb12f7f9c
|
||||
sb=00000820
|
||||
dn=44656661756c74446f6d61696e
|
||||
Address(es):
|
||||
fe80:0:0:0:108f:3188:ff96:8e9f
|
||||
fd7c:af54:fada:564d:7:fd6e:744c:e300
|
||||
fd7c:af54:fada:564d:d9:899d:1217:9e2
|
||||
MilliSecondsSinceDiscovered: 5237
|
||||
MilliSecondsSinceLastChange: 5237
|
||||
Done
|
||||
```
|
||||
|
||||
### bufferinfo
|
||||
|
||||
Show the current message buffer information.
|
||||
|
||||
+137
@@ -41,6 +41,7 @@
|
||||
|
||||
#include <openthread/backbone_router.h>
|
||||
#include <openthread/backbone_router_ftd.h>
|
||||
#include <openthread/border_agent_tracker.h>
|
||||
#include <openthread/border_router.h>
|
||||
#include <openthread/channel_manager.h>
|
||||
#include <openthread/channel_monitor.h>
|
||||
@@ -807,6 +808,139 @@ void Interpreter::HandleBorderAgentEphemeralKeyStateChange(void)
|
||||
|
||||
#endif // OPENTHREAD_CONFIG_BORDER_AGENT_ENABLE
|
||||
|
||||
#if OPENTHREAD_CONFIG_BORDER_AGENT_TRACKER_ENABLE
|
||||
|
||||
template <> otError Interpreter::Process<Cmd("batracker")>(Arg aArgs[])
|
||||
{
|
||||
otError error = OT_ERROR_NONE;
|
||||
|
||||
/**
|
||||
* @cli batracker (enable, disable)
|
||||
* @code
|
||||
* batracker enable
|
||||
* Done
|
||||
* @endcode
|
||||
* @code
|
||||
* batracker disable
|
||||
* Done
|
||||
* @endcode
|
||||
* @cparam batracker @ca{enable|disable}
|
||||
* @par api_copy
|
||||
* #otBorderAgentTrackerSetEnabled
|
||||
*/
|
||||
if (ProcessEnableDisable(aArgs, otBorderAgentTrackerSetEnabled) == OT_ERROR_NONE)
|
||||
{
|
||||
}
|
||||
/**
|
||||
* @cli batracker state
|
||||
* @code
|
||||
* batracker state
|
||||
* running
|
||||
* Done
|
||||
* @endcode
|
||||
* @par
|
||||
* Shows the state of Border Agent Tracker, `running` or `inactive`.
|
||||
*
|
||||
* The tracker can be enabled by the user (e.g., via `batracker enable`) or by the OpenThread stack itself. The
|
||||
* tracker is considered running if it is enabled by either entity and the underlying DNS-SD (mDNS) is ready.
|
||||
*/
|
||||
else if (aArgs[0] == "state")
|
||||
{
|
||||
OutputLine("%s", otBorderAgentTrackerIsRunning(GetInstancePtr()) ? "running" : "inactive");
|
||||
}
|
||||
/**
|
||||
* @cli batracker agents
|
||||
* @code
|
||||
* batracker agents
|
||||
* ServiceName: OTBR-by-Google-be345eefb12f7f9c
|
||||
* Port: 49152
|
||||
* Host: otbe345eefb12f7f9c
|
||||
* TxtData:
|
||||
* id=4b21d3f4a431725048380698f3073a4b
|
||||
* rv=31
|
||||
* nn=4f70656e546872656164
|
||||
* xp=dead00beef00cafe
|
||||
* tv=312e342e30
|
||||
* xa=be345eefb12f7f9c
|
||||
* sb=00000820
|
||||
* dn=44656661756c74446f6d61696e
|
||||
* Address(es):
|
||||
* fe80:0:0:0:108f:3188:ff96:8e9f
|
||||
* fd7c:af54:fada:564d:7:fd6e:744c:e300
|
||||
* fd7c:af54:fada:564d:d9:899d:1217:9e2
|
||||
* MilliSecondsSinceDiscovered: 5237
|
||||
* MilliSecondsSinceLastChange: 5237
|
||||
* Done
|
||||
* @endcode
|
||||
* @par
|
||||
* Outputs the list of discovered border agents. Information per agent:
|
||||
* - Service name
|
||||
* - Port number
|
||||
* - Host name
|
||||
* - TXT data (key/value pairs per line)
|
||||
* - Host addresses
|
||||
* - Milliseconds since agent was first discovered
|
||||
* - Milliseconds since the last change to agent info (port, addresses, TXT data)
|
||||
*/
|
||||
else if (aArgs[0] == "agents")
|
||||
{
|
||||
otBorderAgentTrackerIterator iterator;
|
||||
otBorderAgentTrackerAgentInfo agent;
|
||||
|
||||
otBorderAgentTrackerInitIterator(GetInstancePtr(), &iterator);
|
||||
|
||||
while (otBorderAgentTrackerGetNextAgent(GetInstancePtr(), &iterator, &agent) == OT_ERROR_NONE)
|
||||
{
|
||||
OutputLine("ServiceName: %s", agent.mServiceName);
|
||||
OutputLine(kIndentSize, "Port: %u", agent.mPort);
|
||||
OutputLine(kIndentSize, "Host: %s", agent.mHostName != nullptr ? agent.mHostName : "(null)");
|
||||
|
||||
OutputFormat(kIndentSize, "TxtData:");
|
||||
|
||||
if (agent.mTxtData != nullptr)
|
||||
{
|
||||
OutputNewLine();
|
||||
OutputDnsTxtData(kIndentSize * 2, agent.mTxtData, agent.mTxtDataLength);
|
||||
}
|
||||
else
|
||||
{
|
||||
OutputLine(" (null)");
|
||||
}
|
||||
|
||||
OutputFormat(kIndentSize, "Address(es):");
|
||||
|
||||
if (agent.mAddresses != nullptr)
|
||||
{
|
||||
OutputNewLine();
|
||||
|
||||
for (uint16_t i = 0; i < agent.mNumAddresses; i++)
|
||||
{
|
||||
OutputSpaces(kIndentSize * 2);
|
||||
OutputIp6AddressLine(agent.mAddresses[i]);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
OutputLine(" (null)");
|
||||
}
|
||||
|
||||
OutputFormat(kIndentSize, "MilliSecondsSinceDiscovered: ");
|
||||
OutputUint64Line(agent.mMsecSinceDiscovered);
|
||||
|
||||
OutputFormat(kIndentSize, "MilliSecondsSinceLastChange: ");
|
||||
OutputUint64Line(agent.mMsecSinceLastChange);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
error = OT_ERROR_INVALID_ARGS;
|
||||
}
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
#endif // OPENTHREAD_CONFIG_BORDER_AGENT_TRACKER_ENABLE
|
||||
|
||||
#if OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE
|
||||
template <> otError Interpreter::Process<Cmd("br")>(Arg aArgs[]) { return mBr.Process(aArgs); }
|
||||
#endif
|
||||
@@ -8433,6 +8567,9 @@ otError Interpreter::ProcessCommand(Arg aArgs[])
|
||||
#if OPENTHREAD_CONFIG_BORDER_AGENT_ENABLE
|
||||
CmdEntry("ba"),
|
||||
#endif
|
||||
#if OPENTHREAD_CONFIG_BORDER_AGENT_TRACKER_ENABLE
|
||||
CmdEntry("batracker"),
|
||||
#endif
|
||||
#if (OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2)
|
||||
CmdEntry("bbr"),
|
||||
#endif
|
||||
|
||||
+31
-3
@@ -218,6 +218,19 @@ void Utils::OutputSockAddrLine(const otSockAddr &aSockAddr)
|
||||
}
|
||||
|
||||
void Utils::OutputDnsTxtData(const uint8_t *aTxtData, uint16_t aTxtDataLength)
|
||||
{
|
||||
OutputDnsTxtData(/* aKeyValuePerLine */ false, 0, aTxtData, aTxtDataLength);
|
||||
}
|
||||
|
||||
void Utils::OutputDnsTxtData(uint8_t aIndentSize, const uint8_t *aTxtData, uint16_t aTxtDataLength)
|
||||
{
|
||||
OutputDnsTxtData(/* aKeyValuePerLine */ true, aIndentSize, aTxtData, aTxtDataLength);
|
||||
}
|
||||
|
||||
void Utils::OutputDnsTxtData(bool aKeyValuePerLine,
|
||||
uint8_t aIndentSize,
|
||||
const uint8_t *aTxtData,
|
||||
uint16_t aTxtDataLength)
|
||||
{
|
||||
otDnsTxtEntry entry;
|
||||
otDnsTxtEntryIterator iterator;
|
||||
@@ -225,11 +238,18 @@ void Utils::OutputDnsTxtData(const uint8_t *aTxtData, uint16_t aTxtDataLength)
|
||||
|
||||
otDnsInitTxtEntryIterator(&iterator, aTxtData, aTxtDataLength);
|
||||
|
||||
OutputFormat("[");
|
||||
if (!aKeyValuePerLine)
|
||||
{
|
||||
OutputFormat("[");
|
||||
}
|
||||
|
||||
while (otDnsGetNextTxtEntry(&iterator, &entry) == OT_ERROR_NONE)
|
||||
{
|
||||
if (!isFirst)
|
||||
if (aKeyValuePerLine)
|
||||
{
|
||||
OutputSpaces(aIndentSize);
|
||||
}
|
||||
else if (!isFirst)
|
||||
{
|
||||
OutputFormat(", ");
|
||||
}
|
||||
@@ -257,9 +277,17 @@ void Utils::OutputDnsTxtData(const uint8_t *aTxtData, uint16_t aTxtDataLength)
|
||||
}
|
||||
|
||||
isFirst = false;
|
||||
|
||||
if (aKeyValuePerLine)
|
||||
{
|
||||
OutputNewLine();
|
||||
}
|
||||
}
|
||||
|
||||
OutputFormat("]");
|
||||
if (!aKeyValuePerLine)
|
||||
{
|
||||
OutputFormat("]");
|
||||
}
|
||||
}
|
||||
|
||||
const char *Utils::PercentageToString(uint16_t aValue, PercentageStringBuffer &aBuffer)
|
||||
|
||||
@@ -403,11 +403,24 @@ public:
|
||||
/**
|
||||
* Outputs DNS TXT data to the CLI console.
|
||||
*
|
||||
* All key-value pairs are output on a single line in the format: "[key1=value1, key2=value2, ...]".
|
||||
*
|
||||
* @param[in] aTxtData A pointer to a buffer containing the DNS TXT data.
|
||||
* @param[in] aTxtDataLength The length of @p aTxtData (in bytes).
|
||||
*/
|
||||
void OutputDnsTxtData(const uint8_t *aTxtData, uint16_t aTxtDataLength);
|
||||
|
||||
/**
|
||||
* Outputs DNS TXT data to the CLI console, one key per line and applying an indentation.
|
||||
*
|
||||
* Each key-value pair is output on its own line, preceded by the specified indentation.
|
||||
*
|
||||
* @param[in] aIndentSize The number of space characters to prepend as indentation to each output line.
|
||||
* @param[in] aTxtData A pointer to a buffer containing the DNS TXT data.
|
||||
* @param[in] aTxtDataLength The length of @p aTxtData (in bytes).
|
||||
*/
|
||||
void OutputDnsTxtData(uint8_t aIndentSize, const uint8_t *aTxtData, uint16_t aTxtDataLength);
|
||||
|
||||
/**
|
||||
* Represents a buffer which is used when converting an encoded rate value to percentage string.
|
||||
*/
|
||||
@@ -719,6 +732,9 @@ private:
|
||||
|
||||
void OutputTableHeader(uint8_t aNumColumns, const char *const aTitles[], const uint8_t aWidths[]);
|
||||
void OutputTableSeparator(uint8_t aNumColumns, const uint8_t aWidths[]);
|
||||
#if OPENTHREAD_FTD || OPENTHREAD_MTD
|
||||
void OutputDnsTxtData(bool aKeyValuePerLine, uint8_t aIndentSize, const uint8_t *aTxtData, uint16_t aTxtDataLength);
|
||||
#endif
|
||||
|
||||
otInstance *mInstance;
|
||||
OutputImplementer &mImplementer;
|
||||
|
||||
@@ -309,6 +309,7 @@ openthread_core_files = [
|
||||
"api/backbone_router_ftd_api.cpp",
|
||||
"api/ble_secure_api.cpp",
|
||||
"api/border_agent_api.cpp",
|
||||
"api/border_agent_tracker_api.cpp",
|
||||
"api/border_router_api.cpp",
|
||||
"api/border_routing_api.cpp",
|
||||
"api/channel_manager_api.cpp",
|
||||
@@ -523,6 +524,8 @@ openthread_core_files = [
|
||||
"meshcop/announce_begin_client.hpp",
|
||||
"meshcop/border_agent.cpp",
|
||||
"meshcop/border_agent.hpp",
|
||||
"meshcop/border_agent_tracker.cpp",
|
||||
"meshcop/border_agent_tracker.hpp",
|
||||
"meshcop/commissioner.cpp",
|
||||
"meshcop/commissioner.hpp",
|
||||
"meshcop/dataset.cpp",
|
||||
|
||||
@@ -35,6 +35,7 @@ set(COMMON_SOURCES
|
||||
api/backbone_router_ftd_api.cpp
|
||||
api/ble_secure_api.cpp
|
||||
api/border_agent_api.cpp
|
||||
api/border_agent_tracker_api.cpp
|
||||
api/border_router_api.cpp
|
||||
api/border_routing_api.cpp
|
||||
api/channel_manager_api.cpp
|
||||
@@ -156,6 +157,7 @@ set(COMMON_SOURCES
|
||||
mac/wakeup_tx_scheduler.cpp
|
||||
meshcop/announce_begin_client.cpp
|
||||
meshcop/border_agent.cpp
|
||||
meshcop/border_agent_tracker.cpp
|
||||
meshcop/commissioner.cpp
|
||||
meshcop/dataset.cpp
|
||||
meshcop/dataset_manager.cpp
|
||||
|
||||
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
* Copyright (c) 2025, 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.
|
||||
*/
|
||||
|
||||
#include "openthread-core-config.h"
|
||||
|
||||
#if OPENTHREAD_CONFIG_BORDER_AGENT_TRACKER_ENABLE
|
||||
|
||||
#include <openthread/border_agent_tracker.h>
|
||||
|
||||
#include "common/as_core_type.hpp"
|
||||
#include "instance/instance.hpp"
|
||||
|
||||
using namespace ot;
|
||||
|
||||
void otBorderAgentTrackerSetEnabled(otInstance *aInstance, bool aEnable)
|
||||
{
|
||||
AsCoreType(aInstance).Get<MeshCoP::BorderAgentTracker>().SetEnabled(aEnable,
|
||||
MeshCoP::BorderAgentTracker::kRequesterUser);
|
||||
}
|
||||
|
||||
bool otBorderAgentTrackerIsRunning(otInstance *aInstance)
|
||||
{
|
||||
return AsCoreType(aInstance).Get<MeshCoP::BorderAgentTracker>().IsRunning();
|
||||
}
|
||||
|
||||
void otBorderAgentTrackerInitIterator(otInstance *aInstance, otBorderAgentTrackerIterator *aIterator)
|
||||
{
|
||||
AsCoreType(aIterator).Init(AsCoreType(aInstance));
|
||||
}
|
||||
|
||||
otError otBorderAgentTrackerGetNextAgent(otInstance *aInstance,
|
||||
otBorderAgentTrackerIterator *aIterator,
|
||||
otBorderAgentTrackerAgentInfo *aAgentInfo)
|
||||
{
|
||||
OT_UNUSED_VARIABLE(aInstance);
|
||||
AssertPointerIsNotNull(aAgentInfo);
|
||||
|
||||
return AsCoreType(aIterator).GetNextAgentInfo(*aAgentInfo);
|
||||
}
|
||||
|
||||
#endif // OPENTHREAD_CONFIG_BORDER_AGENT_TRACKER_ENABLE
|
||||
@@ -172,6 +172,16 @@ public:
|
||||
*/
|
||||
bool operator==(const String &aString) const { return (*this == aString.AsCString()); }
|
||||
|
||||
/**
|
||||
* Overloads operator `!=` to evaluate whether or not two `String` are not equal.
|
||||
*
|
||||
* @param[in] aString The other string to compare with.
|
||||
*
|
||||
* @retval TRUE If the two strings are not equal.
|
||||
* @retval FALSE If the two strings are equal.
|
||||
*/
|
||||
bool operator!=(const String &aString) const { return (*this != aString.AsCString()); }
|
||||
|
||||
String(const String &) = delete;
|
||||
String &operator=(const String &) = delete;
|
||||
|
||||
|
||||
@@ -122,6 +122,18 @@
|
||||
#define OPENTHREAD_CONFIG_BORDER_AGENT_MESHCOP_SERVICE_BASE_NAME "OpenThread BR (unspecified vendor) "
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @def OPENTHREAD_CONFIG_BORDER_AGENT_TRACKER_ENABLE
|
||||
*
|
||||
* Define to 1 to enable the Border Agent Tracker feature.
|
||||
*
|
||||
* The Border Agent Tracker feature discovers and tracks Border Agents on the infrastructure network. This feature
|
||||
* requires either `OPENTHREAD_CONFIG_PLATFORM_DNSSD_ENABLE` or `OPENTHREAD_CONFIG_MULTICAST_DNS_ENABLE` to be enabled.
|
||||
*/
|
||||
#ifndef OPENTHREAD_CONFIG_BORDER_AGENT_TRACKER_ENABLE
|
||||
#define OPENTHREAD_CONFIG_BORDER_AGENT_TRACKER_ENABLE 0
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @}
|
||||
*/
|
||||
|
||||
@@ -174,6 +174,9 @@ Instance::Instance(void)
|
||||
#if OPENTHREAD_CONFIG_BORDER_AGENT_ENABLE
|
||||
, mBorderAgent(*this)
|
||||
#endif
|
||||
#if OPENTHREAD_CONFIG_BORDER_AGENT_TRACKER_ENABLE
|
||||
, mBorderAgentTracker(*this)
|
||||
#endif
|
||||
#if OPENTHREAD_CONFIG_COMMISSIONER_ENABLE && OPENTHREAD_FTD
|
||||
, mCommissioner(*this)
|
||||
#endif
|
||||
|
||||
@@ -84,6 +84,7 @@
|
||||
#include "mac/mac.hpp"
|
||||
#include "mac/wakeup_tx_scheduler.hpp"
|
||||
#include "meshcop/border_agent.hpp"
|
||||
#include "meshcop/border_agent_tracker.hpp"
|
||||
#include "meshcop/commissioner.hpp"
|
||||
#include "meshcop/dataset_manager.hpp"
|
||||
#include "meshcop/dataset_updater.hpp"
|
||||
@@ -589,6 +590,10 @@ private:
|
||||
MeshCoP::BorderAgent mBorderAgent;
|
||||
#endif
|
||||
|
||||
#if OPENTHREAD_CONFIG_BORDER_AGENT_TRACKER_ENABLE
|
||||
MeshCoP::BorderAgentTracker mBorderAgentTracker;
|
||||
#endif
|
||||
|
||||
#if OPENTHREAD_CONFIG_COMMISSIONER_ENABLE && OPENTHREAD_FTD
|
||||
MeshCoP::Commissioner mCommissioner;
|
||||
#endif
|
||||
@@ -1024,6 +1029,10 @@ template <> inline MeshCoP::DatasetUpdater &Instance::Get(void) { return mDatase
|
||||
template <> inline MeshCoP::BorderAgent &Instance::Get(void) { return mBorderAgent; }
|
||||
#endif
|
||||
|
||||
#if OPENTHREAD_CONFIG_BORDER_AGENT_TRACKER_ENABLE
|
||||
template <> inline MeshCoP::BorderAgentTracker &Instance::Get(void) { return mBorderAgentTracker; }
|
||||
#endif
|
||||
|
||||
#if OPENTHREAD_CONFIG_BORDER_AGENT_ENABLE && OPENTHREAD_CONFIG_BORDER_AGENT_EPHEMERAL_KEY_ENABLE
|
||||
template <> inline MeshCoP::BorderAgent::EphemeralKeyManager &Instance::Get(void)
|
||||
{
|
||||
|
||||
@@ -0,0 +1,569 @@
|
||||
/*
|
||||
* Copyright (c) 2025, 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 the Border Agent Tracker.
|
||||
*/
|
||||
|
||||
#include "border_agent_tracker.hpp"
|
||||
|
||||
#if OPENTHREAD_CONFIG_BORDER_AGENT_TRACKER_ENABLE
|
||||
|
||||
#include "instance/instance.hpp"
|
||||
|
||||
namespace ot {
|
||||
namespace MeshCoP {
|
||||
|
||||
RegisterLogModule("BaTracker");
|
||||
|
||||
//---------------------------------------------------------------------------------------------------------------------
|
||||
// BorderAgentTracker
|
||||
|
||||
const char BorderAgentTracker::kServiceType[] = "_meshcop._udp";
|
||||
|
||||
BorderAgentTracker::BorderAgentTracker(Instance &aInstance)
|
||||
: InstanceLocator(aInstance)
|
||||
, mState(kStateStopped)
|
||||
, mUserEnabled(false)
|
||||
, mStackEnabled(false)
|
||||
{
|
||||
}
|
||||
|
||||
void BorderAgentTracker::SetEnabled(bool aEnable, Requester aRequester)
|
||||
{
|
||||
switch (aRequester)
|
||||
{
|
||||
case kRequesterUser:
|
||||
mUserEnabled = aEnable;
|
||||
break;
|
||||
case kRequesterStack:
|
||||
mStackEnabled = aEnable;
|
||||
break;
|
||||
}
|
||||
|
||||
UpdateState();
|
||||
}
|
||||
|
||||
void BorderAgentTracker::HandleDnssdPlatformStateChange(void) { UpdateState(); }
|
||||
|
||||
void BorderAgentTracker::UpdateState(void)
|
||||
{
|
||||
State newState;
|
||||
|
||||
if (mUserEnabled || mStackEnabled)
|
||||
{
|
||||
newState = Get<Dnssd>().IsReady() ? kStateRunning : kStatePendingDnssd;
|
||||
}
|
||||
else
|
||||
{
|
||||
newState = kStateStopped;
|
||||
}
|
||||
|
||||
VerifyOrExit(newState != mState);
|
||||
|
||||
if (mState == kStateRunning)
|
||||
{
|
||||
Get<Dnssd>().StopBrowser(Browser());
|
||||
mAgents.Free();
|
||||
}
|
||||
|
||||
LogInfo("State: %s -> %s", StateToString(mState), StateToString(newState));
|
||||
mState = newState;
|
||||
|
||||
// It is important to start browser after `mState` is updated.
|
||||
// This ensures that if the `HandleBrowseResult()` callback is
|
||||
// invoked immediately from within the `StartBrowser()` method
|
||||
// call, the state is valid.
|
||||
|
||||
if (newState == kStateRunning)
|
||||
{
|
||||
Get<Dnssd>().StartBrowser(Browser());
|
||||
}
|
||||
|
||||
exit:
|
||||
return;
|
||||
}
|
||||
|
||||
void BorderAgentTracker::HandleBrowseResult(otInstance *aInstance, const otPlatDnssdBrowseResult *aResult)
|
||||
{
|
||||
AsCoreType(aInstance).Get<BorderAgentTracker>().HandleBrowseResult(*aResult);
|
||||
}
|
||||
|
||||
void BorderAgentTracker::HandleBrowseResult(const Dnssd::BrowseResult &aResult)
|
||||
{
|
||||
Error error = kErrorNone;
|
||||
Agent *newAgent;
|
||||
|
||||
VerifyOrExit(IsRunning());
|
||||
|
||||
VerifyOrExit(aResult.mServiceInstance != nullptr);
|
||||
|
||||
if (aResult.mTtl == 0)
|
||||
{
|
||||
mAgents.RemoveMatching(kMatchServiceName, aResult.mServiceInstance);
|
||||
ExitNow();
|
||||
}
|
||||
|
||||
VerifyOrExit(!mAgents.ContainsMatching(kMatchServiceName, aResult.mServiceInstance));
|
||||
|
||||
LogInfo("Discovered agent %s", aResult.mServiceInstance);
|
||||
|
||||
newAgent = Agent::Allocate(GetInstance());
|
||||
VerifyOrExit(newAgent != nullptr, error = kErrorNoBufs);
|
||||
|
||||
// We add the new agent to the list first before setting service
|
||||
// name and starting the SRV and TXT resolvers. This ensures that
|
||||
// if the `HandleSrvResult()` or `HandleTxtResult()` callbacks
|
||||
// are invoked immediately from within the method call that start
|
||||
// the resolvers, the agent entry can be correctly found in the
|
||||
// list.
|
||||
|
||||
mAgents.Push(*newAgent);
|
||||
|
||||
if (newAgent->SetServiceNameAndStartSrvTxtResolvers(aResult.mServiceInstance) != kErrorNone)
|
||||
{
|
||||
// `Pop()` returns a temporary `OwnedPtr<Agent>` which is
|
||||
// immediately destroyed, freeing the allocated agent.
|
||||
mAgents.Pop();
|
||||
error = kErrorNoBufs;
|
||||
}
|
||||
|
||||
exit:
|
||||
LogOnError(error, "add new agent", aResult.mServiceInstance);
|
||||
}
|
||||
|
||||
void BorderAgentTracker::HandleSrvResult(otInstance *aInstance, const otPlatDnssdSrvResult *aResult)
|
||||
{
|
||||
AsCoreType(aInstance).Get<BorderAgentTracker>().HandleSrvResult(*aResult);
|
||||
}
|
||||
|
||||
void BorderAgentTracker::HandleSrvResult(const Dnssd::SrvResult &aResult)
|
||||
{
|
||||
Agent *agent;
|
||||
|
||||
VerifyOrExit(IsRunning());
|
||||
|
||||
agent = mAgents.FindMatching(kMatchServiceName, aResult.mServiceInstance);
|
||||
VerifyOrExit(agent != nullptr);
|
||||
|
||||
if (aResult.mTtl == 0)
|
||||
{
|
||||
agent->SetPort(0);
|
||||
agent->ClearHost();
|
||||
ExitNow();
|
||||
}
|
||||
|
||||
agent->SetPort(aResult.mPort);
|
||||
|
||||
VerifyOrExit(aResult.mHostName != nullptr);
|
||||
agent->SetHost(aResult.mHostName);
|
||||
|
||||
exit:
|
||||
return;
|
||||
}
|
||||
|
||||
void BorderAgentTracker::HandleTxtResult(otInstance *aInstance, const otPlatDnssdTxtResult *aResult)
|
||||
{
|
||||
AsCoreType(aInstance).Get<BorderAgentTracker>().HandleTxtResult(*aResult);
|
||||
}
|
||||
|
||||
void BorderAgentTracker::HandleTxtResult(const Dnssd::TxtResult &aResult)
|
||||
{
|
||||
Agent *agent;
|
||||
|
||||
VerifyOrExit(IsRunning());
|
||||
|
||||
agent = mAgents.FindMatching(kMatchServiceName, aResult.mServiceInstance);
|
||||
VerifyOrExit(agent != nullptr);
|
||||
|
||||
if ((aResult.mTtl == 0) || (aResult.mTxtData == nullptr))
|
||||
{
|
||||
agent->ClearTxtData();
|
||||
}
|
||||
else
|
||||
{
|
||||
agent->SetTxtData(aResult.mTxtData, aResult.mTxtDataLength);
|
||||
}
|
||||
|
||||
exit:
|
||||
return;
|
||||
}
|
||||
|
||||
void BorderAgentTracker::HandleAddressResult(otInstance *aInstance, const otPlatDnssdAddressResult *aResult)
|
||||
{
|
||||
AsCoreType(aInstance).Get<BorderAgentTracker>().HandleAddressResult(*aResult);
|
||||
}
|
||||
|
||||
void BorderAgentTracker::HandleAddressResult(const Dnssd::AddressResult &aResult)
|
||||
{
|
||||
Agent *agent;
|
||||
|
||||
VerifyOrExit(IsRunning());
|
||||
|
||||
agent = mAgents.FindMatching(kMatchHostName, aResult.mHostName);
|
||||
VerifyOrExit(agent != nullptr);
|
||||
|
||||
agent->mHost->SetAddresses(aResult);
|
||||
|
||||
exit:
|
||||
return;
|
||||
}
|
||||
|
||||
bool BorderAgentTracker::NameMatch(const Heap::String &aHeapString, const char *aName)
|
||||
{
|
||||
return !aHeapString.IsNull() && StringMatch(aHeapString.AsCString(), aName, kStringCaseInsensitiveMatch);
|
||||
}
|
||||
|
||||
#if OT_SHOULD_LOG_AT(OT_LOG_LEVEL_WARN)
|
||||
|
||||
void BorderAgentTracker::LogOnError(Error aError, const char *aText, const char *aName)
|
||||
{
|
||||
if (aError != kErrorNone)
|
||||
{
|
||||
LogWarn("Error %s - Failed to %s - %s", ErrorToString(aError), aText, (aName != nullptr) ? aName : "");
|
||||
}
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
void BorderAgentTracker::LogOnError(Error, const char *, const char *) {}
|
||||
|
||||
#endif
|
||||
|
||||
const char *BorderAgentTracker::StateToString(State aState)
|
||||
{
|
||||
static const char *const kStateStrings[] = {
|
||||
"Stopped",
|
||||
"PendingDnssd",
|
||||
"Running",
|
||||
};
|
||||
|
||||
struct EnumCheck
|
||||
{
|
||||
InitEnumValidatorCounter();
|
||||
ValidateNextEnum(kStateStopped);
|
||||
ValidateNextEnum(kStatePendingDnssd);
|
||||
ValidateNextEnum(kStateRunning);
|
||||
};
|
||||
|
||||
return kStateStrings[aState];
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------------------------------------------------
|
||||
// BorderAgentTracker::Iterator
|
||||
|
||||
void BorderAgentTracker::Iterator::Init(Instance &aInstance)
|
||||
{
|
||||
SetAgentEntry(aInstance.Get<BorderAgentTracker>().mAgents.GetHead());
|
||||
SetInitUptime(aInstance.Get<Uptime>().GetUptime());
|
||||
}
|
||||
|
||||
Error BorderAgentTracker::Iterator::GetNextAgentInfo(AgentInfo &aInfo)
|
||||
{
|
||||
Error error = kErrorNone;
|
||||
const Agent *agent = GetAgentEntry();
|
||||
|
||||
VerifyOrExit(agent != nullptr, error = kErrorNotFound);
|
||||
agent->CopyInfoTo(aInfo, GetInitUptime());
|
||||
SetAgentEntry(agent->GetNext());
|
||||
|
||||
exit:
|
||||
return error;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------------------------------------------------
|
||||
// BorderAgentTracker::Host
|
||||
|
||||
BorderAgentTracker::Host::Host(Instance &aInstance)
|
||||
: InstanceLocator(aInstance)
|
||||
{
|
||||
}
|
||||
|
||||
BorderAgentTracker::Host::~Host(void)
|
||||
{
|
||||
VerifyOrExit(mName != nullptr);
|
||||
Get<Dnssd>().StopIp6AddressResolver(AddressResolver(mName.AsCString()));
|
||||
|
||||
exit:
|
||||
return;
|
||||
}
|
||||
|
||||
Error BorderAgentTracker::Host::SetNameAndStartAddrResolver(const char *aHostName)
|
||||
{
|
||||
Error error;
|
||||
|
||||
SuccessOrExit(error = mName.Set(aHostName));
|
||||
Get<Dnssd>().StartIp6AddressResolver(AddressResolver(mName.AsCString()));
|
||||
|
||||
exit:
|
||||
LogOnError(error, "set host name", aHostName);
|
||||
return error;
|
||||
}
|
||||
|
||||
void BorderAgentTracker::Host::SetAddresses(const Dnssd::AddressResult &aResult)
|
||||
{
|
||||
Error error = kErrorNone;
|
||||
uint16_t length;
|
||||
const Dnssd::AddressAndTtl *addrAndTtl;
|
||||
|
||||
mAddresses.Free();
|
||||
|
||||
length = aResult.mAddressesLength;
|
||||
|
||||
SuccessOrExit(error = mAddresses.ReserveCapacity(length));
|
||||
|
||||
for (addrAndTtl = aResult.mAddresses; length > 0; length--, addrAndTtl++)
|
||||
{
|
||||
const Ip6::Address &addr = AsCoreType(&addrAndTtl->mAddress);
|
||||
|
||||
if (addrAndTtl->mTtl == 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!mAddresses.Contains(addr))
|
||||
{
|
||||
SuccessOrAssert(mAddresses.PushBack(addr));
|
||||
}
|
||||
}
|
||||
|
||||
exit:
|
||||
LogOnError(error, "set host addresses", mName.AsCString());
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------------------------------------------------
|
||||
// BorderAgentTracker::Agent
|
||||
|
||||
BorderAgentTracker::Agent::Agent(Instance &aInstance)
|
||||
: InstanceLocator(aInstance)
|
||||
, mNext(nullptr)
|
||||
, mDiscoverUptime(aInstance.Get<Uptime>().GetUptime())
|
||||
, mLastUpdateUptime(mDiscoverUptime)
|
||||
, mPort(0)
|
||||
{
|
||||
}
|
||||
|
||||
BorderAgentTracker::Agent::~Agent(void)
|
||||
{
|
||||
VerifyOrExit(mServiceName != nullptr);
|
||||
Get<Dnssd>().StopSrvResolver(SrvResolver(mServiceName.AsCString()));
|
||||
Get<Dnssd>().StopTxtResolver(TxtResolver(mServiceName.AsCString()));
|
||||
|
||||
exit:
|
||||
return;
|
||||
}
|
||||
|
||||
Error BorderAgentTracker::Agent::SetServiceNameAndStartSrvTxtResolvers(const char *aServiceName)
|
||||
{
|
||||
Error error;
|
||||
|
||||
SuccessOrExit(error = mServiceName.Set(aServiceName));
|
||||
|
||||
SetUpdateTimeToNow();
|
||||
|
||||
Get<Dnssd>().StartSrvResolver(SrvResolver(mServiceName.AsCString()));
|
||||
Get<Dnssd>().StartTxtResolver(TxtResolver(mServiceName.AsCString()));
|
||||
|
||||
exit:
|
||||
return error;
|
||||
}
|
||||
|
||||
void BorderAgentTracker::Agent::SetHost(const char *aHostName)
|
||||
{
|
||||
Agent *matchingHostAgent;
|
||||
|
||||
if (mHost != nullptr)
|
||||
{
|
||||
VerifyOrExit(!NameMatch(mHost->mName, aHostName));
|
||||
}
|
||||
|
||||
SetUpdateTimeToNow();
|
||||
|
||||
// We handle the case where multiple meshcop services are
|
||||
// advertised from the same host. While this is unlikely in
|
||||
// actual deployments, it can be useful for testing. To minimize
|
||||
// resource usage (memory and mDNS queries), we check if another
|
||||
// `Agent` is already tracking the same host. If so, we share its
|
||||
// `Host` entry. Otherwise, we allocate a new one. Note that
|
||||
// `mHost` is defined as `RetainPtr` which does ref-counting.
|
||||
|
||||
matchingHostAgent = Get<BorderAgentTracker>().mAgents.FindMatching(kMatchHostName, aHostName);
|
||||
|
||||
if (matchingHostAgent != nullptr)
|
||||
{
|
||||
mHost = matchingHostAgent->mHost;
|
||||
}
|
||||
else
|
||||
{
|
||||
mHost.Reset(Host::Allocate(GetInstance()));
|
||||
VerifyOrExit(mHost != nullptr);
|
||||
|
||||
if (mHost->SetNameAndStartAddrResolver(aHostName) != kErrorNone)
|
||||
{
|
||||
ClearHost();
|
||||
}
|
||||
}
|
||||
|
||||
exit:
|
||||
return;
|
||||
}
|
||||
|
||||
void BorderAgentTracker::Agent::ClearHost(void)
|
||||
{
|
||||
VerifyOrExit(mHost != nullptr);
|
||||
|
||||
mHost.Reset();
|
||||
SetUpdateTimeToNow();
|
||||
|
||||
exit:
|
||||
return;
|
||||
}
|
||||
|
||||
void BorderAgentTracker::Agent::SetPort(uint16_t aPort)
|
||||
{
|
||||
VerifyOrExit(mPort != aPort);
|
||||
|
||||
SetUpdateTimeToNow();
|
||||
mPort = aPort;
|
||||
|
||||
exit:
|
||||
return;
|
||||
}
|
||||
|
||||
void BorderAgentTracker::Agent::SetTxtData(const uint8_t *aData, uint16_t aDataLength)
|
||||
{
|
||||
Error error = kErrorNone;
|
||||
|
||||
VerifyOrExit(!mTxtData.Matches(aData, aDataLength));
|
||||
|
||||
SuccessOrExit(error = mTxtData.SetFrom(aData, aDataLength));
|
||||
SetUpdateTimeToNow();
|
||||
|
||||
exit:
|
||||
LogOnError(error, "set TXT data", mServiceName.AsCString());
|
||||
}
|
||||
|
||||
void BorderAgentTracker::Agent::ClearTxtData(void)
|
||||
{
|
||||
VerifyOrExit(!mTxtData.IsNull());
|
||||
|
||||
SetUpdateTimeToNow();
|
||||
mTxtData.Free();
|
||||
|
||||
exit:
|
||||
return;
|
||||
}
|
||||
|
||||
void BorderAgentTracker::Agent::SetUpdateTimeToNow(void) { mLastUpdateUptime = Get<Uptime>().GetUptime(); }
|
||||
|
||||
bool BorderAgentTracker::Agent::Matches(MatchType aType, const char *aName) const
|
||||
{
|
||||
bool matches = false;
|
||||
|
||||
switch (aType)
|
||||
{
|
||||
case kMatchServiceName:
|
||||
matches = NameMatch(mServiceName, aName);
|
||||
break;
|
||||
|
||||
case kMatchHostName:
|
||||
VerifyOrExit(mHost != nullptr);
|
||||
matches = NameMatch(mHost->mName, aName);
|
||||
break;
|
||||
}
|
||||
|
||||
exit:
|
||||
return matches;
|
||||
}
|
||||
|
||||
void BorderAgentTracker::Agent::CopyInfoTo(AgentInfo &aInfo, uint64_t aUptimeNow) const
|
||||
{
|
||||
ClearAllBytes(aInfo);
|
||||
|
||||
aInfo.mServiceName = mServiceName.AsCString();
|
||||
aInfo.mPort = mPort;
|
||||
aInfo.mTxtData = mTxtData.GetBytes();
|
||||
aInfo.mTxtDataLength = mTxtData.GetLength();
|
||||
aInfo.mMsecSinceDiscovered = aUptimeNow - mDiscoverUptime;
|
||||
aInfo.mMsecSinceLastChange = aUptimeNow - mLastUpdateUptime;
|
||||
|
||||
if (mHost != nullptr)
|
||||
{
|
||||
aInfo.mHostName = mHost->mName.AsCString();
|
||||
aInfo.mAddresses = mHost->mAddresses.AsCArray();
|
||||
aInfo.mNumAddresses = mHost->mAddresses.GetLength();
|
||||
}
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
// BorderAgentTracker::Browser
|
||||
|
||||
BorderAgentTracker::Browser::Browser(void)
|
||||
{
|
||||
Clear();
|
||||
mServiceType = kServiceType;
|
||||
mCallback = BorderAgentTracker::HandleBrowseResult;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
// BorderAgentTracker::SrvResolver
|
||||
|
||||
BorderAgentTracker::SrvResolver::SrvResolver(const char *aServiceName)
|
||||
{
|
||||
Clear();
|
||||
mServiceInstance = aServiceName;
|
||||
mServiceType = kServiceType;
|
||||
mCallback = BorderAgentTracker::HandleSrvResult;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
// BorderAgentTracker::TxtResolver
|
||||
|
||||
BorderAgentTracker::TxtResolver::TxtResolver(const char *aServiceName)
|
||||
{
|
||||
Clear();
|
||||
mServiceInstance = aServiceName;
|
||||
mServiceType = kServiceType;
|
||||
mCallback = BorderAgentTracker::HandleTxtResult;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
// BorderAgentTracker::AddressResolver
|
||||
|
||||
BorderAgentTracker::AddressResolver::AddressResolver(const char *aHostName)
|
||||
{
|
||||
Clear();
|
||||
mHostName = aHostName;
|
||||
mCallback = BorderAgentTracker::HandleAddressResult;
|
||||
}
|
||||
|
||||
} // namespace MeshCoP
|
||||
} // namespace ot
|
||||
|
||||
#endif // OPENTHREAD_CONFIG_BORDER_AGENT_TRACKER_ENABLE
|
||||
@@ -0,0 +1,245 @@
|
||||
/*
|
||||
* Copyright (c) 2025, 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 Border Agent Tracker.
|
||||
*/
|
||||
|
||||
#ifndef BORDER_AGENT_TRACKER_HPP_
|
||||
#define BORDER_AGENT_TRACKER_HPP_
|
||||
|
||||
#include "openthread-core-config.h"
|
||||
|
||||
#if OPENTHREAD_CONFIG_BORDER_AGENT_TRACKER_ENABLE
|
||||
|
||||
#if !OPENTHREAD_CONFIG_PLATFORM_DNSSD_ENABLE && !OPENTHREAD_CONFIG_MULTICAST_DNS_ENABLE
|
||||
#error "BORDER_AGENT_TRACKER_ENABLE requires either the native mDNS or platform DNS-SD APIs"
|
||||
#endif
|
||||
|
||||
#include <openthread/border_agent_tracker.h>
|
||||
|
||||
#include "common/as_core_type.hpp"
|
||||
#include "common/heap_allocatable.hpp"
|
||||
#include "common/heap_array.hpp"
|
||||
#include "common/heap_data.hpp"
|
||||
#include "common/heap_string.hpp"
|
||||
#include "common/locator.hpp"
|
||||
#include "common/owning_list.hpp"
|
||||
#include "common/retain_ptr.hpp"
|
||||
#include "net/dnssd.hpp"
|
||||
#include "net/ip6_address.hpp"
|
||||
|
||||
namespace ot {
|
||||
namespace MeshCoP {
|
||||
|
||||
/**
|
||||
* Implements the Border Agent Tracker.
|
||||
*/
|
||||
class BorderAgentTracker : public InstanceLocator
|
||||
{
|
||||
friend class ot::Dnssd;
|
||||
|
||||
struct Agent;
|
||||
|
||||
public:
|
||||
typedef otBorderAgentTrackerAgentInfo AgentInfo; ///< Information about a discovered Border Agent.
|
||||
|
||||
/**
|
||||
* Represents an entity requesting to start or stop.
|
||||
*/
|
||||
enum Requester : uint8_t
|
||||
{
|
||||
kRequesterUser, ///< Requested by user (public OT API).
|
||||
kRequesterStack, ///< Requested by stack itself (other OT modules).
|
||||
};
|
||||
|
||||
class Iterator : public otBorderAgentTrackerIterator
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Initializes the iterator.
|
||||
*
|
||||
* An iterator MUST be initialized before being used.
|
||||
*
|
||||
* @param[in] aInstance The OpenThread instance.
|
||||
*/
|
||||
void Init(Instance &aInstance);
|
||||
|
||||
/**
|
||||
* Gets the information for the next discovered Border Agent.
|
||||
*
|
||||
* @param[out] aInfo A reference to an `AgentInfo` to populate with the agent's information.
|
||||
*
|
||||
* @retval kErrorNone Successfully retrieved the information for the next agent.
|
||||
* @retval kErrorNotFound No more agents were found.
|
||||
*/
|
||||
Error GetNextAgentInfo(AgentInfo &aInfo);
|
||||
|
||||
private:
|
||||
const Agent *GetAgentEntry(void) const { return static_cast<const Agent *>(mPtr); }
|
||||
void SetAgentEntry(const Agent *aEntry) { mPtr = aEntry; }
|
||||
uint64_t GetInitUptime(void) const { return mData; }
|
||||
void SetInitUptime(uint64_t aUptime) { mData = aUptime; }
|
||||
};
|
||||
|
||||
/**
|
||||
* Initializes the Border Agent Tracker.
|
||||
*
|
||||
* @param[in] aInstance The OpenThread instance.
|
||||
*/
|
||||
explicit BorderAgentTracker(Instance &aInstance);
|
||||
|
||||
/**
|
||||
* Enables or disables the Border Agent Tracker.
|
||||
*
|
||||
* When enabled, the tracker browses for the `_meshcop._udp` mDNS service to discover and track Border Agents on
|
||||
* the infra-if network.
|
||||
*
|
||||
* The Border Agent Tracker can be enabled by multiple requesters (see `Requester`). It remains enabled as long as
|
||||
* at least one requester has it enabled. It is disabled only when all requesters have disabled it.
|
||||
*
|
||||
* @param[in] aEnable A boolean to enable/disable the Border Agent Tracker.
|
||||
* @param[in] aRequester The entity requesting to enable/disable.
|
||||
*/
|
||||
void SetEnabled(bool aEnable, Requester aRequester);
|
||||
|
||||
/**
|
||||
* Indicates whether the tracker is running.
|
||||
*
|
||||
* The tracker is running if at least one requester (see `Requester`) has enabled it and the underlying DNS-SD
|
||||
* platform is ready.
|
||||
*
|
||||
* @retval TRUE If the tracker is running.
|
||||
* @retval FALSE If the tracker is not running.
|
||||
*/
|
||||
bool IsRunning(void) const { return mState == kStateRunning; }
|
||||
|
||||
private:
|
||||
enum State : uint8_t
|
||||
{
|
||||
kStateStopped,
|
||||
kStatePendingDnssd,
|
||||
kStateRunning,
|
||||
};
|
||||
|
||||
enum MatchType : uint8_t
|
||||
{
|
||||
kMatchServiceName,
|
||||
kMatchHostName,
|
||||
};
|
||||
|
||||
struct Host : public InstanceLocator, public RetainCountable, public Heap::Allocatable<Host>
|
||||
{
|
||||
explicit Host(Instance &aInstance);
|
||||
~Host(void);
|
||||
|
||||
Error SetNameAndStartAddrResolver(const char *aHostName);
|
||||
void SetAddresses(const Dnssd::AddressResult &aResult);
|
||||
|
||||
Heap::String mName;
|
||||
Heap::Array<Ip6::Address> mAddresses;
|
||||
};
|
||||
|
||||
struct Agent : public InstanceLocator, public Heap::Allocatable<Agent>, public LinkedListEntry<Agent>
|
||||
{
|
||||
explicit Agent(Instance &aInstance);
|
||||
~Agent(void);
|
||||
|
||||
Error SetServiceNameAndStartSrvTxtResolvers(const char *aServiceName);
|
||||
void SetHost(const char *aHostName);
|
||||
void ClearHost(void);
|
||||
void SetPort(uint16_t aPort);
|
||||
void SetTxtData(const uint8_t *aData, uint16_t aDataLength);
|
||||
void ClearTxtData(void);
|
||||
void SetUpdateTimeToNow(void);
|
||||
bool Matches(MatchType aType, const char *aName) const;
|
||||
void CopyInfoTo(AgentInfo &aInfo, uint64_t aUptimeNow) const;
|
||||
|
||||
Agent *mNext;
|
||||
Heap::String mServiceName;
|
||||
RetainPtr<Host> mHost;
|
||||
Heap::Data mTxtData;
|
||||
uint64_t mDiscoverUptime;
|
||||
uint64_t mLastUpdateUptime;
|
||||
uint16_t mPort;
|
||||
};
|
||||
|
||||
struct Browser : public Dnssd::Browser
|
||||
{
|
||||
Browser(void);
|
||||
};
|
||||
|
||||
struct SrvResolver : public Dnssd::SrvResolver
|
||||
{
|
||||
explicit SrvResolver(const char *aServiceName);
|
||||
};
|
||||
|
||||
struct TxtResolver : public Dnssd::TxtResolver
|
||||
{
|
||||
explicit TxtResolver(const char *aServiceName);
|
||||
};
|
||||
|
||||
struct AddressResolver : public Dnssd::AddressResolver
|
||||
{
|
||||
explicit AddressResolver(const char *aHostName);
|
||||
};
|
||||
|
||||
void UpdateState(void);
|
||||
void HandleDnssdPlatformStateChange(void);
|
||||
void HandleBrowseResult(const Dnssd::BrowseResult &aResult);
|
||||
void HandleSrvResult(const Dnssd::SrvResult &aResult);
|
||||
void HandleTxtResult(const Dnssd::TxtResult &aResult);
|
||||
void HandleAddressResult(const Dnssd::AddressResult &aResult);
|
||||
|
||||
static void HandleBrowseResult(otInstance *aInstance, const otPlatDnssdBrowseResult *aResult);
|
||||
static void HandleSrvResult(otInstance *aInstance, const otPlatDnssdSrvResult *aResult);
|
||||
static void HandleTxtResult(otInstance *aInstance, const otPlatDnssdTxtResult *aResult);
|
||||
static void HandleAddressResult(otInstance *aInstance, const otPlatDnssdAddressResult *aResult);
|
||||
|
||||
static bool NameMatch(const Heap::String &aHeapString, const char *aName);
|
||||
static void LogOnError(Error aError, const char *aText, const char *aName);
|
||||
static const char *StateToString(State aState);
|
||||
|
||||
static const char kServiceType[];
|
||||
|
||||
State mState;
|
||||
bool mUserEnabled : 1;
|
||||
bool mStackEnabled : 1;
|
||||
OwningList<Agent> mAgents;
|
||||
};
|
||||
|
||||
} // namespace MeshCoP
|
||||
|
||||
DefineCoreType(otBorderAgentTrackerIterator, MeshCoP::BorderAgentTracker::Iterator);
|
||||
|
||||
} // namespace ot
|
||||
|
||||
#endif // OPENTHREAD_CONFIG_BORDER_AGENT_TRACKER_ENABLE
|
||||
|
||||
#endif // BORDER_AGENT_TRACKER_HPP_
|
||||
@@ -531,6 +531,10 @@ void Dnssd::HandleStateChange(void)
|
||||
Get<MeshCoP::BorderAgent>().HandleDnssdPlatformStateChange();
|
||||
#endif
|
||||
|
||||
#if OPENTHREAD_CONFIG_BORDER_AGENT_TRACKER_ENABLE
|
||||
Get<MeshCoP::BorderAgentTracker>().HandleDnssdPlatformStateChange();
|
||||
#endif
|
||||
|
||||
#if OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE && OPENTHREAD_CONFIG_TREL_MANAGE_DNSSD_ENABLE
|
||||
Get<Trel::PeerDiscoverer>().HandleDnssdPlatformStateChange();
|
||||
#endif
|
||||
|
||||
@@ -112,6 +112,7 @@ endmacro()
|
||||
#----------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
ot_nexus_test(border_agent)
|
||||
ot_nexus_test(border_agent_tracker)
|
||||
ot_nexus_test(dtls)
|
||||
ot_nexus_test(form_join)
|
||||
ot_nexus_test(full_network_reset)
|
||||
|
||||
@@ -46,6 +46,7 @@
|
||||
#define OPENTHREAD_CONFIG_BORDER_AGENT_ENABLE 1
|
||||
#define OPENTHREAD_CONFIG_BORDER_AGENT_EPHEMERAL_KEY_ENABLE 1
|
||||
#define OPENTHREAD_CONFIG_BORDER_AGENT_ID_ENABLE 1
|
||||
#define OPENTHREAD_CONFIG_BORDER_AGENT_TRACKER_ENABLE 1
|
||||
#define OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE 1
|
||||
#define OPENTHREAD_CONFIG_BORDER_ROUTING_DHCP6_PD_ENABLE 1
|
||||
#define OPENTHREAD_CONFIG_BORDER_ROUTING_DHCP6_PD_CLIENT_ENABLE 0
|
||||
|
||||
@@ -0,0 +1,271 @@
|
||||
/*
|
||||
* Copyright (c) 2025, 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.
|
||||
*/
|
||||
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "platform/nexus_core.hpp"
|
||||
#include "platform/nexus_node.hpp"
|
||||
|
||||
namespace ot {
|
||||
namespace Nexus {
|
||||
|
||||
void TestBorderAgentTracker(void)
|
||||
{
|
||||
static constexpr uint32_t kInfraIfIndex = 1;
|
||||
|
||||
Core nexus;
|
||||
Node &node0 = nexus.CreateNode();
|
||||
Node &node1 = nexus.CreateNode();
|
||||
Node &node2 = nexus.CreateNode();
|
||||
Node &node3 = nexus.CreateNode();
|
||||
MeshCoP::BorderAgentTracker::Iterator iterator;
|
||||
MeshCoP::BorderAgentTracker::AgentInfo agent;
|
||||
Dns::Multicast::Core::Service service;
|
||||
uint16_t count;
|
||||
bool found;
|
||||
|
||||
Log("------------------------------------------------------------------------------------------------------");
|
||||
Log("TestBorderAgentTracker");
|
||||
|
||||
SuccessOrQuit(node0.Get<Dns::Multicast::Core>().SetEnabled(true, kInfraIfIndex));
|
||||
SuccessOrQuit(node1.Get<Dns::Multicast::Core>().SetEnabled(true, kInfraIfIndex));
|
||||
SuccessOrQuit(node2.Get<Dns::Multicast::Core>().SetEnabled(true, kInfraIfIndex));
|
||||
SuccessOrQuit(node3.Get<Dns::Multicast::Core>().SetEnabled(true, kInfraIfIndex));
|
||||
|
||||
node0.Form();
|
||||
nexus.AdvanceTime(13 * 1000);
|
||||
VerifyOrQuit(node0.Get<Mle::Mle>().IsLeader());
|
||||
|
||||
node1.Join(node0);
|
||||
node2.Join(node0);
|
||||
node3.Form();
|
||||
nexus.AdvanceTime(600 * 1000);
|
||||
|
||||
VerifyOrQuit(node1.Get<Mle::Mle>().IsRouter());
|
||||
VerifyOrQuit(node2.Get<Mle::Mle>().IsRouter());
|
||||
VerifyOrQuit(node3.Get<Mle::Mle>().IsLeader());
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
Log("Check Border Agent Tracker's initial state");
|
||||
|
||||
VerifyOrQuit(!node0.Get<MeshCoP::BorderAgentTracker>().IsRunning());
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
Log("Enable Border Agent Tracker");
|
||||
|
||||
node0.Get<MeshCoP::BorderAgentTracker>().SetEnabled(true, MeshCoP::BorderAgentTracker::kRequesterUser);
|
||||
nexus.AdvanceTime(10);
|
||||
|
||||
VerifyOrQuit(node0.Get<MeshCoP::BorderAgentTracker>().IsRunning());
|
||||
|
||||
nexus.AdvanceTime(5000);
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
Log("Check the tracked agents");
|
||||
|
||||
iterator.Init(node0.GetInstance());
|
||||
count = 0;
|
||||
|
||||
while (iterator.GetNextAgentInfo(agent) == kErrorNone)
|
||||
{
|
||||
count++;
|
||||
Log("- %u) \"%s\", host:\"%s\", port:%u", count, agent.mServiceName, agent.mHostName, agent.mPort);
|
||||
|
||||
VerifyOrQuit(agent.mHostName != nullptr);
|
||||
VerifyOrQuit(agent.mPort != 0);
|
||||
VerifyOrQuit(agent.mTxtData != nullptr);
|
||||
VerifyOrQuit(agent.mAddresses != nullptr);
|
||||
}
|
||||
|
||||
VerifyOrQuit(count == 4);
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
Log("Disable BA function on node0, ensure that it is removed from the `BorderAgentTracker` list");
|
||||
|
||||
node0.Get<MeshCoP::BorderAgent>().SetEnabled(false);
|
||||
nexus.AdvanceTime(5000);
|
||||
|
||||
iterator.Init(node0.GetInstance());
|
||||
count = 0;
|
||||
|
||||
while (iterator.GetNextAgentInfo(agent) == kErrorNone)
|
||||
{
|
||||
count++;
|
||||
Log("- %u) \"%s\", host:\"%s\", port:%u", count, agent.mServiceName, agent.mHostName, agent.mPort);
|
||||
|
||||
VerifyOrQuit(agent.mHostName != nullptr);
|
||||
VerifyOrQuit(agent.mPort != 0);
|
||||
VerifyOrQuit(agent.mTxtData != nullptr);
|
||||
VerifyOrQuit(agent.mAddresses != nullptr);
|
||||
}
|
||||
|
||||
VerifyOrQuit(count == 3);
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
Log("Re-enable BA function on node0, ensure that it is added again in the `BorderAgentTracker` list");
|
||||
|
||||
node0.Get<MeshCoP::BorderAgent>().SetEnabled(true);
|
||||
nexus.AdvanceTime(5000);
|
||||
|
||||
iterator.Init(node0.GetInstance());
|
||||
count = 0;
|
||||
|
||||
while (iterator.GetNextAgentInfo(agent) == kErrorNone)
|
||||
{
|
||||
count++;
|
||||
Log("- %u) \"%s\", host:\"%s\", port:%u", count, agent.mServiceName, agent.mHostName, agent.mPort);
|
||||
|
||||
VerifyOrQuit(agent.mHostName != nullptr);
|
||||
VerifyOrQuit(agent.mPort != 0);
|
||||
VerifyOrQuit(agent.mTxtData != nullptr);
|
||||
VerifyOrQuit(agent.mAddresses != nullptr);
|
||||
}
|
||||
|
||||
VerifyOrQuit(count == 4);
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
Log("Disable Border Agent Tracker");
|
||||
|
||||
node0.Get<MeshCoP::BorderAgentTracker>().SetEnabled(false, MeshCoP::BorderAgentTracker::kRequesterUser);
|
||||
nexus.AdvanceTime(10);
|
||||
|
||||
VerifyOrQuit(!node0.Get<MeshCoP::BorderAgentTracker>().IsRunning());
|
||||
|
||||
iterator.Init(node0.GetInstance());
|
||||
VerifyOrQuit(iterator.GetNextAgentInfo(agent) == kErrorNotFound);
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
Log("Re-enable BA tracker and ensure all agents are discovered again");
|
||||
|
||||
node0.Get<MeshCoP::BorderAgentTracker>().SetEnabled(true, MeshCoP::BorderAgentTracker::kRequesterUser);
|
||||
nexus.AdvanceTime(10);
|
||||
|
||||
VerifyOrQuit(node0.Get<MeshCoP::BorderAgentTracker>().IsRunning());
|
||||
|
||||
nexus.AdvanceTime(5000);
|
||||
|
||||
iterator.Init(node0.GetInstance());
|
||||
count = 0;
|
||||
|
||||
while (iterator.GetNextAgentInfo(agent) == kErrorNone)
|
||||
{
|
||||
count++;
|
||||
Log("- %u) \"%s\", host:\"%s\", port:%u", count, agent.mServiceName, agent.mHostName, agent.mPort);
|
||||
|
||||
VerifyOrQuit(agent.mHostName != nullptr);
|
||||
VerifyOrQuit(agent.mPort != 0);
|
||||
VerifyOrQuit(agent.mTxtData != nullptr);
|
||||
VerifyOrQuit(agent.mAddresses != nullptr);
|
||||
}
|
||||
|
||||
VerifyOrQuit(count == 4);
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
Log("Manually register a second `_meshcop._udp` service on node3");
|
||||
|
||||
ClearAllBytes(service);
|
||||
service.mServiceInstance = "extra";
|
||||
service.mServiceType = "_meshcop._udp";
|
||||
service.mPort = 1234;
|
||||
|
||||
SuccessOrQuit(node3.Get<Dns::Multicast::Core>().RegisterService(service, /* aRequestId */ 0, nullptr));
|
||||
|
||||
nexus.AdvanceTime(5 * 1000);
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
Log("Validate that both agent services from node3 are discovered and tracked correctly");
|
||||
|
||||
iterator.Init(node0.GetInstance());
|
||||
count = 0;
|
||||
found = false;
|
||||
|
||||
while (iterator.GetNextAgentInfo(agent) == kErrorNone)
|
||||
{
|
||||
count++;
|
||||
Log("- %u) \"%s\", host:\"%s\", port:%u", count, agent.mServiceName, agent.mHostName, agent.mPort);
|
||||
|
||||
VerifyOrQuit(agent.mHostName != nullptr);
|
||||
VerifyOrQuit(agent.mPort != 0);
|
||||
VerifyOrQuit(agent.mTxtData != nullptr);
|
||||
VerifyOrQuit(agent.mAddresses != nullptr);
|
||||
|
||||
if (StringMatch(agent.mServiceName, service.mServiceInstance, kStringCaseInsensitiveMatch))
|
||||
{
|
||||
VerifyOrQuit(agent.mPort == service.mPort);
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
|
||||
VerifyOrQuit(count == 5);
|
||||
VerifyOrQuit(found);
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
Log("Unregister the manually added second `_meshcop._udp` service");
|
||||
|
||||
SuccessOrQuit(node3.Get<Dns::Multicast::Core>().UnregisterService(service));
|
||||
|
||||
nexus.AdvanceTime(5 * 1000);
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
Log("Validate that tracked agents is updated");
|
||||
|
||||
iterator.Init(node0.GetInstance());
|
||||
count = 0;
|
||||
found = false;
|
||||
|
||||
while (iterator.GetNextAgentInfo(agent) == kErrorNone)
|
||||
{
|
||||
count++;
|
||||
Log("- %u) \"%s\", host:\"%s\", port:%u", count, agent.mServiceName, agent.mHostName, agent.mPort);
|
||||
|
||||
VerifyOrQuit(agent.mHostName != nullptr);
|
||||
VerifyOrQuit(agent.mPort != 0);
|
||||
VerifyOrQuit(agent.mTxtData != nullptr);
|
||||
VerifyOrQuit(agent.mAddresses != nullptr);
|
||||
|
||||
if (StringMatch(agent.mServiceName, service.mServiceInstance, kStringCaseInsensitiveMatch))
|
||||
{
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
|
||||
VerifyOrQuit(count == 4);
|
||||
VerifyOrQuit(!found);
|
||||
}
|
||||
|
||||
} // namespace Nexus
|
||||
} // namespace ot
|
||||
|
||||
int main(void)
|
||||
{
|
||||
ot::Nexus::TestBorderAgentTracker();
|
||||
printf("All tests passed\n");
|
||||
return 0;
|
||||
}
|
||||
@@ -82,6 +82,8 @@
|
||||
|
||||
#define OPENTHREAD_CONFIG_BORDER_AGENT_ID_ENABLE 1
|
||||
|
||||
#define OPENTHREAD_CONFIG_BORDER_AGENT_TRACKER_ENABLE 1
|
||||
|
||||
#define OPENTHREAD_CONFIG_DIAG_ENABLE 1
|
||||
|
||||
#define OPENTHREAD_CONFIG_MLE_LINK_METRICS_SUBJECT_ENABLE 1
|
||||
|
||||
Reference in New Issue
Block a user