[trel] manage mDNS/DNSSD and peer discovery in core (#11528)

This commit enhances the TREL module to manage mDNS/DNSSD service
registration and peer discovery (browse and resolving for TREL
services). This feature can be controlled through
`OPENTHREAD_CONFIG_TREL_MANAGE_DNSSD_ENABLE` (and/or the CMake option
`OT_TREL_MANAGE_DNSSD`).

When enabled, TREL will utilize the `Dnssd` module, which provides
mDNS-related APIs. This can be tied to OpenThread's native mDNS
implementation or to `otPlatDnssd` (i.e., provided by the platform
layer).

This commit also adds support for the TREL platform in the `Nexus`
test framework and uses this to add a detailed `test_trel` case. This
test covers basic TREL peer discovery and operation, along with
specific scenarios such as peer removal delay, delayed mDNS start,
TREL service name conflict resolution, host address changes, and
supporting multiple services on the same host (while unlikely in
actual deployments, this can be useful for testing and simulation
where a single machine may act as multiple Thread nodes, thus
advertising multiple TREL services from the same hostname. This is
explicitly supported by the implementation and covered in the
tests).
This commit is contained in:
Abtin Keshavarzian
2025-06-02 12:45:39 -07:00
committed by GitHub
parent c020f86230
commit c2de9f646a
27 changed files with 2206 additions and 66 deletions
+3
View File
@@ -212,6 +212,9 @@ jobs:
run: |
./tests/nexus/build.sh
ninja test
git clean -dfx
./tests/nexus/build.sh trel
./tests/nexus/nexus_trel
upload-coverage:
needs:
+1
View File
@@ -255,6 +255,7 @@ ot_option(OT_SRP_SERVER_FAST_START_MDOE OPENTHREAD_CONFIG_SRP_SERVER_FAST_START_
ot_option(OT_TCP OPENTHREAD_CONFIG_TCP_ENABLE "TCP")
ot_option(OT_TIME_SYNC OPENTHREAD_CONFIG_TIME_SYNC_ENABLE "time synchronization service")
ot_option(OT_TREL OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE "TREL radio link for Thread over Infrastructure feature")
ot_option(OT_TREL_MANAGE_DNSSD OPENTHREAD_CONFIG_TREL_MANAGE_DNSSD_ENABLE "TREL to manage DNSSD and peer discovery")
ot_option(OT_TX_BEACON_PAYLOAD OPENTHREAD_CONFIG_MAC_OUTGOING_BEACON_PAYLOAD_ENABLE "tx beacon payload")
ot_option(OT_TX_QUEUE_STATS OPENTHREAD_CONFIG_TX_QUEUE_STATISTICS_ENABLE "tx queue statistics")
ot_option(OT_UDP_FORWARD OPENTHREAD_CONFIG_UDP_FORWARD_ENABLE "UDP forward")
+1 -1
View File
@@ -52,7 +52,7 @@ extern "C" {
*
* @note This number versions both OpenThread platform and user APIs.
*/
#define OPENTHREAD_API_VERSION (509)
#define OPENTHREAD_API_VERSION (510)
/**
* @addtogroup api-instance
+19 -12
View File
@@ -65,18 +65,19 @@ extern "C" {
* The socket is also bound to network interface(s) on which TREL is to be supported. The socket and the chosen port
* should stay valid while TREL is enabled.
*
* 2) Platform layer MUST initiate an ongoing DNS-SD browse on the service name "_trel._udp" within the local browsing
* domain to discover other devices supporting TREL. The ongoing browse will produce two different types of events:
* "add" events and "remove" events. When the browse is started, it should produce an "add" event for every TREL peer
* currently present on the network. Whenever a TREL peer goes offline, a "remove" event should be produced. "remove"
* events are not guaranteed, however. When a TREL service instance is discovered, a new ongoing DNS-SD query for an
* AAAA record should be started on the hostname indicated in the SRV record of the discovered instance. If multiple
* host IPv6 addressees are discovered for a peer, one with highest scope among all addresses MUST be reported (if
* there are multiple address at same scope, one must be selected randomly).
*
* TREL platform MUST signal back the discovered peer info using `otPlatTrelHandleDiscoveredPeerInfo()` callback. This
* callback MUST be invoked when a new peer is discovered, when there is a change in an existing entry (e.g., new
* TXT record or new port number or new IPv6 address), or when the peer is removed.
* 2) If `OPENTHREAD_CONFIG_TREL_MANAGE_DNSSD_ENABLE` is enabled, the OpenThread core TREL implementation itself will
* handle mDNS (DNS-SD) TREL service registration and peer discovery. Otherwise the platform layer MUST initiate an
* ongoing DNS-SD browse on the service name "_trel._udp" within the local browsing domain to discover other devices
* supporting TREL. The ongoing browse will produce two different types of events: "add" events and "remove" events.
* When the browse is started, it should produce an "add" event for every TREL peer currently present on the network.
* Whenever a TREL peer goes offline, a "remove" event should be produced. "remove" events are not guaranteed, however.
* When a TREL service instance is discovered, a new ongoing DNS-SD query for an AAAA record should be started on the
* hostname indicated in the SRV record of the discovered instance. If multiple host IPv6 addressees are discovered for
* a peer, one with highest scope among all addresses MUST be reported (if there are multiple address at same scope,
* one must be selected randomly). TREL platform MUST signal back the discovered peer info using
* `otPlatTrelHandleDiscoveredPeerInfo()` callback. This callback MUST be invoked when a new peer is discovered, when
* there is a change in an existing entry (e.g., new TXT record or new port number or new IPv6 address), or when the
* peer is removed.
*
* @param[in] aInstance The OpenThread instance.
* @param[out] aUdpPort A pointer to return the selected port number by platform layer.
@@ -127,6 +128,8 @@ typedef struct otPlatTrelPeerInfo
/**
* This is a callback function from platform layer to report a discovered TREL peer info.
*
* This is only applicable when `OPENTHREAD_CONFIG_TREL_MANAGE_DNSSD_ENABLE` is disabled.
*
* @note The @p aInfo structure and its content (e.g., the `mTxtData` buffer) does not need to persist after returning
* from this call. OpenThread code will make a copy of all the info it needs.
*
@@ -139,6 +142,8 @@ extern void otPlatTrelHandleDiscoveredPeerInfo(otInstance *aInstance, const otPl
* Notifies platform that a TREL packet is received from a peer using a different socket address than the one reported
* earlier from `otPlatTrelHandleDiscoveredPeerInfo()`.
*
* This is only applicable when `OPENTHREAD_CONFIG_TREL_MANAGE_DNSSD_ENABLE` is disabled.
*
* Ideally the platform underlying DNS-SD should detect changes to advertised port and addresses by peers, however,
* there are situations where this is not detected reliably. This function signals to the platform layer than we
* received a packet from a peer with it using a different port or address. This can be used by the playroom layer to
@@ -155,6 +160,8 @@ void otPlatTrelNotifyPeerSocketAddressDifference(otInstance *aInstance,
/**
* Registers a new service to be advertised using DNS-SD [RFC6763].
*
* This is only applicable when `OPENTHREAD_CONFIG_TREL_MANAGE_DNSSD_ENABLE` is disabled.
*
* The service name is "_trel._udp". The platform should use its own hostname, which when combined with the service
* name and the local DNS-SD domain name will produce the full service instance name, for example
* "example-host._trel._udp.local.".
+14 -1
View File
@@ -43,13 +43,26 @@
* @{
*/
/**
* @def OPENTHREAD_CONFIG_TREL_MANAGE_DNSSD_ENABLE
*
* Define as 1 to have the OpenThread core TREL implementation directly manage mDNS (DNS-SD) service registration
* and peer discovery (browse and service/address resolution of TREL service for TREL peer discovery).
*
* When this feature is disabled, the mDNS (DNS-SD) functions are delegated to the platform layer. More details are
* provided in the `platform/trel.h` API documentation.
*/
#ifndef OPENTHREAD_CONFIG_TREL_MANAGE_DNSSD_ENABLE
#define OPENTHREAD_CONFIG_TREL_MANAGE_DNSSD_ENABLE 0
#endif
/**
* @def OPENTHREAD_CONFIG_TREL_USE_HEAP_ENABLE
*
* Define as 1 to allow TREL modules to use heap allocated objects (e.g. for the TREL peer table).
*/
#ifndef OPENTHREAD_CONFIG_TREL_USE_HEAP_ENABLE
#define OPENTHREAD_CONFIG_TREL_USE_HEAP_ENABLE 0
#define OPENTHREAD_CONFIG_TREL_USE_HEAP_ENABLE OPENTHREAD_CONFIG_TREL_MANAGE_DNSSD_ENABLE
#endif
/**
+3 -3
View File
@@ -95,6 +95,9 @@ Instance::Instance(void)
// allow other modules to use it.
, mDnssd(*this)
#endif
#if OPENTHREAD_CONFIG_MULTICAST_DNS_ENABLE
, mMdnsCore(*this)
#endif
#if OPENTHREAD_CONFIG_PLATFORM_KEY_REFERENCES_ENABLE
, mCryptoStorageKeyRefManager(*this)
#endif
@@ -128,9 +131,6 @@ Instance::Instance(void)
#if OPENTHREAD_CONFIG_DNS_DSO_ENABLE
, mDnsDso(*this)
#endif
#if OPENTHREAD_CONFIG_MULTICAST_DNS_ENABLE
, mMdnsCore(*this)
#endif
#if OPENTHREAD_CONFIG_SNTP_CLIENT_ENABLE
, mSntpClient(*this)
#endif
+5 -6
View File
@@ -480,10 +480,13 @@ private:
MessagePool mMessagePool;
#if OPENTHREAD_CONFIG_PLATFORM_DNSSD_ENABLE || OPENTHREAD_CONFIG_MULTICAST_DNS_ENABLE
// DNS-SD (mDNS) platform is initialized early to
// allow other modules to use it.
// DNS-SD platform and mDNS are initialized early to
// allow other modules to use them.
Dnssd mDnssd;
#endif
#if OPENTHREAD_CONFIG_MULTICAST_DNS_ENABLE
Dns::Multicast::Core mMdnsCore;
#endif
#if OPENTHREAD_CONFIG_PLATFORM_KEY_REFERENCES_ENABLE
Crypto::Storage::KeyRefManager mCryptoStorageKeyRefManager;
@@ -529,10 +532,6 @@ private:
Dns::Dso mDnsDso;
#endif
#if OPENTHREAD_CONFIG_MULTICAST_DNS_ENABLE
Dns::Multicast::Core mMdnsCore;
#endif
#if OPENTHREAD_CONFIG_SNTP_CLIENT_ENABLE
Sntp::Client mSntpClient;
#endif
+4
View File
@@ -530,6 +530,10 @@ void Dnssd::HandleStateChange(void)
#if OPENTHREAD_CONFIG_BORDER_AGENT_ENABLE && OPENTHREAD_CONFIG_BORDER_AGENT_MESHCOP_SERVICE_ENABLE
Get<MeshCoP::BorderAgent>().HandleDnssdPlatformStateChange();
#endif
#if OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE && OPENTHREAD_CONFIG_TREL_MANAGE_DNSSD_ENABLE
Get<Trel::PeerDiscoverer>().HandleDnssdPlatformStateChange();
#endif
}
#if OPENTHREAD_CONFIG_MULTICAST_DNS_ENABLE
+2 -1
View File
@@ -114,11 +114,12 @@ Error Core::SetEnabled(bool aEnable, uint32_t aInfraIfIndex)
Error error = kErrorNone;
VerifyOrExit(aEnable != mIsEnabled, error = kErrorAlready);
SuccessOrExit(error = otPlatMdnsSetListeningEnabled(&GetInstance(), aEnable, aInfraIfIndex));
mIsEnabled = aEnable;
mInfraIfIndex = aInfraIfIndex;
SuccessOrExit(error = otPlatMdnsSetListeningEnabled(&GetInstance(), aEnable, aInfraIfIndex));
if (mIsEnabled)
{
LogInfo("Enabling on infra-if-index %lu", ToUlong(mInfraIfIndex));
+6 -1
View File
@@ -127,6 +127,11 @@ Error Interface::Send(Packet &aPacket, bool aIsDiscovery)
Header::AckMode originalAckMode = aPacket.GetHeader().GetAckMode();
Neighbor *neighbor;
if (!peer.IsStateValid())
{
continue;
}
if (!aIsDiscovery && (peer.GetExtPanId() != Get<MeshCoP::ExtendedPanIdManager>().GetExtPanId()))
{
continue;
@@ -151,7 +156,7 @@ Error Interface::Send(Packet &aPacket, bool aIsDiscovery)
case Header::kTypeUnicast:
case Header::kTypeAck:
peerEntry = Get<PeerTable>().FindMatching(aPacket.GetHeader().GetDestination());
VerifyOrExit(peerEntry != nullptr, error = kErrorAbort);
VerifyOrExit((peerEntry != nullptr) && peerEntry->IsStateValid(), error = kErrorAbort);
otPlatTrelSend(&GetInstance(), aPacket.GetBuffer(), aPacket.GetLength(), &peerEntry->mSockAddr);
break;
}
+1 -1
View File
@@ -407,7 +407,7 @@ void Link::CheckPeerAddrOnRxSuccess(PeerSockAddrUpdateMode aMode)
if (aMode == kAllowPeerSockAddrUpdate)
{
LogNote("Updating the peer sock-addr to the newly received");
mRxPacketPeer->SetSockAddr(mRxPacketSenderAddr);
mRxPacketPeer->UpdateSockAddrBasedOnRx(mRxPacketSenderAddr);
}
mPeerDiscoverer.NotifyPeerSocketAddressDifference(prevSockAddr, mRxPacketSenderAddr);
+173 -3
View File
@@ -52,11 +52,23 @@ void Peer::Init(Instance &aInstance)
AsCoreType(&mExtPanId).Clear();
AsCoreType(&mSockAddr).Clear();
#if OPENTHREAD_CONFIG_TREL_MANAGE_DNSSD_ENABLE
mPort = 0;
mExtAddressSet = false;
mResolvingService = false;
mResolvingHost = false;
mTxtDataValidated = false;
mSockAddrUpdatedBasedOnRx = false;
mState = kStateResolving;
#else
mState = kStateValid;
#endif
}
void Peer::Free(void)
{
SignalPeerRemoval();
Log(kDeleted);
#if OPENTHREAD_CONFIG_TREL_USE_HEAP_ENABLE
@@ -67,22 +79,62 @@ void Peer::Free(void)
#endif
}
void Peer::UpdateSockAddrBasedOnRx(const Ip6::SockAddr &aSockAddr)
{
VerifyOrExit(GetSockAddr() != aSockAddr);
SetSockAddr(aSockAddr);
#if OPENTHREAD_CONFIG_TREL_MANAGE_DNSSD_ENABLE
mSockAddrUpdatedBasedOnRx = true;
#endif
Log(kUpdated);
exit:
return;
}
void Peer::ScheduleToRemoveAfter(uint32_t aDelay)
{
VerifyOrExit(IsStateValid());
VerifyOrExit(!IsStateRemoving());
mRemoveTime = TimerMilli::GetNow() + aDelay;
Log(kRemoving);
LogInfo(" after %u msec", aDelay);
SetState(kStateRemoving);
Get<PeerTable>().mTimer.FireAtIfEarlier(mRemoveTime);
Log(kRemoving);
LogInfo(" after %u msec", aDelay);
SignalPeerRemoval();
exit:
return;
}
void Peer::SetExtAddress(const Mac::ExtAddress &aExtAddress)
{
mExtAddress = aExtAddress;
#if OPENTHREAD_CONFIG_TREL_MANAGE_DNSSD_ENABLE
mExtAddressSet = true;
#endif
}
bool Peer::Matches(const Mac::ExtAddress &aExtAddress) const
{
bool matches = false;
#if OPENTHREAD_CONFIG_TREL_MANAGE_DNSSD_ENABLE
VerifyOrExit(mExtAddressSet);
#endif
VerifyOrExit(GetExtAddress() == aExtAddress);
matches = true;
exit:
return matches;
}
bool Peer::Matches(const NonNeighborMatcher &aMatcher) const
{
// Matches only if the peer is not a neighbor. This is used when
@@ -102,14 +154,109 @@ exit:
return matches;
}
#if OPENTHREAD_CONFIG_TREL_MANAGE_DNSSD_ENABLE
void Peer::SignalPeerRemoval(void)
{
// Signal to `PeerDiscoverer` that peer is removed and/or about to be
// freed. The `PeerDiscoverer` will then stop any active resolvers
// associated with this peer.
Get<PeerDiscoverer>().HandlePeerRemoval(*this);
}
void Peer::SetPort(uint16_t aPort)
{
VerifyOrExit(mPort != aPort);
mPort = aPort;
if (mPort != 0)
{
AsCoreType(&mSockAddr).SetPort(mPort);
}
exit:
return;
}
bool Peer::Matches(const ServiceNameMatcher &aMatcher) const { return NameMatch(mServiceName, aMatcher.mServiceName); }
bool Peer::Matches(const HostNameMatcher &aMatcher) const
{
return mResolvingHost && NameMatch(mHostName, aMatcher.mHostName);
}
bool Peer::NameMatch(const Heap::String &aHeapString, const char *aName)
{
// Compares a DNS name given as a `Heap::String` with a
// `aName` C string.
return !aHeapString.IsNull() && StringMatch(aHeapString.AsCString(), aName, kStringCaseInsensitiveMatch);
}
#endif
#if OT_SHOULD_LOG_AT(OT_LOG_LEVEL_INFO)
#if OPENTHREAD_CONFIG_TREL_MANAGE_DNSSD_ENABLE
const char *Peer::StateToString(State aState)
{
static const char *const kStateStrings[] = {
"valid", // (0) kStateValid
"removing", // (1) kStateRemoving
"resolving", // (2) kStateResolving
};
struct EnumCheck
{
InitEnumValidatorCounter();
ValidateNextEnum(kStateValid);
ValidateNextEnum(kStateRemoving);
ValidateNextEnum(kStateResolving);
};
return kStateStrings[aState];
}
void Peer::Log(Action aAction) const
{
LogInfo("%s peer %s, state:%s", ActionToString(aAction),
mServiceName.IsNull() ? "(null)" : mServiceName.AsCString(), StateToString(mState));
if (!mHostName.IsNull())
{
LogInfo(" host:%s, port:%u", mHostName.AsCString(), mPort);
if (mHostAddresses.GetLength() > 0)
{
LogInfo(" num-addrs:%u, %s", mHostAddresses.GetLength(), mHostAddresses[0].ToString().AsCString());
}
}
if (mExtAddressSet)
{
LogInfo(" ext-addr:%s, ext-panid:%s", GetExtAddress().ToString().AsCString(),
GetExtPanId().ToString().AsCString());
}
if (!GetSockAddr().GetAddress().IsUnspecified())
{
LogInfo(" sock-addr:%s, based-on-rx:%s", GetSockAddr().ToString().AsCString(),
ToYesNo(mSockAddrUpdatedBasedOnRx));
}
}
#else
void Peer::Log(Action aAction) const
{
LogInfo("%s peer mac:%s, xpan:%s, %s", ActionToString(aAction), GetExtAddress().ToString().AsCString(),
GetExtPanId().ToString().AsCString(), GetSockAddr().ToString().AsCString());
}
#endif
const char *Peer::ActionToString(Action aAction)
{
static const char *const kActionStrings[] = {
@@ -137,6 +284,29 @@ const char *Peer::ActionToString(Action aAction)
#endif // OT_SHOULD_LOG_AT(OT_LOG_LEVEL_INFO)
#if OPENTHREAD_CONFIG_TREL_MANAGE_DNSSD_ENABLE
//---------------------------------------------------------------------------------------------------------------------
// Peer::AddressArray
void Peer::AddressArray::CloneFrom(const AddressArray &aOtherArray)
{
Free();
VerifyOrExit(!aOtherArray.IsEmpty());
SuccessOrAssert(ReserveCapacity(aOtherArray.GetLength()));
for (const Ip6::Address &addr : aOtherArray)
{
SuccessOrAssert(PushBack(addr));
}
exit:
return;
}
#endif
//---------------------------------------------------------------------------------------------------------------------
// PeerTable
+100 -6
View File
@@ -43,6 +43,9 @@
#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/log.hpp"
#include "common/non_copyable.hpp"
@@ -83,6 +86,14 @@ class Peer : public InstanceLocatorInit,
friend class LinkedListEntry<Peer>;
public:
/**
* Indicates whether the `Peer` is in valid state.
*
* @returns TRUE If the `peer` is in valid state.
* @returns FALSE If the `Peer` is not in valid state (e.g., still being resolved or scheduled for removal).
*/
bool IsStateValid(void) const { return mState == kStateValid; }
/**
* Returns the Extended MAC Address of the discovered TREL peer.
*
@@ -105,17 +116,45 @@ public:
const Ip6::SockAddr &GetSockAddr(void) const { return AsCoreType(&mSockAddr); }
/**
* Set the IPv6 socket address of the discovered TREL peer.
* Updates the IPv6 socket address of the discovered TREL peer based on a received message from peer.
*
* @param[in] aSockAddr The IPv6 socket address.
* @param[in] aSockAddr The IPv6 socket address discovered based on received message from the peer
*/
void SetSockAddr(const Ip6::SockAddr &aSockAddr) { mSockAddr = aSockAddr; }
void UpdateSockAddrBasedOnRx(const Ip6::SockAddr &aSockAddr);
#if OPENTHREAD_CONFIG_TREL_MANAGE_DNSSD_ENABLE
/**
* Returns the TREL peer service name (service instance label).
*
* @returns The peer service name, or `nullptr` if not yet known.
*/
const char *GetServiceName(void) const { return mServiceName.AsCString(); }
/**
* Returns the host name of the discovered TREL peer.
*
* @returns The host name as a null-terminated C string, or `nullptr` if not yet known.
*/
const char *GetHostName(void) const { return mHostName.AsCString(); }
/**
* Returns the list of discovered IPv6 host addresses for the TREL peer.
*
* The array is sorted to place preferred addresses at the top of the list.
*
* @returns An array of IPv6 addresses.
*/
const Heap::Array<Ip6::Address> &GetHostAddresses(void) const { return mHostAddresses; }
#endif // OPENTHREAD_CONFIG_TREL_MANAGE_DNSSD_ENABLE
private:
enum State : uint8_t
{
kStateValid,
kStateRemoving,
kStateResolving,
};
enum Action : uint8_t
@@ -158,24 +197,68 @@ private:
TimeMilli mNow;
};
#if OPENTHREAD_CONFIG_TREL_MANAGE_DNSSD_ENABLE
struct ServiceNameMatcher
{
explicit ServiceNameMatcher(const char *SrerviceName)
: mServiceName(SrerviceName)
{
}
const char *mServiceName;
};
struct HostNameMatcher
{
explicit HostNameMatcher(const char *aHostName)
: mHostName(aHostName)
{
}
const char *mHostName;
};
class AddressArray : public Heap::Array<Ip6::Address>
{
public:
bool IsEmpty(void) const { return (GetLength() == 0); }
void CloneFrom(const AddressArray &aOtherArray);
};
#endif
void Init(Instance &aInstance);
void Free(void);
void SetState(State aState) { mState = aState; }
bool IsStateValid(void) const { return mState == kStateValid; }
bool IsStateRemoving(void) const { return mState == kStateRemoving; }
void SetExtAddress(const Mac::ExtAddress &aExtAddress) { mExtAddress = aExtAddress; }
bool IsStateResolving(void) const { return mState == kStateResolving; }
void SetExtAddress(const Mac::ExtAddress &aExtAddress);
void SetExtPanId(const MeshCoP::ExtendedPanId &aExtPanId) { mExtPanId = aExtPanId; }
void SetSockAddr(const Ip6::SockAddr &aSockAddr) { mSockAddr = aSockAddr; }
void ScheduleToRemoveAfter(uint32_t aDelay);
bool Matches(const Mac::ExtAddress &aExtAddress) const { return GetExtAddress() == aExtAddress; }
bool Matches(const Mac::ExtAddress &aExtAddress) const;
bool Matches(const Ip6::SockAddr &aSockAddr) const { return GetSockAddr() == aSockAddr; }
bool Matches(State aState) const { return mState == aState; }
bool Matches(const Peer &aPeer) const { return this == &aPeer; }
bool Matches(const OtherExtPanIdMatcher &aMatcher) const { return GetExtPanId() != aMatcher.mExtPanId; }
bool Matches(const NonNeighborMatcher &aMatcher) const;
bool Matches(const ExpireChecker &aChecker) const { return IsStateRemoving() && (aChecker.mNow >= mRemoveTime); }
#if OPENTHREAD_CONFIG_TREL_MANAGE_DNSSD_ENABLE
void SetPort(uint16_t aPort);
bool Matches(const ServiceNameMatcher &aMatcher) const;
bool Matches(const HostNameMatcher &aMatcher) const;
void SignalPeerRemoval(void);
static bool NameMatch(const Heap::String &aHeapString, const char *aName);
#else
void SignalPeerRemoval(void) {}
#endif
#if OT_SHOULD_LOG_AT(OT_LOG_LEVEL_INFO)
void Log(Action aAction) const;
static const char *ActionToString(Action aAction);
static const char *StateToString(State aState);
#else
void Log(Action) const {}
#endif
@@ -183,6 +266,17 @@ private:
Peer *mNext;
State mState;
TimeMilli mRemoveTime;
#if OPENTHREAD_CONFIG_TREL_MANAGE_DNSSD_ENABLE
bool mExtAddressSet : 1;
bool mResolvingService : 1;
bool mResolvingHost : 1;
bool mTxtDataValidated : 1;
bool mSockAddrUpdatedBasedOnRx : 1;
uint16_t mPort;
Heap::String mServiceName;
Heap::String mHostName;
AddressArray mHostAddresses;
#endif
};
//---------------------------------------------------------------------------------------------------------------------
+683 -16
View File
@@ -41,19 +41,42 @@ namespace Trel {
RegisterLogModule("TrelDiscoverer");
#if OPENTHREAD_CONFIG_TREL_MANAGE_DNSSD_ENABLE
const char PeerDiscoverer::kTrelServiceType[] = "_trel._udp";
#endif
PeerDiscoverer::PeerDiscoverer(Instance &aInstance)
: InstanceLocator(aInstance)
, mIsRunning(false)
, mRegisterServiceTask(aInstance)
, mState(kStateStopped)
, mServiceTask(aInstance)
#if OPENTHREAD_CONFIG_TREL_MANAGE_DNSSD_ENABLE
, mServiceName(aInstance)
, mBrowsing(false)
#endif
{
}
PeerDiscoverer::~PeerDiscoverer(void)
{
mState = kStateStopped;
Get<PeerTable>().Clear();
}
void PeerDiscoverer::Start(void)
{
VerifyOrExit(!mIsRunning);
VerifyOrExit(mState == kStateStopped);
mIsRunning = true;
PostRegisterServiceTask();
#if OPENTHREAD_CONFIG_TREL_MANAGE_DNSSD_ENABLE
if (!Get<Dnssd>().IsReady())
{
mState = kStatePendingDnssd;
}
else
#endif
{
mState = kStateRunning;
PostServiceTask();
}
exit:
return;
@@ -61,11 +84,21 @@ exit:
void PeerDiscoverer::Stop(void)
{
VerifyOrExit(mIsRunning);
VerifyOrExit(mState != kStateStopped);
mIsRunning = false;
mState = kStateStopped;
Get<PeerTable>().Clear();
#if OPENTHREAD_CONFIG_TREL_MANAGE_DNSSD_ENABLE
UnregisterService();
if (mBrowsing)
{
mBrowsing = false;
Get<Dnssd>().StopBrowser(Browser());
}
#endif
exit:
return;
}
@@ -76,32 +109,51 @@ void PeerDiscoverer::NotifyPeerSocketAddressDifference(const Ip6::SockAddr &aPee
otPlatTrelNotifyPeerSocketAddressDifference(&GetInstance(), &aPeerSockAddr, &aRxSockAddr);
}
void PeerDiscoverer::PostRegisterServiceTask(void)
void PeerDiscoverer::PostServiceTask(void)
{
if (mIsRunning)
if (IsRunning())
{
mRegisterServiceTask.Post();
mServiceTask.Post();
}
}
void PeerDiscoverer::HandleServiceTask(void)
{
VerifyOrExit(IsRunning());
RegisterService();
#if OPENTHREAD_CONFIG_TREL_MANAGE_DNSSD_ENABLE
if (!mBrowsing)
{
mBrowsing = true;
Get<Dnssd>().StartBrowser(Browser());
}
#endif
exit:
return;
}
void PeerDiscoverer::RegisterService(void)
{
TxtDataEncoder txtData(GetInstance());
uint16_t port;
VerifyOrExit(mIsRunning);
port = Get<Interface>().GetUdpPort();
txtData.Encode();
#if OPENTHREAD_CONFIG_TREL_MANAGE_DNSSD_ENABLE
RegisterService(port, txtData);
#else
LogInfo("Registering DNS-SD service: port:%u", port);
otPlatTrelRegisterService(&GetInstance(), port, txtData.GetBytes(), static_cast<uint8_t>(txtData.GetLength()));
exit:
return;
#endif
}
#if !OPENTHREAD_CONFIG_TREL_MANAGE_DNSSD_ENABLE
extern "C" void otPlatTrelHandleDiscoveredPeerInfo(otInstance *aInstance, const otPlatTrelPeerInfo *aInfo)
{
Instance &instance = AsCoreType(aInstance);
@@ -121,7 +173,7 @@ void PeerDiscoverer::HandleDiscoveredPeerInfo(const PeerInfo &aInfo)
Peer::Action action = Peer::kUpdated;
bool shouldLog = true;
VerifyOrExit(mIsRunning);
VerifyOrExit(IsRunning());
txtData.Init(aInfo.mTxtData, aInfo.mTxtLength);
SuccessOrExit(txtData.Decode(txtInfo));
@@ -186,6 +238,493 @@ exit:
return;
}
#endif // !OPENTHREAD_CONFIG_TREL_MANAGE_DNSSD_ENABLE
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
#if OPENTHREAD_CONFIG_TREL_MANAGE_DNSSD_ENABLE
void PeerDiscoverer::HandleDnssdPlatformStateChange(void)
{
if (Get<Dnssd>().IsReady())
{
VerifyOrExit(mState == kStatePendingDnssd);
mState = kStateRunning;
PostServiceTask();
}
else
{
VerifyOrExit(mState != kStateStopped);
mState = kStatePendingDnssd;
mBrowsing = false;
Get<PeerTable>().Clear();
}
exit:
return;
}
void PeerDiscoverer::RegisterService(uint16_t aPort, const TxtData &aTxtData)
{
Dnssd::Service service;
service.Clear();
service.mServiceType = kTrelServiceType;
service.mServiceInstance = mServiceName.GetName();
service.mTxtData = aTxtData.GetBytes();
service.mTxtDataLength = aTxtData.GetLength();
service.mPort = aPort;
LogInfo("Registering service %s.%s", service.mServiceInstance, kTrelServiceType);
LogInfo(" port:%u, ext-addr:%s, ext-panid:%s", aPort, Get<Mac::Mac>().GetExtAddress().ToString().AsCString(),
Get<MeshCoP::ExtendedPanIdManager>().GetExtPanId().ToString().AsCString());
Get<Dnssd>().RegisterService(service, /* aRequestId */ 0, HandleRegisterDone);
}
void PeerDiscoverer::UnregisterService(void)
{
Dnssd::Service service;
service.Clear();
service.mServiceType = kTrelServiceType;
service.mServiceInstance = mServiceName.GetName();
Get<Dnssd>().UnregisterService(service, /* aRequestId */ 0, /* aCallback */ nullptr);
}
void PeerDiscoverer::HandleRegisterDone(otInstance *aInstance, Dnssd::RequestId aRequestId, otError aError)
{
OT_UNUSED_VARIABLE(aRequestId);
AsCoreType(aInstance).Get<PeerDiscoverer>().HandleRegisterDone(aError);
}
void PeerDiscoverer::HandleRegisterDone(Error aError)
{
VerifyOrExit(IsRunning());
if (aError == kErrorNone)
{
LogInfo("DNS-SD service registered successfully");
}
else
{
LogInfo("Failed to register DNS-SD service with name:%s, Error:%s", mServiceName.GetName(),
ErrorToString(aError));
UnregisterService();
// Generate a new name (appending a suffix index to the name)
// and try again.
mServiceName.GenerateName();
PostServiceTask();
}
exit:
return;
}
void PeerDiscoverer::HandleBrowseResult(otInstance *aInstance, const otPlatDnssdBrowseResult *aResult)
{
AsCoreType(aInstance).Get<PeerDiscoverer>().HandleBrowseResult(*aResult);
}
void PeerDiscoverer::HandleBrowseResult(const Dnssd::BrowseResult &aResult)
{
Peer *peer;
VerifyOrExit(IsRunning());
peer = Get<PeerTable>().FindMatching(Peer::ServiceNameMatcher(aResult.mServiceInstance));
if (aResult.mTtl == 0)
{
// Previously discovered service is now removed.
VerifyOrExit(peer != nullptr);
peer->ScheduleToRemoveAfter(kRemoveDelay);
}
else
{
// A new service is discovered.
Peer::Action action = Peer::kAdded;
if (peer == nullptr)
{
peer = Get<PeerTable>().AllocateAndAddNewPeer();
VerifyOrExit(peer != nullptr);
SuccessOrAssert(peer->mServiceName.Set(aResult.mServiceInstance));
}
else
{
action = Peer::kReAdded;
}
peer->SetState(Peer::kStateResolving);
peer->Log(action);
StartServiceResolvers(*peer);
}
exit:
return;
}
void PeerDiscoverer::StartServiceResolvers(Peer &aPeer)
{
VerifyOrExit(!aPeer.mResolvingService);
aPeer.mResolvingService = true;
Get<Dnssd>().StartSrvResolver(SrvResolver(aPeer));
Get<Dnssd>().StartTxtResolver(TxtResolver(aPeer));
exit:
return;
}
void PeerDiscoverer::StopServiceResolvers(Peer &aPeer)
{
VerifyOrExit(aPeer.mResolvingService);
aPeer.mResolvingService = false;
aPeer.mTxtDataValidated = false;
aPeer.mPort = 0;
aPeer.mHostName.Free();
Get<Dnssd>().StopSrvResolver(SrvResolver(aPeer));
Get<Dnssd>().StopTxtResolver(TxtResolver(aPeer));
exit:
return;
}
void PeerDiscoverer::HandleSrvResult(otInstance *aInstance, const otPlatDnssdSrvResult *aResult)
{
AsCoreType(aInstance).Get<PeerDiscoverer>().HandleSrvResult(*aResult);
}
void PeerDiscoverer::HandleSrvResult(const Dnssd::SrvResult &aResult)
{
Peer *peer;
VerifyOrExit(IsRunning());
peer = Get<PeerTable>().FindMatching(Peer::ServiceNameMatcher(aResult.mServiceInstance));
VerifyOrExit(peer != nullptr);
if (aResult.mTtl == 0)
{
peer->SetPort(0);
StopHostAddressResolver(*peer);
}
else
{
peer->SetPort(aResult.mPort);
if (!Peer::NameMatch(peer->mHostName, aResult.mHostName))
{
StopHostAddressResolver(*peer);
SuccessOrAssert(peer->mHostName.Set(aResult.mHostName));
StartHostAddressResolver(*peer);
}
}
UpdatePeerState(*peer);
exit:
return;
}
void PeerDiscoverer::HandleTxtResult(otInstance *aInstance, const otPlatDnssdTxtResult *aResult)
{
AsCoreType(aInstance).Get<PeerDiscoverer>().HandleTxtResult(*aResult);
}
void PeerDiscoverer::HandleTxtResult(const Dnssd::TxtResult &aResult)
{
Peer *peer;
VerifyOrExit(IsRunning());
peer = Get<PeerTable>().FindMatching(Peer::ServiceNameMatcher(aResult.mServiceInstance));
VerifyOrExit(peer != nullptr);
ProcessPeerTxtData(aResult, *peer);
UpdatePeerState(*peer);
exit:
return;
}
void PeerDiscoverer::ProcessPeerTxtData(const Dnssd::TxtResult &aResult, Peer &aPeer)
{
TxtData txtData;
TxtData::Info txtInfo;
aPeer.mTxtDataValidated = false;
VerifyOrExit(aResult.mTtl != 0);
txtData.Init(aResult.mTxtData, aResult.mTxtDataLength);
SuccessOrExit(txtData.Decode(txtInfo));
if (txtInfo.mExtAddress == Get<Mac::Mac>().GetExtAddress())
{
LogInfo("Peer %s is this device itself", aPeer.mServiceName.AsCString());
aPeer.ScheduleToRemoveAfter(0);
ExitNow();
}
aPeer.SetExtPanId(txtInfo.mExtPanId);
if (aPeer.GetExtAddress() != txtInfo.mExtAddress)
{
// Remove any peer that is associated with the same ExtAddress.
// These are likely stale entries. This ensure we have at most
// one entry associated with an `ExtAddress`.
Get<PeerTable>().RemoveAndFreeAllMatching(txtInfo.mExtAddress);
aPeer.SetExtAddress(txtInfo.mExtAddress);
}
aPeer.mTxtDataValidated = true;
exit:
return;
}
void PeerDiscoverer::StartHostAddressResolver(Peer &aPeer)
{
Peer *sameHostPeer;
VerifyOrExit(!aPeer.mResolvingHost);
sameHostPeer = Get<PeerTable>().FindMatching(Peer::HostNameMatcher(aPeer.mHostName.AsCString()));
aPeer.mResolvingHost = true;
if (sameHostPeer != nullptr)
{
UpdatePeerAddresses(aPeer, sameHostPeer->mHostAddresses);
}
else
{
Get<Dnssd>().StartIp6AddressResolver(AddressResolver(aPeer));
}
exit:
return;
}
void PeerDiscoverer::StopHostAddressResolver(Peer &aPeer)
{
// We check if any `Peer` in the table is associated with the
// same host name before we decide whether we stop the
// `AddressResolver` for the given host name.
VerifyOrExit(aPeer.mResolvingHost);
aPeer.mResolvingHost = false;
aPeer.mHostAddresses.Free();
VerifyOrExit(!Get<PeerTable>().ContainsMatching(Peer::HostNameMatcher(aPeer.mHostName.AsCString())));
Get<Dnssd>().StopIp6AddressResolver(AddressResolver(aPeer));
exit:
return;
}
void PeerDiscoverer::HandleAddressResult(otInstance *aInstance, const otPlatDnssdAddressResult *aResult)
{
AsCoreType(aInstance).Get<PeerDiscoverer>().HandleAddressResult(*aResult);
}
void PeerDiscoverer::HandleAddressResult(const Dnssd::AddressResult &aResult)
{
Peer::AddressArray sortedAddresses;
VerifyOrExit(IsRunning());
while (true)
{
// Iterate through addresses in `aResult`, adding them one by
// one to `sortedAddresses` such that more favored addresses
// are placed at the beginning of the array.
AddrAndTtl favored;
favored.Clear();
for (uint16_t index = 0; index < aResult.mAddressesLength; index++)
{
const Dnssd::AddressAndTtl &newAddrAndTtl = aResult.mAddresses[index];
const Ip6::Address &newAddr = AsCoreType(&aResult.mAddresses[index].mAddress);
uint32_t newTtl = aResult.mAddresses[index].mTtl;
// Skip the address if it is already in the `sortedAddresses`
// list or if the address is invalid(e.g., zero TTL or
// multicast). Then check if it is favored over the current
// `favored` selection and update `favored` accordingly.
if (sortedAddresses.Contains(newAddr))
{
continue;
}
if ((newTtl == 0) || newAddr.IsUnspecified() || newAddr.IsLoopback() || newAddr.IsMulticast())
{
continue;
}
if (favored.IsFavoredOver(newAddrAndTtl))
{
continue;
}
favored.SetFrom(newAddrAndTtl);
}
if (favored.IsEmpty())
{
break;
}
SuccessOrAssert(sortedAddresses.PushBack(favored.mAddress));
}
// Update the addresses of all `Peer`s that are associated with
// the resolved `aResult.mHostName`.
//
// This handles the case where multiple TREL services may be
// present on the same host machine. While this is unlikely in
// actual deployments, it can be useful for testing and
// simulation where a single machine may be acting as multiple
// Thread nodes thus advertising multiple TREL services from the
// same host name.
for (Peer &peer : Get<PeerTable>())
{
if (peer.IsStateRemoving())
{
continue;
}
if (peer.Matches(Peer::HostNameMatcher(aResult.mHostName)))
{
UpdatePeerAddresses(peer, sortedAddresses);
UpdatePeerState(peer);
}
}
exit:
return;
}
void PeerDiscoverer::UpdatePeerAddresses(Peer &aPeer, const Peer::AddressArray &aSortedAddresses)
{
// Updates `aPeer.mHostAddresses` and decides whether to update
// `aPeer.mSockAddr`(the primary address used for communication
// with the peer).
bool shouldChangeSockAddr = false;
// If the new `aSortedAddresses` is empty, clear
// `aPeer.mHostAddresses` but leave `aPeer.mSockAddr`
// unchanged (retaining the last known good address).
if (aSortedAddresses.IsEmpty())
{
aPeer.mHostAddresses.Clear();
ExitNow();
}
// Determine if `aPeer.mSockAddr` should be updated: The goal is
// to use the most stable address, preferring one learned from a
// received packet if mDNS still considers it valid or if mDNS
// information is unstable.
//
// If `mSockAddr` was not set by a received packet, then the
// `mSockAddr` was last set by a previous mDNS resolution. Always
// update it with the new address `aSortedAddresses[0]`.
//
// If `mSockAddr` was previously set by a received packet
// (`aPeer.mSockAddrUpdatedBasedOnRx` is true):
//
// - If the current `mSockAddr` is in the `aSortedAddresses` list
// then we keep the current `mSockAddr`. It's packet-verified
// and now mDNS-confirmed.
//
// - If the current `mSockAddr` (from rx packet) is not present in
// the `aSortedAddresses`, then we only update and use
// `aSortedAddresses[0]` if this differs from the previous one
// discovered through mDNS resolution. This approach avoids
// changing a working, rx-verified address due to transient
// mDNS issues. If we see a change to the list of addresses
// reported through mDNS, we can be sure that an mDNS answer
// was indeed received and processed (which updated the list,
// so we know the list is most likely more recent and correct)
if (!aPeer.mSockAddrUpdatedBasedOnRx)
{
shouldChangeSockAddr = true;
}
else if (!aSortedAddresses.Contains(aPeer.GetSockAddr().GetAddress()))
{
const Ip6::Address *prevFront = aPeer.mHostAddresses.Front();
if ((prevFront == nullptr) || (*prevFront != aSortedAddresses[0]))
{
shouldChangeSockAddr = true;
}
}
if (shouldChangeSockAddr && (aPeer.GetSockAddr().GetAddress() != aSortedAddresses[0]))
{
AsCoreType(&aPeer.mSockAddr).SetAddress(aSortedAddresses[0]);
aPeer.mSockAddrUpdatedBasedOnRx = false;
}
aPeer.mHostAddresses.CloneFrom(aSortedAddresses);
exit:
return;
}
void PeerDiscoverer::UpdatePeerState(Peer &aPeer)
{
VerifyOrExit(aPeer.IsStateResolving());
VerifyOrExit(aPeer.mResolvingService && aPeer.mResolvingHost);
VerifyOrExit(aPeer.mTxtDataValidated);
VerifyOrExit(aPeer.mPort != 0);
VerifyOrExit(aPeer.mHostAddresses.GetLength() > 0);
aPeer.SetState(Peer::kStateValid);
aPeer.Log(Peer::kUpdated);
exit:
return;
}
void PeerDiscoverer::HandlePeerRemoval(Peer &aPeer)
{
// Callback from `Peer` signaling that peer is being removed
// or scheduled to be removed. We stop any active resolvers
// associated with this peer.
//
// The order of calls is important here since the
// `StopServiceResolvers()` clears the `aPeer.mHostName` which
// would be needed in `StopHostAddressResolver()`.
StopHostAddressResolver(aPeer);
StopServiceResolvers(aPeer);
}
#endif // OPENTHREAD_CONFIG_TREL_MANAGE_DNSSD_ENABLE
//----------------------------------------------------------------------------------------------------------------------
// PeerDiscoverer::TxtData
@@ -267,6 +806,134 @@ void PeerDiscoverer::TxtDataEncoder::Encode(void)
mLength = encoder.GetLength();
}
#if OPENTHREAD_CONFIG_TREL_MANAGE_DNSSD_ENABLE
//----------------------------------------------------------------------------------------------------------------------
// PeerDiscoverer::ServiceName
const char PeerDiscoverer::ServiceName::kNamePrefix[] = "otTREL";
PeerDiscoverer::ServiceName::ServiceName(Instance &aInstance)
: InstanceLocator(aInstance)
, mSuffixIndex(0)
{
ClearAllBytes(mName);
}
const char *PeerDiscoverer::ServiceName::GetName(void)
{
if (mName[0] == kNullChar)
{
GenerateName();
}
return mName;
}
void PeerDiscoverer::ServiceName::GenerateName(void)
{
StringWriter writer(mName, sizeof(mName));
writer.Append("%s%s", kNamePrefix, Get<Mac::Mac>().GetExtAddress().ToString().AsCString());
if (mSuffixIndex != 0)
{
writer.Append("(%u)", mSuffixIndex);
}
mSuffixIndex++;
}
//----------------------------------------------------------------------------------------------------------------------
// PeerDiscoverer::Browser
PeerDiscoverer::Browser::Browser(void)
{
Clear();
mServiceType = kTrelServiceType;
mCallback = PeerDiscoverer::HandleBrowseResult;
}
//----------------------------------------------------------------------------------------------------------------------
// PeerDiscoverer::SrvResolver
PeerDiscoverer::SrvResolver::SrvResolver(const Peer &aPeer)
{
Clear();
mServiceInstance = aPeer.mServiceName.AsCString();
mServiceType = kTrelServiceType;
mCallback = PeerDiscoverer::HandleSrvResult;
}
//----------------------------------------------------------------------------------------------------------------------
// PeerDiscoverer::TxtResolver
PeerDiscoverer::TxtResolver::TxtResolver(const Peer &aPeer)
{
Clear();
mServiceInstance = aPeer.mServiceName.AsCString();
mServiceType = kTrelServiceType;
mCallback = PeerDiscoverer::HandleTxtResult;
}
//----------------------------------------------------------------------------------------------------------------------
// PeerDiscoverer::AddressResolver
PeerDiscoverer::AddressResolver::AddressResolver(const Peer &aPeer)
{
Clear();
mHostName = aPeer.mHostName.AsCString();
mCallback = PeerDiscoverer::HandleAddressResult;
}
//----------------------------------------------------------------------------------------------------------------------
// PeerDiscoverer::AddrAndTtl
void PeerDiscoverer::AddrAndTtl::SetFrom(const Dnssd::AddressAndTtl &aAddrAndTtl)
{
mAddress = AsCoreType(&aAddrAndTtl.mAddress);
mTtl = aAddrAndTtl.mTtl;
}
bool PeerDiscoverer::AddrAndTtl::IsFavoredOver(const Dnssd::AddressAndTtl &aAddrAndTtl) const
{
int compare;
const Ip6::Address &newAddress = AsCoreType(&aAddrAndTtl.mAddress);
uint32_t newTtl = aAddrAndTtl.mTtl;
Ip6::Prefix prefix;
Ip6::Prefix newPrefix;
if (IsEmpty())
{
// Prefer any address when empty.
ExitNow(compare = -1);
}
// Prefer a link-local address over non-link-local.
compare = ThreeWayCompare(mAddress.IsLinkLocalUnicast(), newAddress.IsLinkLocalUnicast());
VerifyOrExit(compare == 0);
// Prefer non-ULA address over ULA address
mAddress.GetPrefix(Ip6::NetworkPrefix::kLength, prefix);
newAddress.GetPrefix(Ip6::NetworkPrefix::kLength, newPrefix);
compare = ThreeWayCompare(!prefix.IsUniqueLocal(), !newPrefix.IsUniqueLocal());
VerifyOrExit(compare == 0);
// Prefer address with longer TTL.
compare = ThreeWayCompare(mTtl, newTtl);
VerifyOrExit(compare == 0);
compare = (mAddress < newAddress) ? 1 : 0;
exit:
return (compare > 0);
}
#endif // OPENTHREAD_CONFIG_TREL_MANAGE_DNSSD_ENABLE
} // namespace Trel
} // namespace ot
+141 -7
View File
@@ -43,8 +43,22 @@
#include "common/clearable.hpp"
#include "common/locator.hpp"
#include "common/tasklet.hpp"
#include "net/dns_types.hpp"
#include "net/dnssd.hpp"
#include "radio/trel_peer.hpp"
#if OPENTHREAD_CONFIG_TREL_MANAGE_DNSSD_ENABLE
#if !(OPENTHREAD_CONFIG_PLATFORM_DNSSD_ENABLE || OPENTHREAD_CONFIG_MULTICAST_DNS_ENABLE)
#error "OPENTHREAD_CONFIG_TREL_MANAGE_DNSSD_ENABLE requires either the native mDNS or platform DNS-SD APIs"
#endif
#if !OPENTHREAD_CONFIG_TREL_USE_HEAP_ENABLE
#error "OPENTHREAD_CONFIG_TREL_MANAGE_DNSSD_ENABLE requires OPENTHREAD_CONFIG_TREL_USE_HEAP_ENABLE"
#endif
#endif
namespace ot {
namespace Trel {
@@ -58,7 +72,12 @@ extern "C" void otPlatTrelHandleDiscoveredPeerInfo(otInstance *aInstance, const
class PeerDiscoverer : public InstanceLocator
{
friend class Link;
#if OPENTHREAD_CONFIG_TREL_MANAGE_DNSSD_ENABLE
friend class ot::Dnssd;
friend class Peer;
#else
friend void otPlatTrelHandleDiscoveredPeerInfo(otInstance *aInstance, const otPlatTrelPeerInfo *aInfo);
#endif
public:
/**
@@ -74,12 +93,12 @@ public:
/**
* Notifies that device's Extended MAC Address has changed.
*/
void HandleExtAddressChange(void) { PostRegisterServiceTask(); }
void HandleExtAddressChange(void) { PostServiceTask(); }
/**
* Notifies that device's Extended PAN Identifier has changed.
*/
void HandleExtPanIdChange(void) { PostRegisterServiceTask(); }
void HandleExtPanIdChange(void) { PostServiceTask(); }
/**
* Notifies that a TREL packet is received from a peer using a different socket address than the one reported
@@ -90,9 +109,25 @@ public:
*/
void NotifyPeerSocketAddressDifference(const Ip6::SockAddr &aPeerSockAddr, const Ip6::SockAddr &aRxSockAddr);
#if OPENTHREAD_CONFIG_TREL_MANAGE_DNSSD_ENABLE
/**
* Returns the TREL service name (service instance label) used by the device itself when advertising TREL service.
*
* @returns The TREL service name.
*/
const char *GetServiceName(void) { return mServiceName.GetName(); }
#endif
private:
static constexpr uint32_t kRemoveDelay = 7 * Time::kOneSecondInMsec;
enum State : uint8_t
{
kStateStopped, // Stopped.
kStatePendingDnssd, // Started but waiting for `Dnssd` to be ready.
kStateRunning, // Started and `Dnssd` is also ready.
};
class TxtData
{
public:
@@ -133,22 +168,121 @@ private:
uint8_t mBuffer[kMaxSize];
};
#if OPENTHREAD_CONFIG_TREL_MANAGE_DNSSD_ENABLE
class ServiceName : public InstanceLocator
{
// Tracks the service name (instance label) to use when
// registering TREL mDNS service
public:
explicit ServiceName(Instance &aInstance);
const char *GetName(void);
void GenerateName(void);
private:
static const char kNamePrefix[];
Dns::Name::LabelBuffer mName;
uint8_t mSuffixIndex;
};
class Browser : public Dnssd::Browser
{
public:
Browser(void);
};
class SrvResolver : public Dnssd::SrvResolver
{
public:
explicit SrvResolver(const Peer &aPeer);
};
class TxtResolver : public Dnssd::TxtResolver
{
public:
explicit TxtResolver(const Peer &aPeer);
};
class AddressResolver : public Dnssd::AddressResolver
{
public:
explicit AddressResolver(const Peer &aPeer);
};
struct AddrAndTtl : public Clearable<AddrAndTtl>
{
void SetFrom(const Dnssd::AddressAndTtl &aAddrAndTtl);
bool IsFavoredOver(const Dnssd::AddressAndTtl &aAddrAndTtl) const;
bool IsEmpty(void) const { return mTtl == 0; }
Ip6::Address mAddress;
uint32_t mTtl;
};
#endif // OPENTHREAD_CONFIG_TREL_MANAGE_DNSSD_ENABLE
#if !OPENTHREAD_CONFIG_TREL_MANAGE_DNSSD_ENABLE
struct PeerInfo : public otPlatTrelPeerInfo
{
bool IsRemoved(void) const { return mRemoved; }
const Ip6::SockAddr &GetSockAddr(void) const { return AsCoreType(&mSockAddr); }
};
#endif
explicit PeerDiscoverer(Instance &aInstance);
~PeerDiscoverer(void);
void HandleDiscoveredPeerInfo(const PeerInfo &aInfo);
void PostRegisterServiceTask(void);
bool IsRunning(void) const { return mState == kStateRunning; }
void PostServiceTask(void);
void HandleServiceTask(void);
void RegisterService(void);
using RegisterServiceTask = TaskletIn<PeerDiscoverer, &PeerDiscoverer::RegisterService>;
#if OPENTHREAD_CONFIG_TREL_MANAGE_DNSSD_ENABLE
// Callback from `Dnssd`
void HandleDnssdPlatformStateChange(void);
// Callback from `Peer` class.
void HandlePeerRemoval(Peer &aPeer);
bool mIsRunning;
RegisterServiceTask mRegisterServiceTask;
void RegisterService(uint16_t aPort, const TxtData &aTxtData);
void UnregisterService(void);
void HandleRegisterDone(Error aError);
void HandleBrowseResult(const Dnssd::BrowseResult &aResult);
void StartServiceResolvers(Peer &aPeer);
void StopServiceResolvers(Peer &aPeer);
void HandleSrvResult(const Dnssd::SrvResult &aResult);
void HandleTxtResult(const Dnssd::TxtResult &aResult);
void ProcessPeerTxtData(const Dnssd::TxtResult &aResult, Peer &aPeer);
void StartHostAddressResolver(Peer &aPeer);
void StopHostAddressResolver(Peer &aPeer);
void HandleAddressResult(const Dnssd::AddressResult &aResult);
void UpdatePeerAddresses(Peer &aPeer, const Peer::AddressArray &aSortedAddresses);
void UpdatePeerState(Peer &aPeer);
static void HandleRegisterDone(otInstance *aInstance, Dnssd::RequestId aRequestId, otError aError);
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);
#else
void HandleDiscoveredPeerInfo(const PeerInfo &aInfo);
#endif
using ServiceTask = TaskletIn<PeerDiscoverer, &PeerDiscoverer::HandleServiceTask>;
#if OPENTHREAD_CONFIG_TREL_MANAGE_DNSSD_ENABLE
static const char kTrelServiceType[];
#endif
State mState;
ServiceTask mServiceTask;
#if OPENTHREAD_CONFIG_TREL_MANAGE_DNSSD_ENABLE
ServiceName mServiceName;
bool mBrowsing;
#endif
};
} // namespace Trel
+2
View File
@@ -47,6 +47,7 @@ add_library(ot-nexus-platform
platform/nexus_node.cpp
platform/nexus_radio.cpp
platform/nexus_settings.cpp
platform/nexus_trel.cpp
)
target_include_directories(ot-nexus-platform
@@ -113,3 +114,4 @@ ot_nexus_test(border_agent)
ot_nexus_test(dtls)
ot_nexus_test(form_join)
ot_nexus_test(large_network)
ot_nexus_test(trel)
+12
View File
@@ -44,6 +44,17 @@ else
top_builddir=.
fi
case $1 in
trel)
trel=ON
fifteenfour=OFF
;;
*)
trel=OFF
fifteenfour=ON
;;
esac
echo "===================================================================================================="
echo "Building OpenThread Nexus test platform"
echo "===================================================================================================="
@@ -51,6 +62,7 @@ cd "${top_builddir}" || die "cd failed"
cmake -GNinja -DOT_PLATFORM=nexus -DOT_COMPILE_WARNING_AS_ERROR=ON \
-DOT_MULTIPLE_INSTANCE=ON \
-DOT_THREAD_VERSION=1.4 -DOT_APP_CLI=OFF -DOT_APP_NCP=OFF -DOT_APP_RCP=OFF \
-DOT_15_4=${fifteenfour} -DOT_TREL=${trel} \
-DOT_PROJECT_CONFIG=../tests/nexus/openthread-core-nexus-config.h \
"${top_srcdir}" || die
ninja || die
@@ -127,6 +127,8 @@
#define OPENTHREAD_CONFIG_TMF_NETDATA_SERVICE_ENABLE 1
#define OPENTHREAD_CONFIG_TMF_NETDIAG_CLIENT_ENABLE 1
#define OPENTHREAD_CONFIG_TMF_SNOOP_CACHE_ENTRY_TIMEOUT 3
#define OPENTHREAD_CONFIG_TREL_MANAGE_DNSSD_ENABLE 1
#define OPENTHREAD_CONFIG_TREL_USE_HEAP_ENABLE 1
#define OPENTHREAD_CONFIG_UPTIME_ENABLE 1
#endif /* OT_CORE_NEXUS_CONFIG_H_ */
+37 -4
View File
@@ -32,20 +32,22 @@
namespace ot {
namespace Nexus {
Core *Core::sCore = nullptr;
Core *Core::sCore = nullptr;
bool Core::sInUse = false;
Core::Core(void)
: mNow(0)
, mCurNodeId(0)
, mPendingAction(false)
{
VerifyOrQuit(sCore == nullptr);
sCore = this;
VerifyOrQuit(!sInUse);
sCore = this;
sInUse = true;
mNextAlarmTime = mNow.GetDistantFuture();
}
Core::~Core(void) { sCore = nullptr; }
Core::~Core(void) { sInUse = false; }
Node &Core::CreateNode(void)
{
@@ -101,6 +103,9 @@ void Core::Process(Node &aNode)
ProcessRadio(aNode);
ProcessMdns(aNode);
#if OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE
ProcessTrel(aNode);
#endif
if (aNode.mAlarm.ShouldTrigger(mNow))
{
@@ -221,5 +226,33 @@ void Core::ProcessMdns(Node &aNode)
aNode.mMdns.mPendingTxList.Free();
}
#if OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE
void Core::ProcessTrel(Node &aNode)
{
Ip6::SockAddr senderSockAddr;
Ip6::SockAddr rxNodeSockAddr;
aNode.GetTrelSockAddr(senderSockAddr);
for (Trel::PendingTx &pendingTx : aNode.mTrel.mPendingTxList)
{
for (Node &rxNode : mNodes)
{
rxNode.GetTrelSockAddr(rxNodeSockAddr);
if (pendingTx.mDestSockAddr == rxNodeSockAddr)
{
rxNode.mTrel.Receive(rxNode.GetInstance(), pendingTx.mPayloadData, senderSockAddr);
break;
}
}
}
aNode.mTrel.mPendingTxList.Free();
}
#endif // OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE
} // namespace Nexus
} // namespace ot
+4
View File
@@ -77,8 +77,12 @@ private:
void Process(Node &aNode);
void ProcessRadio(Node &aNode);
void ProcessMdns(Node &aNode);
#if OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE
void ProcessTrel(Node &aNode);
#endif
static Core *sCore;
static bool sInUse;
OwningList<Node> mNodes;
uint16_t mCurNodeId;
+8
View File
@@ -83,5 +83,13 @@ void Node::AllowList(Node &aNode)
void Node::UnallowList(Node &aNode) { Get<Mac::Filter>().RemoveAddress(aNode.Get<Mac::Mac>().GetExtAddress()); }
#if OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE
void Node::GetTrelSockAddr(Ip6::SockAddr &aSockAddr) const
{
aSockAddr.SetAddress(mMdns.mIfAddresses[0]);
aSockAddr.SetPort(mTrel.mUdpPort);
}
#endif
} // namespace Nexus
} // namespace ot
+8 -1
View File
@@ -36,6 +36,7 @@
#include "nexus_mdns.hpp"
#include "nexus_radio.hpp"
#include "nexus_settings.hpp"
#include "nexus_trel.hpp"
#include "nexus_utils.hpp"
namespace ot {
@@ -58,6 +59,9 @@ public:
void Join(Node &aNode, JoinMode aJoinMode = kAsFtd);
void AllowList(Node &aNode);
void UnallowList(Node &aNode);
#if OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE
void GetTrelSockAddr(Ip6::SockAddr &aSockAddr) const;
#endif
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@@ -82,7 +86,10 @@ public:
Alarm mAlarm;
Mdns mMdns;
Settings mSettings;
bool mPendingTasklet;
#if OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE
Trel mTrel;
#endif
bool mPendingTasklet;
private:
Node(void) = default;
+120
View File
@@ -0,0 +1,120 @@
/*
* 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/platform/trel.h>
#include "nexus_core.hpp"
#include "nexus_node.hpp"
#if OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE
namespace ot {
namespace Nexus {
//---------------------------------------------------------------------------------------------------------------------
// otPlatTrel APIs
extern "C" {
void otPlatTrelEnable(otInstance *aInstance, uint16_t *aUdpPort) { AsNode(aInstance).mTrel.Enable(*aUdpPort); }
void otPlatTrelDisable(otInstance *aInstance) { AsNode(aInstance).mTrel.Disable(); }
void otPlatTrelSend(otInstance *aInstance,
const uint8_t *aUdpPayload,
uint16_t aUdpPayloadLen,
const otSockAddr *aDestSockAddr)
{
AsNode(aInstance).mTrel.Send(aUdpPayload, aUdpPayloadLen, AsCoreType(aDestSockAddr));
}
void otPlatTrelNotifyPeerSocketAddressDifference(otInstance *aInstance,
const otSockAddr *aPeerSockAddr,
const otSockAddr *aRxSockAddr)
{
OT_UNUSED_VARIABLE(aInstance);
OT_UNUSED_VARIABLE(aPeerSockAddr);
OT_UNUSED_VARIABLE(aRxSockAddr);
}
const otPlatTrelCounters *otPlatTrelGetCounters(otInstance *aInstance) { return &AsNode(aInstance).mTrel.mCounters; }
void otPlatTrelResetCounters(otInstance *aInstance) { AsNode(aInstance).mTrel.ResetCounters(); }
} // extern "C"
//---------------------------------------------------------------------------------------------------------------------
// Trel
uint16_t Trel::sLastUsedUdpPort = kUdpPortStart;
Trel::Trel(void)
: mEnabled(false)
, mUdpPort(sLastUsedUdpPort++)
{
ClearAllBytes(mCounters);
}
void Trel::Enable(uint16_t &aUdpPort)
{
mEnabled = true;
aUdpPort = mUdpPort;
}
void Trel::Disable(void)
{
mEnabled = false;
mPendingTxList.Free();
}
void Trel::Send(const uint8_t *aUdpPayload, uint16_t aUdpPayloadLen, const Ip6::SockAddr &aDestSockAddr)
{
PendingTx *pendingTx = PendingTx::Allocate();
VerifyOrQuit(pendingTx != nullptr);
SuccessOrQuit(pendingTx->mPayloadData.SetFrom(aUdpPayload, aUdpPayloadLen));
pendingTx->mDestSockAddr = aDestSockAddr;
mPendingTxList.PushAfterTail(*pendingTx);
mCounters.mTxPackets++;
mCounters.mTxBytes += aUdpPayloadLen;
}
void Trel::Receive(Instance &aInstance, Heap::Data &aPayloadData, const Ip6::SockAddr &aSenderAddr)
{
mCounters.mRxPackets++;
mCounters.mRxBytes += aPayloadData.GetLength();
otPlatTrelHandleReceived(&aInstance, AsNonConst(aPayloadData.GetBytes()), aPayloadData.GetLength(), &aSenderAddr);
}
} // namespace Nexus
} // namespace ot
#endif // OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE
+72
View File
@@ -0,0 +1,72 @@
/*
* 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.
*/
#ifndef OT_NEXUS_TREL_HPP_
#define OT_NEXUS_TREL_HPP_
#include "instance/instance.hpp"
#if OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE
namespace ot {
namespace Nexus {
struct Trel
{
static constexpr uint16_t kUdpPortStart = 49152;
typedef otPlatTrelCounters Counters;
Trel(void);
void Enable(uint16_t &aUdpPort);
void Disable(void);
void Send(const uint8_t *aUdpPayload, uint16_t aUdpPayloadLen, const Ip6::SockAddr &aDestSockAddr);
void ResetCounters(void) { ClearAllBytes(mCounters); }
void Receive(Instance &aInstance, Heap::Data &aPayloadData, const Ip6::SockAddr &aSenderAddr);
struct PendingTx : public Heap::Allocatable<PendingTx>, public LinkedListEntry<PendingTx>
{
PendingTx *mNext;
Heap::Data mPayloadData;
Ip6::SockAddr mDestSockAddr;
};
static uint16_t sLastUsedUdpPort;
bool mEnabled;
uint16_t mUdpPort;
OwningList<PendingTx> mPendingTxList;
Counters mCounters;
};
} // namespace Nexus
} // namespace ot
#endif // OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE
#endif // OT_NEXUS_TREL_HPP_
+778
View File
@@ -0,0 +1,778 @@
/*
* 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 {
#if OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE
static constexpr uint32_t kInfraIfIndex = 1;
static constexpr uint16_t kMaxTxtDataSize = 128;
void TestTrelBasic(void)
{
// Validate basic operations, forming a network and
// joining as router, FED, MED, and SED using TREL.
// Also check TREL peer table on all devices.
Core nexus;
Node &leader = nexus.CreateNode();
Node &fed = nexus.CreateNode();
Node &sed = nexus.CreateNode();
Node &med = nexus.CreateNode();
Node &router1 = nexus.CreateNode();
Node &router2 = nexus.CreateNode();
Log("---------------------------------------------------------------------------------------");
Log("TestTrelBasic()");
nexus.AdvanceTime(0);
for (Node &node : nexus.GetNodes())
{
node.GetInstance().SetLogLevel(kLogLevelWarn);
SuccessOrQuit(node.Get<Dns::Multicast::Core>().SetEnabled(true, kInfraIfIndex));
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Log("Form network");
leader.Form();
nexus.AdvanceTime(13 * 1000);
VerifyOrQuit(leader.Get<Mle::Mle>().IsLeader());
fed.Join(leader, Node::kAsFed);
nexus.AdvanceTime(10 * 1000);
VerifyOrQuit(fed.Get<Mle::Mle>().IsChild());
sed.Join(leader, Node::kAsSed);
nexus.AdvanceTime(2 * 1000);
VerifyOrQuit(sed.Get<Mle::Mle>().IsChild());
med.Join(leader, Node::kAsMed);
nexus.AdvanceTime(2 * 1000);
VerifyOrQuit(med.Get<Mle::Mle>().IsChild());
router1.Join(leader);
router2.Join(leader);
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Log("Check all nodes roles and device modes");
nexus.AdvanceTime(300 * 1000);
VerifyOrQuit(leader.Get<Mle::Mle>().IsLeader());
VerifyOrQuit(fed.Get<Mle::Mle>().IsChild());
VerifyOrQuit(sed.Get<Mle::Mle>().IsChild());
VerifyOrQuit(router1.Get<Mle::Mle>().IsRouter());
VerifyOrQuit(router2.Get<Mle::Mle>().IsRouter());
VerifyOrQuit(fed.Get<Mle::Mle>().IsRxOnWhenIdle());
VerifyOrQuit(fed.Get<Mle::Mle>().IsFullThreadDevice());
VerifyOrQuit(med.Get<Mle::Mle>().IsRxOnWhenIdle());
VerifyOrQuit(!med.Get<Mle::Mle>().IsFullThreadDevice());
VerifyOrQuit(med.Get<Mle::Mle>().IsMinimalEndDevice());
VerifyOrQuit(!sed.Get<Mle::Mle>().IsRxOnWhenIdle());
VerifyOrQuit(!sed.Get<Mle::Mle>().IsFullThreadDevice());
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Log("Check TREL peer table on all nodes");
for (Node &node : nexus.GetNodes())
{
VerifyOrQuit(node.Get<ot::Trel::PeerTable>().GetNumberOfPeers() == 5);
for (const ot::Trel::Peer &peer : node.Get<ot::Trel::PeerTable>())
{
bool found = false;
VerifyOrQuit(peer.IsStateValid());
VerifyOrQuit(peer.GetExtPanId() == node.Get<MeshCoP::ExtendedPanIdManager>().GetExtPanId());
for (Node &otherNode : nexus.GetNodes())
{
if (&otherNode == &node)
{
continue;
}
if (peer.GetExtAddress() == otherNode.Get<Mac::Mac>().GetExtAddress())
{
Ip6::SockAddr otherSockAddr;
found = true;
otherNode.GetTrelSockAddr(otherSockAddr);
VerifyOrQuit(peer.GetSockAddr() == otherSockAddr);
VerifyOrQuit(peer.GetServiceName() != nullptr);
VerifyOrQuit(
StringMatch(peer.GetServiceName(), otherNode.Get<ot::Trel::PeerDiscoverer>().GetServiceName()));
VerifyOrQuit(peer.GetHostName() != nullptr);
VerifyOrQuit(StringStartsWith(peer.GetHostName(), "ot"));
VerifyOrQuit(StringEndsWith(peer.GetHostName(),
otherNode.Get<Mac::Mac>().GetExtAddress().ToString().AsCString()));
VerifyOrQuit(peer.GetHostAddresses().GetLength() == 1);
VerifyOrQuit(peer.GetHostAddresses()[0] == otherSockAddr.GetAddress());
break;
}
}
VerifyOrQuit(found);
// Check the format of the TREL service name
VerifyOrQuit(StringStartsWith(node.Get<ot::Trel::PeerDiscoverer>().GetServiceName(), "otTREL"));
VerifyOrQuit(StringEndsWith(node.Get<ot::Trel::PeerDiscoverer>().GetServiceName(),
node.Get<Mac::Mac>().GetExtAddress().ToString().AsCString()));
}
}
}
void TestTrelDelayedMdnsStartAndPeerRemovalDelay(void)
{
Core nexus;
Node &node1 = nexus.CreateNode();
Node &node2 = nexus.CreateNode();
const ot::Trel::Peer *peer;
Log("---------------------------------------------------------------------------------------");
Log("TestTrelDelayedMdnsStartAndPeerRemovalDelay()");
nexus.AdvanceTime(0);
for (Node &node : nexus.GetNodes())
{
node.GetInstance().SetLogLevel(kLogLevelWarn);
VerifyOrQuit(!node.Get<Dns::Multicast::Core>().IsEnabled());
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Log("Start first network on node1");
SuccessOrQuit(node1.Get<Dns::Multicast::Core>().SetEnabled(true, kInfraIfIndex));
node1.Form();
nexus.AdvanceTime(45 * 1000);
VerifyOrQuit(node1.Get<Mle::Mle>().IsLeader());
// Check that `node1` did not discover any `TREL` peer,
// as `mDNS` is not yet enabled on other nodes.
// Additionally, the `TREL` peer table must exclude the device itself.
VerifyOrQuit(node1.Get<ot::Trel::PeerTable>().IsEmpty());
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Log("Enable mDNS on `node2`, form a new network");
SuccessOrQuit(node2.Get<Dns::Multicast::Core>().SetEnabled(true, kInfraIfIndex));
node2.Form();
nexus.AdvanceTime(45 * 1000);
VerifyOrQuit(node1.Get<Mle::Mle>().IsLeader());
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Log("Validate that peer tables are correctly updated on both `node1` and `node2`");
VerifyOrQuit(node1.Get<ot::Trel::PeerTable>().GetNumberOfPeers() == 1);
VerifyOrQuit(node2.Get<ot::Trel::PeerTable>().GetNumberOfPeers() == 1);
// Check peer on `node1` to match `node2` info.
peer = node1.Get<ot::Trel::PeerTable>().GetHead();
VerifyOrQuit(peer != nullptr);
VerifyOrQuit(peer->IsStateValid());
VerifyOrQuit(peer->GetExtPanId() == node2.Get<MeshCoP::ExtendedPanIdManager>().GetExtPanId());
VerifyOrQuit(peer->GetExtAddress() == node2.Get<Mac::Mac>().GetExtAddress());
VerifyOrQuit(peer->GetServiceName() != nullptr);
VerifyOrQuit(StringMatch(peer->GetServiceName(), node2.Get<ot::Trel::PeerDiscoverer>().GetServiceName()));
VerifyOrQuit(peer->GetHostName() != nullptr);
VerifyOrQuit(StringStartsWith(peer->GetHostName(), "ot"));
VerifyOrQuit(StringEndsWith(peer->GetHostName(), node2.Get<Mac::Mac>().GetExtAddress().ToString().AsCString()));
VerifyOrQuit(peer->GetSockAddr().GetPort() == node2.mTrel.mUdpPort);
VerifyOrQuit(peer->GetSockAddr().GetAddress() == node2.mMdns.mIfAddresses[0]);
VerifyOrQuit(peer->GetHostAddresses().GetLength() == 1);
VerifyOrQuit(peer->GetHostAddresses()[0] == node2.mMdns.mIfAddresses[0]);
VerifyOrQuit(peer->GetNext() == nullptr);
// Check peer on `node2` to match `node1` info.
peer = node2.Get<ot::Trel::PeerTable>().GetHead();
VerifyOrQuit(peer != nullptr);
VerifyOrQuit(peer->IsStateValid());
VerifyOrQuit(peer->GetExtPanId() == node1.Get<MeshCoP::ExtendedPanIdManager>().GetExtPanId());
VerifyOrQuit(peer->GetExtAddress() == node1.Get<Mac::Mac>().GetExtAddress());
VerifyOrQuit(peer->GetServiceName() != nullptr);
VerifyOrQuit(StringMatch(peer->GetServiceName(), node1.Get<ot::Trel::PeerDiscoverer>().GetServiceName()));
VerifyOrQuit(peer->GetHostName() != nullptr);
VerifyOrQuit(StringStartsWith(peer->GetHostName(), "ot"));
VerifyOrQuit(StringEndsWith(peer->GetHostName(), node1.Get<Mac::Mac>().GetExtAddress().ToString().AsCString()));
VerifyOrQuit(peer->GetSockAddr().GetPort() == node1.mTrel.mUdpPort);
VerifyOrQuit(peer->GetSockAddr().GetAddress() == node1.mMdns.mIfAddresses[0]);
VerifyOrQuit(peer->GetHostAddresses().GetLength() == 1);
VerifyOrQuit(peer->GetHostAddresses()[0] == node1.mMdns.mIfAddresses[0]);
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Log("Disable TREL Interface (and `PeerDiscoverer`) on `node2`");
node2.Get<ot::Trel::Interface>().Disable();
nexus.AdvanceTime(2 * 1000);
VerifyOrQuit(node2.Get<ot::Trel::PeerTable>().GetNumberOfPeers() == 0);
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Log("Validate that `PeerTable` is properly updated on `node1`");
// Check peer on `node1` is still present but not longer `IsStateValid()`.
peer = node1.Get<ot::Trel::PeerTable>().GetHead();
VerifyOrQuit(peer != nullptr);
VerifyOrQuit(!peer->IsStateValid());
VerifyOrQuit(peer->GetExtPanId() == node2.Get<MeshCoP::ExtendedPanIdManager>().GetExtPanId());
VerifyOrQuit(peer->GetExtAddress() == node2.Get<Mac::Mac>().GetExtAddress());
VerifyOrQuit(peer->GetServiceName() != nullptr);
VerifyOrQuit(StringMatch(peer->GetServiceName(), node2.Get<ot::Trel::PeerDiscoverer>().GetServiceName()));
VerifyOrQuit(peer->GetHostName() == nullptr);
VerifyOrQuit(peer->GetHostAddresses().GetLength() == 0);
VerifyOrQuit(peer->GetNext() == nullptr);
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Log("Re-enable TREL Interface (and `PeerDiscoverer`) on `node2`");
node2.Get<ot::Trel::Interface>().Enable();
nexus.AdvanceTime(15 * 1000);
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Log("Validate that peer tables again updated on both nodes");
VerifyOrQuit(node1.Get<ot::Trel::PeerTable>().GetNumberOfPeers() == 1);
VerifyOrQuit(node2.Get<ot::Trel::PeerTable>().GetNumberOfPeers() == 1);
// Check peer on `node1` to match `node2` info.
peer = node1.Get<ot::Trel::PeerTable>().GetHead();
VerifyOrQuit(peer != nullptr);
VerifyOrQuit(peer->IsStateValid());
VerifyOrQuit(peer->GetExtPanId() == node2.Get<MeshCoP::ExtendedPanIdManager>().GetExtPanId());
VerifyOrQuit(peer->GetExtAddress() == node2.Get<Mac::Mac>().GetExtAddress());
VerifyOrQuit(peer->GetServiceName() != nullptr);
VerifyOrQuit(StringMatch(peer->GetServiceName(), node2.Get<ot::Trel::PeerDiscoverer>().GetServiceName()));
VerifyOrQuit(peer->GetHostName() != nullptr);
VerifyOrQuit(StringStartsWith(peer->GetHostName(), "ot"));
VerifyOrQuit(StringEndsWith(peer->GetHostName(), node2.Get<Mac::Mac>().GetExtAddress().ToString().AsCString()));
VerifyOrQuit(peer->GetSockAddr().GetPort() == node2.mTrel.mUdpPort);
VerifyOrQuit(peer->GetSockAddr().GetAddress() == node2.mMdns.mIfAddresses[0]);
VerifyOrQuit(peer->GetHostAddresses().GetLength() == 1);
VerifyOrQuit(peer->GetHostAddresses()[0] == node2.mMdns.mIfAddresses[0]);
VerifyOrQuit(peer->GetNext() == nullptr);
// Check peer on `node2` to match `node1` info.
peer = node2.Get<ot::Trel::PeerTable>().GetHead();
VerifyOrQuit(peer != nullptr);
VerifyOrQuit(peer->IsStateValid());
VerifyOrQuit(peer->GetExtPanId() == node1.Get<MeshCoP::ExtendedPanIdManager>().GetExtPanId());
VerifyOrQuit(peer->GetExtAddress() == node1.Get<Mac::Mac>().GetExtAddress());
VerifyOrQuit(peer->GetServiceName() != nullptr);
VerifyOrQuit(StringMatch(peer->GetServiceName(), node1.Get<ot::Trel::PeerDiscoverer>().GetServiceName()));
VerifyOrQuit(peer->GetHostName() != nullptr);
VerifyOrQuit(StringStartsWith(peer->GetHostName(), "ot"));
VerifyOrQuit(StringEndsWith(peer->GetHostName(), node1.Get<Mac::Mac>().GetExtAddress().ToString().AsCString()));
VerifyOrQuit(peer->GetSockAddr().GetPort() == node1.mTrel.mUdpPort);
VerifyOrQuit(peer->GetSockAddr().GetAddress() == node1.mMdns.mIfAddresses[0]);
VerifyOrQuit(peer->GetHostAddresses().GetLength() == 1);
VerifyOrQuit(peer->GetHostAddresses()[0] == node1.mMdns.mIfAddresses[0]);
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Log("Disable TREL Interface (and `PeerDiscoverer`) on `node2` again");
node2.Get<ot::Trel::Interface>().Disable();
Log("Wait for long enough for the `node2` peer to be fully deleted on `node1`");
nexus.AdvanceTime(15 * 1000);
VerifyOrQuit(node1.Get<ot::Trel::PeerTable>().IsEmpty());
VerifyOrQuit(node2.Get<ot::Trel::PeerTable>().IsEmpty());
}
void TestServiceNameConflict(void)
{
Core nexus;
Node &node1 = nexus.CreateNode();
Node &node2 = nexus.CreateNode();
Node &conflictNode = nexus.CreateNode();
Dns::Multicast::Core::Service service;
Dns::Name::Buffer conflictName;
bool peersValidated;
Log("---------------------------------------------------------------------------------------");
Log("TestServiceNameConflict()");
nexus.AdvanceTime(0);
for (Node &node : nexus.GetNodes())
{
node.GetInstance().SetLogLevel(kLogLevelWarn);
VerifyOrQuit(!node.Get<Dns::Multicast::Core>().IsEnabled());
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Log("Disable TREL interface but enable mDNS on `conflictNode`");
conflictNode.Get<ot::Trel::Interface>().Disable();
SuccessOrQuit(conflictNode.Get<Dns::Multicast::Core>().SetEnabled(true, kInfraIfIndex));
Log("Register a service on `conflictNode` with same name that `node1` would use");
SuccessOrQuit(StringCopy(conflictName, node1.Get<ot::Trel::PeerDiscoverer>().GetServiceName()));
ClearAllBytes(service);
service.mServiceType = "_trel._udp";
service.mServiceInstance = conflictName;
service.mPort = 12345;
SuccessOrQuit(conflictNode.Get<Dns::Multicast::Core>().RegisterService(service, /* aRequestId */ 0, nullptr));
nexus.AdvanceTime(15 * 1000);
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Log("Enable mDNS on `node1` and `node2` and form a new network");
SuccessOrQuit(node1.Get<Dns::Multicast::Core>().SetEnabled(true, kInfraIfIndex));
SuccessOrQuit(node2.Get<Dns::Multicast::Core>().SetEnabled(true, kInfraIfIndex));
node1.Form();
nexus.AdvanceTime(45 * 1000);
VerifyOrQuit(node1.Get<Mle::Mle>().IsLeader());
node2.Join(node1, Node::kAsFed);
nexus.AdvanceTime(15 * 1000);
VerifyOrQuit(node2.Get<Mle::Mle>().IsChild());
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Log("Validate that `node1` correctly detected the name conflict and appended a (1) to its service name");
VerifyOrQuit(StringStartsWith(node1.Get<ot::Trel::PeerDiscoverer>().GetServiceName(), conflictName));
VerifyOrQuit(StringEndsWith(node1.Get<ot::Trel::PeerDiscoverer>().GetServiceName(), "(1)"));
Log("Check peer table on `node2` to match `node1` info");
peersValidated = false;
for (const ot::Trel::Peer &peer : node2.Get<ot::Trel::PeerTable>())
{
if (peer.IsStateValid())
{
VerifyOrQuit(peer.GetExtPanId() == node1.Get<MeshCoP::ExtendedPanIdManager>().GetExtPanId());
VerifyOrQuit(peer.GetExtAddress() == node1.Get<Mac::Mac>().GetExtAddress());
VerifyOrQuit(peer.GetServiceName() != nullptr);
VerifyOrQuit(StringMatch(peer.GetServiceName(), node1.Get<ot::Trel::PeerDiscoverer>().GetServiceName()));
peersValidated = true;
break;
}
}
VerifyOrQuit(peersValidated);
}
void TestHostAddressChange(void)
{
Core nexus;
Node &node1 = nexus.CreateNode();
Node &node2 = nexus.CreateNode();
const ot::Trel::Peer *peer;
Dns::Multicast::Core::Service service;
uint8_t txtData[kMaxTxtDataSize];
Dns::TxtDataEncoder encoder(txtData, sizeof(txtData));
Ip6::Address linkLocalAddr;
Ip6::Address guaAddr;
Ip6::Address ulaAddr;
Log("---------------------------------------------------------------------------------------");
Log("TestHostAddressChange()");
nexus.AdvanceTime(0);
for (Node &node : nexus.GetNodes())
{
node.GetInstance().SetLogLevel(kLogLevelWarn);
VerifyOrQuit(!node.Get<Dns::Multicast::Core>().IsEnabled());
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Log("Disable TREL interface but enable mDNS on `node2`");
node2.Get<ot::Trel::Interface>().Disable();
SuccessOrQuit(node2.Get<Dns::Multicast::Core>().SetEnabled(true, kInfraIfIndex));
Log("Manually register a TREL service on `node2` with proper TXT data");
SuccessOrQuit(encoder.AppendEntry("xa", node2.Get<Mac::Mac>().GetExtAddress()));
SuccessOrQuit(encoder.AppendEntry("xp", node2.Get<MeshCoP::ExtendedPanIdManager>().GetExtPanId()));
ClearAllBytes(service);
service.mServiceType = "_trel._udp";
service.mServiceInstance = "node2";
service.mTxtData = txtData;
service.mTxtDataLength = encoder.GetLength();
service.mPort = 3333;
SuccessOrQuit(node2.Get<Dns::Multicast::Core>().RegisterService(service, /* aRequestId */ 0, nullptr));
nexus.AdvanceTime(15 * 1000);
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Log("Form a new network on `node1`");
SuccessOrQuit(node1.Get<Dns::Multicast::Core>().SetEnabled(true, kInfraIfIndex));
node1.Form();
nexus.AdvanceTime(45 * 1000);
VerifyOrQuit(node1.Get<Mle::Mle>().IsLeader());
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Log("Validate peer table on `node1` and `node2` is discovered properly");
peer = node1.Get<ot::Trel::PeerTable>().GetHead();
VerifyOrQuit(peer != nullptr);
VerifyOrQuit(peer->IsStateValid());
VerifyOrQuit(peer->GetExtPanId() == node2.Get<MeshCoP::ExtendedPanIdManager>().GetExtPanId());
VerifyOrQuit(peer->GetExtAddress() == node2.Get<Mac::Mac>().GetExtAddress());
VerifyOrQuit(peer->GetServiceName() != nullptr);
VerifyOrQuit(StringMatch(peer->GetServiceName(), service.mServiceInstance));
VerifyOrQuit(peer->GetHostName() != nullptr);
VerifyOrQuit(StringStartsWith(peer->GetHostName(), "ot"));
VerifyOrQuit(StringEndsWith(peer->GetHostName(), node2.Get<Mac::Mac>().GetExtAddress().ToString().AsCString()));
VerifyOrQuit(peer->GetSockAddr().GetPort() == service.mPort);
VerifyOrQuit(peer->GetSockAddr().GetAddress() == node2.mMdns.mIfAddresses[0]);
VerifyOrQuit(peer->GetHostAddresses().GetLength() == 1);
VerifyOrQuit(peer->GetHostAddresses()[0] == node2.mMdns.mIfAddresses[0]);
VerifyOrQuit(peer->GetNext() == nullptr);
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Log("Update the advertised local host addresses on `node2`");
node2.mMdns.mIfAddresses.Clear();
SuccessOrQuit(guaAddr.FromString("2001:cafe::4567"));
SuccessOrQuit(node2.mMdns.mIfAddresses.PushBack(guaAddr));
SuccessOrQuit(ulaAddr.FromString("fd00:abba::1234"));
SuccessOrQuit(node2.mMdns.mIfAddresses.PushBack(ulaAddr));
SuccessOrQuit(linkLocalAddr.FromString("fe80::bd2c:a124"));
SuccessOrQuit(node2.mMdns.mIfAddresses.PushBack(linkLocalAddr));
node2.mMdns.SignalIfAddresses(node2.GetInstance());
nexus.AdvanceTime(3 * 1000);
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Log("Validate peer table on `node1` discovers all the new addresses");
peer = node1.Get<ot::Trel::PeerTable>().GetHead();
VerifyOrQuit(peer != nullptr);
VerifyOrQuit(peer->IsStateValid());
VerifyOrQuit(peer->GetExtPanId() == node2.Get<MeshCoP::ExtendedPanIdManager>().GetExtPanId());
VerifyOrQuit(peer->GetExtAddress() == node2.Get<Mac::Mac>().GetExtAddress());
VerifyOrQuit(peer->GetServiceName() != nullptr);
VerifyOrQuit(StringMatch(peer->GetServiceName(), service.mServiceInstance));
VerifyOrQuit(peer->GetHostName() != nullptr);
VerifyOrQuit(StringStartsWith(peer->GetHostName(), "ot"));
VerifyOrQuit(StringEndsWith(peer->GetHostName(), node2.Get<Mac::Mac>().GetExtAddress().ToString().AsCString()));
VerifyOrQuit(peer->GetSockAddr().GetPort() == service.mPort);
VerifyOrQuit(peer->GetHostAddresses().GetLength() == 3);
for (const Ip6::Address &hostAddress : peer->GetHostAddresses())
{
VerifyOrQuit(node2.mMdns.mIfAddresses.Contains(hostAddress));
}
for (const Ip6::Address &ifAddress : node2.mMdns.mIfAddresses)
{
VerifyOrQuit(peer->GetHostAddresses().Contains(ifAddress));
}
// Check the order of addresses in list:
VerifyOrQuit(peer->GetHostAddresses()[0] == linkLocalAddr);
VerifyOrQuit(peer->GetHostAddresses()[1] == guaAddr);
VerifyOrQuit(peer->GetHostAddresses()[2] == ulaAddr);
Log("Validate the peer `SockAddr` is correctly updated based on new discovered host addresses");
VerifyOrQuit(peer->GetSockAddr().GetAddress() == linkLocalAddr);
VerifyOrQuit(peer->GetSockAddr().GetPort() == service.mPort);
VerifyOrQuit(peer->GetNext() == nullptr);
}
void TestMultiServiceSameHost(void)
{
Core nexus;
Node &node = nexus.CreateNode();
Node &multiServiceNode = nexus.CreateNode();
const ot::Trel::Peer *peer;
Dns::Multicast::Core::Service services[3];
uint8_t txtData[kMaxTxtDataSize];
Ip6::Address address;
Log("---------------------------------------------------------------------------------------");
Log("TestMultiServiceSameHost()");
nexus.AdvanceTime(0);
for (Node &node : nexus.GetNodes())
{
node.GetInstance().SetLogLevel(kLogLevelInfo);
VerifyOrQuit(!node.Get<Dns::Multicast::Core>().IsEnabled());
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Log("Disable TREL interface but enable mDNS on `multiServiceNode`");
multiServiceNode.Get<ot::Trel::Interface>().Disable();
SuccessOrQuit(multiServiceNode.Get<Dns::Multicast::Core>().SetEnabled(true, kInfraIfIndex));
Log("Manually register three TREL services on the `multiServiceNode`");
{
Dns::TxtDataEncoder encoder(txtData, sizeof(txtData));
SuccessOrQuit(encoder.AppendEntry("xa", "0102030405060708"));
SuccessOrQuit(encoder.AppendEntry("xp", "0102030405060708"));
ClearAllBytes(services[0]);
services[0].mServiceType = "_trel._udp";
services[0].mServiceInstance = "service0";
services[0].mTxtData = txtData;
services[0].mTxtDataLength = encoder.GetLength();
services[0].mPort = 11111;
}
SuccessOrQuit(
multiServiceNode.Get<Dns::Multicast::Core>().RegisterService(services[0], /* aRequestId */ 0, nullptr));
{
Dns::TxtDataEncoder encoder(txtData, sizeof(txtData));
SuccessOrQuit(encoder.AppendEntry("xa", "1122334455667788"));
SuccessOrQuit(encoder.AppendEntry("xp", "1122334455667788"));
ClearAllBytes(services[1]);
services[1].mServiceType = "_trel._udp";
services[1].mServiceInstance = "service1";
services[1].mTxtData = txtData;
services[1].mTxtDataLength = encoder.GetLength();
services[1].mPort = 2222;
}
SuccessOrQuit(
multiServiceNode.Get<Dns::Multicast::Core>().RegisterService(services[1], /* aRequestId */ 0, nullptr));
{
Dns::TxtDataEncoder encoder(txtData, sizeof(txtData));
SuccessOrQuit(encoder.AppendEntry("xa", "1020304050607080"));
SuccessOrQuit(encoder.AppendEntry("xp", "1020304050607080"));
ClearAllBytes(services[2]);
services[2].mServiceType = "_trel._udp";
services[2].mServiceInstance = "service2";
services[2].mTxtData = txtData;
services[2].mTxtDataLength = encoder.GetLength();
services[2].mPort = 3333;
}
SuccessOrQuit(
multiServiceNode.Get<Dns::Multicast::Core>().RegisterService(services[2], /* aRequestId */ 0, nullptr));
nexus.AdvanceTime(15 * 1000);
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Log("Form a new network on `node`");
SuccessOrQuit(node.Get<Dns::Multicast::Core>().SetEnabled(true, kInfraIfIndex));
node.Form();
nexus.AdvanceTime(45 * 1000);
VerifyOrQuit(node.Get<Mle::Mle>().IsLeader());
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Log("Validate peer table on `node` and all services are discovered from the same host");
VerifyOrQuit(node.Get<ot::Trel::PeerTable>().GetNumberOfPeers() == 3);
for (const ot::Trel::Peer &peer : node.Get<ot::Trel::PeerTable>())
{
bool found = false;
VerifyOrQuit(peer.IsStateValid());
VerifyOrQuit(peer.GetServiceName() != nullptr);
VerifyOrQuit(peer.GetHostName() != nullptr);
VerifyOrQuit(StringStartsWith(peer.GetHostName(), "ot"));
VerifyOrQuit(StringEndsWith(peer.GetHostName(),
multiServiceNode.Get<Mac::Mac>().GetExtAddress().ToString().AsCString()));
VerifyOrQuit(peer.GetSockAddr().GetAddress() == multiServiceNode.mMdns.mIfAddresses[0]);
VerifyOrQuit(peer.GetHostAddresses().GetLength() == 1);
VerifyOrQuit(peer.GetHostAddresses()[0] == multiServiceNode.mMdns.mIfAddresses[0]);
for (const Dns::Multicast::Core::Service &service : services)
{
if (StringMatch(peer.GetServiceName(), service.mServiceInstance))
{
found = true;
}
}
VerifyOrQuit(found);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Log("Unregister the third service on `multiServiceNode`");
SuccessOrQuit(multiServiceNode.Get<Dns::Multicast::Core>().UnregisterService(services[2]));
nexus.AdvanceTime(15 * 1000);
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Log("Validate peer table on `node`");
VerifyOrQuit(node.Get<ot::Trel::PeerTable>().GetNumberOfPeers() == 2);
for (const ot::Trel::Peer &peer : node.Get<ot::Trel::PeerTable>())
{
bool found = false;
if (!peer.IsStateValid())
{
continue;
}
VerifyOrQuit(peer.GetServiceName() != nullptr);
VerifyOrQuit(peer.GetHostName() != nullptr);
VerifyOrQuit(StringStartsWith(peer.GetHostName(), "ot"));
VerifyOrQuit(StringEndsWith(peer.GetHostName(),
multiServiceNode.Get<Mac::Mac>().GetExtAddress().ToString().AsCString()));
VerifyOrQuit(peer.GetSockAddr().GetAddress() == multiServiceNode.mMdns.mIfAddresses[0]);
VerifyOrQuit(peer.GetHostAddresses().GetLength() == 1);
VerifyOrQuit(peer.GetHostAddresses()[0] == multiServiceNode.mMdns.mIfAddresses[0]);
for (uint16_t index = 0; index < 2; index++)
{
if (StringMatch(peer.GetServiceName(), services[index].mServiceInstance))
{
found = true;
}
}
VerifyOrQuit(found);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Log("Update the local host addresses on `multiServiceNode`");
SuccessOrQuit(address.FromString("fd00:abba::1234"));
SuccessOrQuit(multiServiceNode.mMdns.mIfAddresses.PushBack(address));
multiServiceNode.mMdns.SignalIfAddresses(multiServiceNode.GetInstance());
nexus.AdvanceTime(5 * 1000);
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Log("Validate all peers get the updated list");
VerifyOrQuit(node.Get<ot::Trel::PeerTable>().GetNumberOfPeers() == 2);
for (const ot::Trel::Peer &peer : node.Get<ot::Trel::PeerTable>())
{
bool found = false;
if (!peer.IsStateValid())
{
continue;
}
VerifyOrQuit(peer.GetServiceName() != nullptr);
VerifyOrQuit(peer.GetHostName() != nullptr);
VerifyOrQuit(StringStartsWith(peer.GetHostName(), "ot"));
VerifyOrQuit(StringEndsWith(peer.GetHostName(),
multiServiceNode.Get<Mac::Mac>().GetExtAddress().ToString().AsCString()));
VerifyOrQuit(peer.GetSockAddr().GetAddress() == multiServiceNode.mMdns.mIfAddresses[0]);
VerifyOrQuit(peer.GetHostAddresses().GetLength() == 2);
VerifyOrQuit(peer.GetHostAddresses()[0] == multiServiceNode.mMdns.mIfAddresses[0]);
VerifyOrQuit(peer.GetHostAddresses()[1] == multiServiceNode.mMdns.mIfAddresses[1]);
for (uint16_t index = 0; index < 2; index++)
{
if (StringMatch(peer.GetServiceName(), services[index].mServiceInstance))
{
found = true;
}
}
VerifyOrQuit(found);
}
}
#endif // OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE
} // namespace Nexus
} // namespace ot
int main(void)
{
#if OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE
ot::Nexus::TestTrelBasic();
ot::Nexus::TestTrelDelayedMdnsStartAndPeerRemovalDelay();
ot::Nexus::TestServiceNameConflict();
ot::Nexus::TestHostAddressChange();
ot::Nexus::TestMultiServiceSameHost();
printf("All tests passed\n");
#else
printf("TREL is not enabled - test skipped\n");
#endif
return 0;
}
@@ -46,6 +46,8 @@
#define OPENTHREAD_CONFIG_PLATFORM_DNSSD_ENABLE 0
#define OPENTHREAD_CONFIG_TREL_MANAGE_DNSSD_ENABLE 1
#ifdef __linux__
#define OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE 1
#endif
@@ -66,12 +68,12 @@
#define OPENTHREAD_POSIX_CONFIG_NAT64_AIL_PREFIX_ENABLE 1
// The following two features (`USE_HEAP`) are disabled explicitly on posix `toranj`
// The following two features (`USE_HEAP`) are set explicitly on posix `toranj`
// to validate the build with these config. The `toranj` build under simulation
// platform covers the opposite configs (allows `USE_HEAP`).
#define OPENTHREAD_CONFIG_BORDER_ROUTING_USE_HEAP_ENABLE 0
#define OPENTHREAD_CONFIG_TREL_USE_HEAP_ENABLE 0
#define OPENTHREAD_CONFIG_TREL_USE_HEAP_ENABLE 1
#endif /* OPENTHREAD_CORE_TORANJ_CONFIG_POSIX_H_ */
@@ -87,6 +87,8 @@
#define OPENTHREAD_CONFIG_RADIO_STATS_ENABLE 0
#define OPENTHREAD_CONFIG_TREL_USE_HEAP_ENABLE 1
#define OPENTHREAD_CONFIG_TREL_MANAGE_DNSSD_ENABLE 0
#define OPENTHREAD_CONFIG_TREL_USE_HEAP_ENABLE 0
#endif /* OPENTHREAD_CORE_TORANJ_CONFIG_SIMULATION_H_ */