[trel-dnssd] Integrate TREL DNS-SD with avahi and mdnssd (#1109)
This commit implements TREL DNS-SD module:
- Register local TREL service to mDNS publishers
- Browse remote TREL services from mDNS Publishers and maintain TREL
peers
Some hints on implementation:
- TREL DNS-SD uses Extended Address as the TREL service instance name
- TREL DNS-SD pauses until all three conditions are met:
1. TREL DNS-SD was enabled by TREL core
2. TREL network interface (e.g. WiFi interface) is ready
3. mDNS Publisher is ready
diff --git a/Android.mk b/Android.mk
index fadc412..5d526fb 100644
--- a/Android.mk
+++ b/Android.mk
@@ -135,6 +135,7 @@
src/sdp_proxy/discovery_proxy.cpp \
src/utils/dns_utils.cpp \
src/utils/hex.cpp \
+ src/utils/string_utils.cpp \
src/utils/thread_helper.cpp \
LOCAL_STATIC_LIBRARIES += \
diff --git a/etc/cmake/options.cmake b/etc/cmake/options.cmake
index 5a03d62..a840b82 100644
--- a/etc/cmake/options.cmake
+++ b/etc/cmake/options.cmake
@@ -79,4 +79,10 @@
target_compile_definitions(otbr-config INTERFACE OTBR_ENABLE_UNSECURE_JOIN=1)
endif()
+option(OTBR_TREL "Enable TREL link support." OFF)
+if(OTBR_TREL)
+ target_compile_definitions(otbr-config INTERFACE OTBR_ENABLE_TREL=1)
+endif()
+
+
option(OTBR_WEB "Enable Web GUI" OFF)
diff --git a/etc/docker/docker_entrypoint.sh b/etc/docker/docker_entrypoint.sh
index 3cc1d02..60cfb88 100755
--- a/etc/docker/docker_entrypoint.sh
+++ b/etc/docker/docker_entrypoint.sh
@@ -38,6 +38,11 @@
shift
shift
;;
+ --trel-url)
+ TREL_URL="$2"
+ shift
+ shift
+ ;;
--interface | -I)
TUN_INTERFACE_NAME=$2
shift
@@ -71,6 +76,7 @@
parse_args "$@"
[ -n "$RADIO_URL" ] || RADIO_URL="spinel+hdlc+uart:///dev/ttyUSB0"
+[ -n "$TREL_URL" ] || TREL_URL=""
[ -n "$TUN_INTERFACE_NAME" ] || TUN_INTERFACE_NAME="wpan0"
[ -n "$BACKBONE_INTERFACE" ] || BACKBONE_INTERFACE="eth0"
[ -n "$AUTO_PREFIX_ROUTE" ] || AUTO_PREFIX_ROUTE=true
@@ -78,6 +84,7 @@
[ -n "$NAT64_PREFIX" ] || NAT64_PREFIX="64:ff9b::/96"
echo "RADIO_URL:" $RADIO_URL
+echo "TREL_URL:" $TREL_URL
echo "TUN_INTERFACE_NAME:" $TUN_INTERFACE_NAME
echo "BACKBONE_INTERFACE: $BACKBONE_INTERFACE"
echo "NAT64_PREFIX:" $NAT64_PREFIX
@@ -90,7 +97,7 @@
sed -i "s/dns64.*$/dns64 $NAT64_PREFIX {};/" /etc/bind/named.conf.options
sed -i "s/$INFRA_IF_NAME/$BACKBONE_INTERFACE/" /etc/sysctl.d/60-otbr-accept-ra.conf
-echo "OTBR_AGENT_OPTS=\"-I $TUN_INTERFACE_NAME -B $BACKBONE_INTERFACE -d7 $RADIO_URL\"" >/etc/default/otbr-agent
+echo "OTBR_AGENT_OPTS=\"-I $TUN_INTERFACE_NAME -B $BACKBONE_INTERFACE -d7 $RADIO_URL $TREL_URL\"" >/etc/default/otbr-agent
echo "OTBR_WEB_OPTS=\"-I $TUN_INTERFACE_NAME -d7 -p 80\"" >/etc/default/otbr-web
/app/script/server
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index b9027e1..26f3b17 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -33,6 +33,7 @@
add_subdirectory(common)
add_subdirectory(ncp)
add_subdirectory(sdp_proxy)
+add_subdirectory(trel_dnssd)
if(OTBR_DBUS)
add_subdirectory(dbus)
diff --git a/src/border_agent/CMakeLists.txt b/src/border_agent/CMakeLists.txt
index 9b60a6e..c58acdb 100644
--- a/src/border_agent/CMakeLists.txt
+++ b/src/border_agent/CMakeLists.txt
@@ -34,5 +34,6 @@
target_link_libraries(otbr-border-agent PRIVATE
$<$<BOOL:${OTBR_MDNS}>:otbr-mdns>
$<$<BOOL:${OTBR_BACKBONE_ROUTER}>:otbr-backbone-router>
+ otbr-trel-dnssd
otbr-common
)
diff --git a/src/border_agent/border_agent.cpp b/src/border_agent/border_agent.cpp
index a4adaba..87490ca 100644
--- a/src/border_agent/border_agent.cpp
+++ b/src/border_agent/border_agent.cpp
@@ -101,6 +101,9 @@
#if OTBR_ENABLE_DNSSD_DISCOVERY_PROXY
, mDiscoveryProxy(aNcp, *mPublisher)
#endif
+#if OTBR_ENABLE_TREL
+ , mTrelDnssd(aNcp, *mPublisher)
+#endif
{
}
@@ -177,6 +180,9 @@
#if OTBR_ENABLE_SRP_ADVERTISING_PROXY
mAdvertisingProxy.PublishAllHostsAndServices();
#endif
+#if OTBR_ENABLE_TREL
+ mTrelDnssd.OnMdnsPublisherReady();
+#endif
break;
default:
otbrLogWarning("mDNS publisher not available!");
diff --git a/src/border_agent/border_agent.hpp b/src/border_agent/border_agent.hpp
index d946e57..ece48c6 100644
--- a/src/border_agent/border_agent.hpp
+++ b/src/border_agent/border_agent.hpp
@@ -50,6 +50,7 @@
#include "ncp/ncp_openthread.hpp"
#include "sdp_proxy/advertising_proxy.hpp"
#include "sdp_proxy/discovery_proxy.hpp"
+#include "trel_dnssd/trel_dnssd.hpp"
#ifndef OTBR_VENDOR_NAME
#define OTBR_VENDOR_NAME "OpenThread"
@@ -176,6 +177,9 @@
#if OTBR_ENABLE_DNSSD_DISCOVERY_PROXY
Dnssd::DiscoveryProxy mDiscoveryProxy;
#endif
+#if OTBR_ENABLE_TREL
+ TrelDnssd::TrelDnssd mTrelDnssd;
+#endif
};
/**
diff --git a/src/common/logging.cpp b/src/common/logging.cpp
index 7e08172..01c8811 100644
--- a/src/common/logging.cpp
+++ b/src/common/logging.cpp
@@ -127,7 +127,7 @@
}
/** Hex dump data to the log */
-void otbrDump(otbrLogLevel aLevel, const char *aPrefix, const void *aMemory, size_t aSize)
+void otbrDump(otbrLogLevel aLevel, const char *aLogTag, const char *aPrefix, const void *aMemory, size_t aSize)
{
static const char kHexChars[] = "0123456789abcdef";
assert(aPrefix && (aMemory || aSize == 0));
@@ -173,7 +173,7 @@
}
*ch = 0;
- syslog(static_cast<int>(aLevel), "%s: %04x: %s", aPrefix, addr, hex);
+ otbrLog(aLevel, aLogTag, "%s: %04x: %s", aPrefix, addr, hex);
}
}
diff --git a/src/common/logging.hpp b/src/common/logging.hpp
index 9d6f61a..e90ba26 100644
--- a/src/common/logging.hpp
+++ b/src/common/logging.hpp
@@ -117,12 +117,13 @@
* This function dump memory as hex string at level @p aLevel.
*
* @param[in] aLevel Log level of the logger.
+ * @param[in] aLogTag Log tag.
* @param[in] aPrefix String before dumping memory.
* @param[in] aMemory The pointer to the memory to be dumped.
* @param[in] aSize The size of memory in bytes to be dumped.
*
*/
-void otbrDump(otbrLogLevel aLevel, const char *aPrefix, const void *aMemory, size_t aSize);
+void otbrDump(otbrLogLevel aLevel, const char *aLogTag, const char *aPrefix, const void *aMemory, size_t aSize);
/**
* This function converts error code to string.
diff --git a/src/mdns/mdns.cpp b/src/mdns/mdns.cpp
index e06263c..490b7b6 100644
--- a/src/mdns/mdns.cpp
+++ b/src/mdns/mdns.cpp
@@ -79,6 +79,40 @@
return error;
}
+otbrError Publisher::DecodeTxtData(Publisher::TxtList &aTxtList, const uint8_t *aTxtData, uint16_t aTxtLength)
+{
+ otbrError error = OTBR_ERROR_NONE;
+
+ for (uint16_t r = 0; r < aTxtLength;)
+ {
+ uint16_t entrySize = aTxtData[r];
+ uint16_t keyStart = r + 1;
+ uint16_t entryEnd = keyStart + entrySize;
+ uint16_t keyEnd = keyStart;
+ uint16_t valStart;
+
+ while (keyEnd < entryEnd && aTxtData[keyEnd] != '=')
+ {
+ keyEnd++;
+ }
+
+ valStart = keyEnd;
+ if (valStart < entryEnd && aTxtData[valStart] == '=')
+ {
+ valStart++;
+ }
+
+ aTxtList.emplace_back(reinterpret_cast<const char *>(&aTxtData[keyStart]), keyEnd - keyStart,
+ &aTxtData[valStart], entryEnd - valStart);
+
+ r += entrySize + 1;
+ VerifyOrExit(r <= aTxtLength, error = OTBR_ERROR_PARSE);
+ }
+
+exit:
+ return error;
+}
+
void Publisher::RemoveSubscriptionCallbacks(uint64_t aSubscriberId)
{
size_t erased;
diff --git a/src/mdns/mdns.hpp b/src/mdns/mdns.hpp
index a9b18bf..ab8fc5b 100644
--- a/src/mdns/mdns.hpp
+++ b/src/mdns/mdns.hpp
@@ -416,9 +416,29 @@
* @retval OTBR_ERROR_NONE Successfully write the TXT entry list.
* @retval OTBR_ERROR_INVALID_ARGS The @p aTxtList includes invalid TXT entry.
*
+ * @sa DecodeTxtData
+ *
*/
static otbrError EncodeTxtData(const TxtList &aTxtList, std::vector<uint8_t> &aTxtData);
+ /**
+ * This function decodes a TXT entry list from a TXT data buffer.
+ *
+ * The input data should be in standard DNS-SD TXT data format.
+ * See RFC 6763 for details: https://tools.ietf.org/html/rfc6763#section-6.
+ *
+ * @param[out] aTxtList A TXT entry list.
+ * @param[in] aTxtData A pointer to TXT data.
+ * @param[in] aTxtLength The TXT data length.
+ *
+ * @retval OTBR_ERROR_NONE Successfully decoded the TXT data.
+ * @retval OTBR_ERROR_INVALID_ARGS The @p aTxtdata has invalid TXT format.
+ *
+ * @sa EncodeTxtData
+ *
+ */
+ static otbrError DecodeTxtData(TxtList &aTxtList, const uint8_t *aTxtData, uint16_t aTxtLength);
+
protected:
enum : uint8_t
{
diff --git a/src/trel_dnssd/CMakeLists.txt b/src/trel_dnssd/CMakeLists.txt
new file mode 100644
index 0000000..b0c4a93
--- /dev/null
+++ b/src/trel_dnssd/CMakeLists.txt
@@ -0,0 +1,37 @@
+#
+# Copyright (c) 2021, The OpenThread Authors.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+# 3. Neither the name of the copyright holder nor the
+# names of its contributors may be used to endorse or promote products
+# derived from this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+#
+
+add_library(otbr-trel-dnssd
+ trel_dnssd.cpp
+ trel_dnssd.hpp
+)
+
+target_link_libraries(otbr-trel-dnssd PRIVATE
+ $<$<BOOL:${OTBR_MDNS}>:otbr-mdns>
+ otbr-common
+)
diff --git a/src/trel_dnssd/trel_dnssd.cpp b/src/trel_dnssd/trel_dnssd.cpp
new file mode 100644
index 0000000..e2f9726
--- /dev/null
+++ b/src/trel_dnssd/trel_dnssd.cpp
@@ -0,0 +1,502 @@
+/*
+ * Copyright (c) 2021, The OpenThread Authors.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the copyright holder nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/**
+ * @file
+ * This file includes implementation of TREL DNS-SD over mDNS.
+ */
+
+#if OTBR_ENABLE_TREL
+
+#define OTBR_LOG_TAG "TrelDns"
+
+#include "trel_dnssd/trel_dnssd.hpp"
+
+#include <net/if.h>
+
+#include <openthread/instance.h>
+#include <openthread/link.h>
+#include <openthread/platform/trel.h>
+
+#include "common/code_utils.hpp"
+#include "utils/hex.hpp"
+#include "utils/string_utils.hpp"
+
+static const char kTrelServiceName[] = "_trel._udp";
+
+static otbr::TrelDnssd::TrelDnssd *sTrelDnssd = nullptr;
+
+void trelDnssdInitialize(const char *aTrelNetif)
+{
+ sTrelDnssd->Initialize(aTrelNetif);
+}
+
+void trelDnssdStartBrowse(void)
+{
+ sTrelDnssd->StartBrowse();
+}
+
+void trelDnssdStopBrowse(void)
+{
+ sTrelDnssd->StopBrowse();
+}
+
+void trelDnssdRegisterService(uint16_t aPort, const uint8_t *aTxtData, uint8_t aTxtLength)
+{
+ sTrelDnssd->RegisterService(aPort, aTxtData, aTxtLength);
+}
+
+void trelDnssdRemoveService(void)
+{
+ sTrelDnssd->UnregisterService();
+}
+
+namespace otbr {
+
+namespace TrelDnssd {
+
+TrelDnssd::TrelDnssd(Ncp::ControllerOpenThread &aNcp, Mdns::Publisher &aPublisher)
+ : mPublisher(aPublisher)
+ , mNcp(aNcp)
+{
+ sTrelDnssd = this;
+}
+
+void TrelDnssd::Initialize(std::string aTrelNetif)
+{
+ mTrelNetif = std::move(aTrelNetif);
+
+ if (IsInitialized())
+ {
+ otbrLogDebug("Initialized on netif \"%s\"", mTrelNetif.c_str());
+ CheckTrelNetifReady();
+ }
+ else
+ {
+ otbrLogDebug("Not initialized");
+ }
+}
+
+void TrelDnssd::StartBrowse(void)
+{
+ VerifyOrExit(IsInitialized());
+
+ otbrLogDebug("Start browsing %s services ...", kTrelServiceName);
+
+ assert(mSubscriberId == 0);
+ mSubscriberId = mPublisher.AddSubscriptionCallbacks(
+ [this](const std::string &aType, const Mdns::Publisher::DiscoveredInstanceInfo &aInstanceInfo) {
+ OnTrelServiceInstanceResolved(aType, aInstanceInfo);
+ },
+ /* aHostCallback */ nullptr);
+
+ if (IsReady())
+ {
+ mPublisher.SubscribeService(kTrelServiceName, /* aInstanceName */ "");
+ }
+
+exit:
+ return;
+}
+
+void TrelDnssd::StopBrowse(void)
+{
+ VerifyOrExit(IsInitialized());
+
+ otbrLogDebug("Stop browsing %s service.", kTrelServiceName);
+ assert(mSubscriberId > 0);
+
+ mPublisher.RemoveSubscriptionCallbacks(mSubscriberId);
+ mSubscriberId = 0;
+
+ if (IsReady())
+ {
+ mPublisher.UnsubscribeService(kTrelServiceName, "");
+ }
+
+exit:
+ return;
+}
+
+void TrelDnssd::RegisterService(uint16_t aPort, const uint8_t *aTxtData, uint8_t aTxtLength)
+{
+ assert(aPort > 0);
+ assert(aTxtData != nullptr);
+
+ VerifyOrExit(IsInitialized());
+
+ otbrLogDebug("Register %s service: port=%u, TXT=%d bytes", kTrelServiceName, aPort, aTxtLength);
+ otbrDump(OTBR_LOG_DEBUG, OTBR_LOG_TAG, "TXT", aTxtData, aTxtLength);
+
+ if (mRegisterInfo.IsValid() && IsReady())
+ {
+ UnpublishTrelService();
+ }
+
+ mRegisterInfo.Assign(aPort, aTxtData, aTxtLength);
+
+ if (IsReady())
+ {
+ PublishTrelService();
+ }
+
+exit:
+ return;
+}
+
+void TrelDnssd::UnregisterService(void)
+{
+ VerifyOrExit(IsInitialized());
+
+ otbrLogDebug("Remove %s service", kTrelServiceName);
+ assert(mRegisterInfo.IsValid());
+
+ if (IsReady())
+ {
+ UnpublishTrelService();
+ }
+
+ mRegisterInfo.Clear();
+
+exit:
+ return;
+}
+
+void TrelDnssd::OnMdnsPublisherReady(void)
+{
+ VerifyOrExit(IsInitialized());
+
+ otbrLogDebug("mDNS Publisher is Ready");
+ mMdnsPublisherReady = true;
+ RemoveAllPeers();
+
+ if (mRegisterInfo.IsPublished())
+ {
+ mRegisterInfo.mInstanceName = "";
+ }
+
+ OnBecomeReady();
+
+exit:
+ return;
+}
+
+void TrelDnssd::OnTrelServiceInstanceResolved(const std::string & aType,
+ const Mdns::Publisher::DiscoveredInstanceInfo &aInstanceInfo)
+{
+ VerifyOrExit(StringUtils::EqualCaseInsensitive(aType, kTrelServiceName));
+ VerifyOrExit(aInstanceInfo.mNetifIndex == mTrelNetifIndex);
+
+ if (aInstanceInfo.mRemoved)
+ {
+ OnTrelServiceInstanceRemoved(aInstanceInfo.mName);
+ }
+ else
+ {
+ OnTrelServiceInstanceAdded(aInstanceInfo);
+ }
+
+exit:
+ return;
+}
+
+std::string TrelDnssd::GetTrelInstanceName(void)
+{
+ const otExtAddress *extaddr = otLinkGetExtendedAddress(mNcp.GetInstance());
+ std::string name;
+ char nameBuf[sizeof(extaddr) * 2 + 1];
+
+ Utils::Bytes2Hex(extaddr->m8, sizeof(extaddr), nameBuf);
+ name = StringUtils::ToLowercase(nameBuf);
+
+ assert(name.length() == sizeof(extaddr) * 2);
+
+ otbrLogDebug("Using instance name %s", name.c_str());
+ return name;
+}
+
+void TrelDnssd::PublishTrelService(void)
+{
+ assert(mRegisterInfo.IsValid());
+ assert(!mRegisterInfo.IsPublished());
+ assert(mTrelNetifIndex > 0);
+
+ mRegisterInfo.mInstanceName = GetTrelInstanceName();
+ mPublisher.PublishService(/* aHostName */ "", mRegisterInfo.mPort, mRegisterInfo.mInstanceName, kTrelServiceName,
+ Mdns::Publisher::SubTypeList{}, mRegisterInfo.mTxtEntries);
+}
+
+void TrelDnssd::UnpublishTrelService(void)
+{
+ assert(mRegisterInfo.IsValid());
+ assert(mRegisterInfo.IsPublished());
+
+ mPublisher.UnpublishService(mRegisterInfo.mInstanceName, kTrelServiceName);
+ mRegisterInfo.mInstanceName = "";
+}
+
+void TrelDnssd::OnTrelServiceInstanceAdded(const Mdns::Publisher::DiscoveredInstanceInfo &aInstanceInfo)
+{
+ std::string instanceName = StringUtils::ToLowercase(aInstanceInfo.mName);
+ otPlatTrelPeerInfo peerInfo;
+
+ // Remove any existing TREL service instance before adding
+ OnTrelServiceInstanceRemoved(instanceName);
+
+ otbrLogDebug("Peer discovered: %s hostname %s addresses %zu port %d priority %d "
+ "weight %d",
+ aInstanceInfo.mName.c_str(), aInstanceInfo.mHostName.c_str(), aInstanceInfo.mAddresses.size(),
+ aInstanceInfo.mPort, aInstanceInfo.mPriority, aInstanceInfo.mWeight);
+
+ for (const auto &addr : aInstanceInfo.mAddresses)
+ {
+ otbrLogDebug("Peer address: %s", addr.ToString().c_str());
+ }
+
+ if (aInstanceInfo.mAddresses.empty())
+ {
+ otbrLogWarning("Peer %s does not have any IPv6 address, ignored", aInstanceInfo.mName.c_str());
+ ExitNow();
+ }
+
+ peerInfo.mRemoved = false;
+ memcpy(&peerInfo.mSockAddr.mAddress, &aInstanceInfo.mAddresses[0], sizeof(peerInfo.mSockAddr.mAddress));
+ peerInfo.mSockAddr.mPort = aInstanceInfo.mPort;
+ peerInfo.mTxtData = aInstanceInfo.mTxtData.data();
+ peerInfo.mTxtLength = aInstanceInfo.mTxtData.size();
+
+ {
+ Peer peer(aInstanceInfo.mTxtData, peerInfo.mSockAddr);
+
+ VerifyOrExit(peer.mValid, otbrLogWarning("Peer %s is invalid", aInstanceInfo.mName.c_str()));
+
+ otPlatTrelHandleDiscoveredPeerInfo(mNcp.GetInstance(), &peerInfo);
+
+ mPeers.emplace(instanceName, peer);
+ CheckPeersNumLimit();
+ }
+
+exit:
+ return;
+}
+
+void TrelDnssd::OnTrelServiceInstanceRemoved(const std::string &aInstanceName)
+{
+ std::string instanceName = StringUtils::ToLowercase(aInstanceName);
+ auto it = mPeers.find(instanceName);
+
+ VerifyOrExit(it != mPeers.end());
+
+ otbrLogDebug("Peer removed: %s", instanceName.c_str());
+
+ // Remove the peer only when all instances are removed because one peer can have multiple instances if expired
+ // instances were not properly removed by mDNS.
+ if (CountDuplicatePeers(it->second) == 0)
+ {
+ NotifyRemovePeer(it->second);
+ }
+
+ mPeers.erase(it);
+
+exit:
+ return;
+}
+
+void TrelDnssd::CheckPeersNumLimit(void)
+{
+ const PeerMap::value_type *oldestPeer = nullptr;
+
+ VerifyOrExit(mPeers.size() >= kPeerCacheSize);
+
+ for (const auto &entry : mPeers)
+ {
+ if (oldestPeer == nullptr || entry.second.mDiscoverTime < oldestPeer->second.mDiscoverTime)
+ {
+ oldestPeer = &entry;
+ }
+ }
+
+ OnTrelServiceInstanceRemoved(oldestPeer->first);
+
+exit:
+ return;
+}
+
+void TrelDnssd::NotifyRemovePeer(const Peer &aPeer)
+{
+ otPlatTrelPeerInfo peerInfo;
+
+ peerInfo.mRemoved = true;
+ peerInfo.mTxtData = aPeer.mTxtData.data();
+ peerInfo.mTxtLength = aPeer.mTxtData.size();
+ peerInfo.mSockAddr = aPeer.mSockAddr;
+
+ otPlatTrelHandleDiscoveredPeerInfo(mNcp.GetInstance(), &peerInfo);
+}
+
+void TrelDnssd::RemoveAllPeers(void)
+{
+ for (const auto &entry : mPeers)
+ {
+ NotifyRemovePeer(entry.second);
+ }
+
+ mPeers.clear();
+}
+
+void TrelDnssd::CheckTrelNetifReady(void)
+{
+ assert(IsInitialized());
+
+ if (mTrelNetifIndex == 0)
+ {
+ mTrelNetifIndex = if_nametoindex(mTrelNetif.c_str());
+
+ if (mTrelNetifIndex != 0)
+ {
+ otbrLogDebug("Netif %s is ready: index = %d", mTrelNetif.c_str(), mTrelNetifIndex);
+ OnBecomeReady();
+ }
+ else
+ {
+ uint16_t delay = kCheckNetifReadyIntervalMs;
+
+ otbrLogWarning("Netif %s is not ready (%s), will retry after %d seconds", mTrelNetif.c_str(),
+ strerror(errno), delay / 1000);
+ mTaskRunner.Post(Milliseconds(delay), [this]() { CheckTrelNetifReady(); });
+ }
+ }
+}
+
+bool TrelDnssd::IsReady(void) const
+{
+ assert(IsInitialized());
+
+ return mTrelNetifIndex > 0 && mMdnsPublisherReady;
+}
+
+void TrelDnssd::OnBecomeReady(void)
+{
+ if (IsReady())
+ {
+ otbrLogInfo("TREL DNS-SD Is Now Ready: Netif=%s(%u), SubscriberId=%u, Register=%s!", mTrelNetif.c_str(),
+ mTrelNetifIndex, mSubscriberId, mRegisterInfo.mInstanceName.c_str());
+
+ if (mSubscriberId > 0)
+ {
+ mPublisher.SubscribeService(kTrelServiceName, /* aInstanceName */ "");
+ }
+
+ if (mRegisterInfo.IsValid())
+ {
+ PublishTrelService();
+ }
+ }
+}
+
+uint16_t TrelDnssd::CountDuplicatePeers(const TrelDnssd::Peer &aPeer)
+{
+ uint16_t count = 0;
+
+ for (const auto &entry : mPeers)
+ {
+ if (&entry.second == &aPeer)
+ {
+ continue;
+ }
+
+ if (!memcmp(&entry.second.mSockAddr, &aPeer.mSockAddr, sizeof(otSockAddr)) &&
+ !memcmp(&entry.second.mExtAddr, &aPeer.mExtAddr, sizeof(otExtAddress)))
+ {
+ count++;
+ }
+ }
+
+ return count;
+}
+
+void TrelDnssd::RegisterInfo::Assign(uint16_t aPort, const uint8_t *aTxtData, uint8_t aTxtLength)
+{
+ otbrError error;
+
+ OTBR_UNUSED_VARIABLE(error);
+
+ assert(!IsPublished());
+ assert(aPort > 0);
+
+ mPort = aPort;
+ mTxtEntries.clear();
+
+ error = Mdns::Publisher::DecodeTxtData(mTxtEntries, aTxtData, aTxtLength);
+ assert(error == OTBR_ERROR_NONE);
+}
+
+void TrelDnssd::RegisterInfo::Clear(void)
+{
+ assert(!IsPublished());
+
+ mPort = 0;
+ mTxtEntries.clear();
+}
+
+const char TrelDnssd::Peer::kTxtRecordExtAddressKey[] = "xa";
+
+void TrelDnssd::Peer::ReadExtAddrFromTxtData(void)
+{
+ std::vector<Mdns::Publisher::TxtEntry> txtEntries;
+
+ memset(&mExtAddr, 0, sizeof(mExtAddr));
+
+ SuccessOrExit(Mdns::Publisher::DecodeTxtData(txtEntries, mTxtData.data(), mTxtData.size()));
+
+ for (const auto &txtEntry : txtEntries)
+ {
+ if (StringUtils::EqualCaseInsensitive(txtEntry.mName, kTxtRecordExtAddressKey))
+ {
+ char extAddrHexBuf[sizeof(mExtAddr) * 2 + 1];
+
+ VerifyOrExit(txtEntry.mValue.size() == sizeof(mExtAddr) * 2);
+
+ memcpy(extAddrHexBuf, txtEntry.mValue.data(), sizeof(mExtAddr) * 2);
+ extAddrHexBuf[sizeof(mExtAddr) * 2] = '\0';
+
+ VerifyOrExit(Utils::Hex2Bytes(extAddrHexBuf, mExtAddr.m8, sizeof(mExtAddr)) == sizeof(mExtAddr));
+ mValid = true;
+ break;
+ }
+ }
+
+exit:
+ return;
+}
+
+} // namespace TrelDnssd
+
+} // namespace otbr
+
+#endif // OTBR_ENABLE_TREL
diff --git a/src/trel_dnssd/trel_dnssd.hpp b/src/trel_dnssd/trel_dnssd.hpp
new file mode 100644
index 0000000..46c8f10
--- /dev/null
+++ b/src/trel_dnssd/trel_dnssd.hpp
@@ -0,0 +1,194 @@
+/*
+ * Copyright (c) 2021, The OpenThread Authors.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the copyright holder nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/**
+ * @file
+ * This file includes definitions for TREL DNS-SD over mDNS.
+ */
+
+#ifndef OTBR_AGENT_TREL_DNSSD_HPP_
+#define OTBR_AGENT_TREL_DNSSD_HPP_
+
+#if OTBR_ENABLE_TREL
+
+#include <assert.h>
+#include <utility>
+
+#include <openthread/instance.h>
+
+#include "common/types.hpp"
+#include "mdns/mdns.hpp"
+#include "ncp/ncp_openthread.hpp"
+
+namespace otbr {
+
+namespace TrelDnssd {
+
+/**
+ * @addtogroup border-router-trel-dnssd
+ *
+ * @brief
+ * This module includes definition for TREL DNS-SD over mDNS.
+ *
+ * @{
+ */
+
+class TrelDnssd
+{
+public:
+ /**
+ * This constructor initializes the TrelDnssd instance.
+ *
+ * @param[in] aNcp A reference to the OpenThread Controller instance.
+ * @param[in] aPublisher A reference to the mDNS Publisher.
+ *
+ */
+ explicit TrelDnssd(Ncp::ControllerOpenThread &aNcp, Mdns::Publisher &aPublisher);
+
+ /**
+ * This method initializes the TrelDnssd instance.
+ *
+ * @param[in] aTrelNetif The network interface for discovering TREL peers.
+ *
+ */
+ void Initialize(std::string aTrelNetif);
+
+ /**
+ * This method starts browsing for TREL peers.
+ *
+ */
+ void StartBrowse(void);
+
+ /**
+ * This method stops browsing for TREL peers.
+ *
+ */
+ void StopBrowse(void);
+
+ /**
+ * This method registers the TREL service to DNS-SD.
+ *
+ * @param[in] aPort The UDP port of TREL service.
+ * @param[in] aTxtData The TXT data of TREL service.
+ * @param[in] aTxtLength The TXT length of TREL service.
+ *
+ */
+ void RegisterService(uint16_t aPort, const uint8_t *aTxtData, uint8_t aTxtLength);
+
+ /**
+ * This method removes the TREL service from DNS-SD.
+ *
+ */
+ void UnregisterService(void);
+
+ /**
+ * This method notifies that mDNS Publisher is ready.
+ *
+ */
+ void OnMdnsPublisherReady(void);
+
+private:
+ static constexpr size_t kPeerCacheSize = 256;
+ static constexpr uint16_t kCheckNetifReadyIntervalMs = 5000;
+
+ struct RegisterInfo
+ {
+ uint16_t mPort = 0;
+ std::vector<Mdns::Publisher::TxtEntry> mTxtEntries;
+ std::string mInstanceName;
+
+ bool IsValid(void) const { return mPort > 0; }
+ bool IsPublished(void) const { return !mInstanceName.empty(); }
+ void Assign(uint16_t aPort, const uint8_t *aTxtData, uint8_t aTxtLength);
+ void Clear(void);
+ };
+
+ using Clock = std::chrono::system_clock;
+
+ struct Peer
+ {
+ static const char kTxtRecordExtAddressKey[];
+
+ explicit Peer(std::vector<uint8_t> aTxtData, const otSockAddr &aSockAddr)
+ : mDiscoverTime(Clock::now())
+ , mTxtData(std::move(aTxtData))
+ , mSockAddr(aSockAddr)
+ {
+ ReadExtAddrFromTxtData();
+ }
+
+ void ReadExtAddrFromTxtData(void);
+
+ Clock::time_point mDiscoverTime;
+ std::vector<uint8_t> mTxtData;
+ otSockAddr mSockAddr;
+ otExtAddress mExtAddr;
+ bool mValid = false;
+ };
+
+ using PeerMap = std::map<std::string, Peer>;
+
+ bool IsInitialized(void) const { return !mTrelNetif.empty(); }
+ bool IsReady(void) const;
+ void OnBecomeReady(void);
+ void CheckTrelNetifReady(void);
+ std::string GetTrelInstanceName(void);
+ void PublishTrelService(void);
+ void UnpublishTrelService(void);
+ void OnTrelServiceInstanceResolved(const std::string & aType,
+ const Mdns::Publisher::DiscoveredInstanceInfo &aInstanceInfo);
+ void OnTrelServiceInstanceAdded(const Mdns::Publisher::DiscoveredInstanceInfo &aInstanceInfo);
+ void OnTrelServiceInstanceRemoved(const std::string &aInstanceName);
+
+ void NotifyRemovePeer(const Peer &aPeer);
+ void CheckPeersNumLimit(void);
+ void RemoveAllPeers(void);
+ uint16_t CountDuplicatePeers(const Peer &aPeer);
+
+ Mdns::Publisher & mPublisher;
+ Ncp::ControllerOpenThread &mNcp;
+ TaskRunner mTaskRunner;
+ std::string mTrelNetif;
+ uint32_t mTrelNetifIndex = 0;
+ uint64_t mSubscriberId = 0;
+ RegisterInfo mRegisterInfo;
+ PeerMap mPeers;
+ bool mMdnsPublisherReady = false;
+};
+
+/**
+ * @}
+ */
+
+} // namespace TrelDnssd
+
+} // namespace otbr
+
+#endif // OTBR_ENABLE_TREL
+
+#endif // OTBR_AGENT_TREL_DNSSD_HPP_
diff --git a/tests/unit/test_logging.cpp b/tests/unit/test_logging.cpp
index dccfc50..b10475b 100644
--- a/tests/unit/test_logging.cpp
+++ b/tests/unit/test_logging.cpp
@@ -92,7 +92,7 @@
sprintf(ident, "otbr-test-%ld", clock());
otbrLogInit(ident, OTBR_LOG_DEBUG, true);
const char s[] = "one super long string with lots of text";
- otbrDump(OTBR_LOG_INFO, "foobar", s, sizeof(s));
+ otbrDump(OTBR_LOG_INFO, "Test", "foobar", s, sizeof(s));
otbrLogDeinit();
sleep(0);
diff --git a/third_party/openthread/CMakeLists.txt b/third_party/openthread/CMakeLists.txt
index 5f86336..82c444f 100644
--- a/third_party/openthread/CMakeLists.txt
+++ b/third_party/openthread/CMakeLists.txt
@@ -40,7 +40,7 @@
set(OT_LINK_METRICS_SUBJECT ON CACHE BOOL "enable link metrics subject" FORCE)
endif()
set(OT_SLAAC ON CACHE BOOL "enable SLAAC" FORCE)
-set(OT_TREL ON CACHE BOOL "enable TREL")
+set(OT_TREL ${OTBR_TREL} CACHE BOOL "enable TREL" FORCE)
if (NOT OT_LOG_LEVEL)
if (CMAKE_BUILD_TYPE STREQUAL "Debug")