From 853238d8307e1df2a965707f6b603d419f2516be Mon Sep 17 00:00:00 2001 From: Handa Wang Date: Tue, 7 Feb 2023 00:10:30 +0800 Subject: [PATCH] save --- etc/cmake/options.cmake | 10 + src/CMakeLists.txt | 2 + src/agent/CMakeLists.txt | 1 + src/agent/application.cpp | 3 + src/agent/application.hpp | 4 + src/border_agent/CMakeLists.txt | 1 + src/border_agent/border_agent.cpp | 3 + src/border_agent/border_agent.hpp | 4 + src/dso/CMakeLists.txt | 39 +++ src/dso/dso_transport.cpp | 457 ++++++++++++++++++++++++++ src/dso/dso_transport.hpp | 240 ++++++++++++++ src/mdns/mdns.cpp | 12 + src/mdns/mdns.hpp | 11 + src/sdp_proxy/advertising_proxy.cpp | 9 + src/srpl_dnssd/CMakeLists.txt | 37 +++ src/srpl_dnssd/srpl_dnssd.cpp | 190 +++++++++++ src/srpl_dnssd/srpl_dnssd.hpp | 122 +++++++ third_party/openthread/CMakeLists.txt | 7 + 18 files changed, 1152 insertions(+) create mode 100644 src/dso/CMakeLists.txt create mode 100644 src/dso/dso_transport.cpp create mode 100644 src/dso/dso_transport.hpp create mode 100644 src/srpl_dnssd/CMakeLists.txt create mode 100644 src/srpl_dnssd/srpl_dnssd.cpp create mode 100644 src/srpl_dnssd/srpl_dnssd.hpp diff --git a/etc/cmake/options.cmake b/etc/cmake/options.cmake index 908bf0adc0f..ff36e7d110d 100644 --- a/etc/cmake/options.cmake +++ b/etc/cmake/options.cmake @@ -116,6 +116,16 @@ if(OTBR_NAT64) target_compile_definitions(otbr-config INTERFACE OTBR_ENABLE_NAT64=1) endif() +option(OTBR_DNS_DSO "Enable DSO" OFF) +if(OTBR_DNS_DSO) + target_compile_definitions(otbr-config INTERFACE OTBR_ENABLE_DNS_DSO=1) +endif() + +option(OTBR_SRP_REPLICATION "Enable SRP Replication" OFF) +if(OTBR_SRP_REPLICATION) + target_compile_definitions(otbr-config INTERFACE OTBR_ENABLE_SRP_REPLICATION=1) +endif() + option(OTBR_VENDOR_INFRA_LINK_SELECT "Enable Vendor-specific infrastructure link selection rules" OFF) if(OTBR_VENDOR_INFRA_LINK_SELECT) target_compile_definitions(otbr-config INTERFACE OTBR_ENABLE_VENDOR_INFRA_LINK_SELECT=1) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index ba8f05d9363..219d87b6071 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -34,8 +34,10 @@ add_subdirectory(common) if(OTBR_FEATURE_FLAGS) add_subdirectory(proto) endif() +add_subdirectory(dso) add_subdirectory(ncp) add_subdirectory(sdp_proxy) +add_subdirectory(srpl_dnssd) add_subdirectory(trel_dnssd) if(OTBR_DBUS) diff --git a/src/agent/CMakeLists.txt b/src/agent/CMakeLists.txt index c16a5e5e820..a323ffd84c7 100644 --- a/src/agent/CMakeLists.txt +++ b/src/agent/CMakeLists.txt @@ -45,6 +45,7 @@ target_link_libraries(otbr-agent PRIVATE openthread-ftd openthread-spinel-rcp openthread-hdlc + otbr-dso otbr-sdp-proxy otbr-ncp otbr-common diff --git a/src/agent/application.cpp b/src/agent/application.cpp index ad834303d0b..4f5e0addbf9 100644 --- a/src/agent/application.cpp +++ b/src/agent/application.cpp @@ -78,6 +78,9 @@ Application::Application(const std::string &aInterfaceName, #if OTBR_ENABLE_VENDOR_SERVER , mVendorServer(mNcp) #endif +#if OTBR_ENABLE_DNS_DSO + , mDsoAgent() +#endif { OTBR_UNUSED_VARIABLE(aRestListenAddress); } diff --git a/src/agent/application.hpp b/src/agent/application.hpp index 5c9e2163557..6bd75e244f0 100644 --- a/src/agent/application.hpp +++ b/src/agent/application.hpp @@ -58,6 +58,7 @@ #if OTBR_ENABLE_VENDOR_SERVER #include "agent/vendor.hpp" #endif +#include "dso/dso_transport.hpp" #include "utils/infra_link_selector.hpp" namespace otbr { @@ -144,6 +145,9 @@ class Application : private NonCopyable #if OTBR_ENABLE_VENDOR_SERVER vendor::VendorServer mVendorServer; #endif +#if OTBR_ENABLE_DNS_DSO + Dso::DsoAgent mDsoAgent; +#endif static std::atomic_bool sShouldTerminate; }; diff --git a/src/border_agent/CMakeLists.txt b/src/border_agent/CMakeLists.txt index c58acdbc46f..cb2f38e9fe5 100644 --- a/src/border_agent/CMakeLists.txt +++ b/src/border_agent/CMakeLists.txt @@ -34,6 +34,7 @@ add_library(otbr-border-agent target_link_libraries(otbr-border-agent PRIVATE $<$:otbr-mdns> $<$:otbr-backbone-router> + otbr-srpl-dnssd otbr-trel-dnssd otbr-common ) diff --git a/src/border_agent/border_agent.cpp b/src/border_agent/border_agent.cpp index 382b5597cb5..5c402f8ce37 100644 --- a/src/border_agent/border_agent.cpp +++ b/src/border_agent/border_agent.cpp @@ -149,6 +149,9 @@ BorderAgent::BorderAgent(otbr::Ncp::ControllerOpenThread &aNcp) #if OTBR_ENABLE_TREL , mTrelDnssd(aNcp, *mPublisher) #endif +#if OTBR_ENABLE_SRP_REPLICATION + , mSrplDnssd(aNcp, *mPublisher) +#endif { } diff --git a/src/border_agent/border_agent.hpp b/src/border_agent/border_agent.hpp index 896281e6db5..c76f3ad6c4a 100644 --- a/src/border_agent/border_agent.hpp +++ b/src/border_agent/border_agent.hpp @@ -49,6 +49,7 @@ #include "ncp/ncp_openthread.hpp" #include "sdp_proxy/advertising_proxy.hpp" #include "sdp_proxy/discovery_proxy.hpp" +#include "srpl_dnssd/srpl_dnssd.hpp" #include "trel_dnssd/trel_dnssd.hpp" #ifndef OTBR_VENDOR_NAME @@ -144,6 +145,9 @@ class BorderAgent : private NonCopyable #if OTBR_ENABLE_TREL TrelDnssd::TrelDnssd mTrelDnssd; #endif +#if OTBR_ENABLE_SRP_REPLICATION + SrplDnssd::SrplDnssd mSrplDnssd; +#endif std::string mServiceInstanceName; }; diff --git a/src/dso/CMakeLists.txt b/src/dso/CMakeLists.txt new file mode 100644 index 00000000000..1fd6117cb51 --- /dev/null +++ b/src/dso/CMakeLists.txt @@ -0,0 +1,39 @@ +# +# Copyright (c) 2023, 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-dso + dso_transport.cpp + dso_transport.hpp + ) + +target_link_libraries(otbr-dso + PUBLIC + mbedtls + PRIVATE + otbr-common +) diff --git a/src/dso/dso_transport.cpp b/src/dso/dso_transport.cpp new file mode 100644 index 00000000000..5fe205943a3 --- /dev/null +++ b/src/dso/dso_transport.cpp @@ -0,0 +1,457 @@ +/* + * Copyright (c) 2023, 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. + */ + +#if OTBR_ENABLE_DNS_DSO + +#define OTBR_LOG_TAG "DSO" + +#include "dso_transport.hpp" + +#include +#include + +#include "mbedtls/net_sockets.h" +#include "openthread/openthread-system.h" +#include "openthread/platform/dso_transport.h" + +static otbr::Dso::DsoAgent *sDsoAgent = nullptr; + +extern "C" void otPlatDsoEnableListening(otInstance *aInstance, bool aEnabled) +{ + sDsoAgent->SetEnabled(aInstance, aEnabled); +} + +extern "C" void otPlatDsoConnect(otPlatDsoConnection *aConnection, const otSockAddr *aPeerSockAddr) +{ + sDsoAgent->FindOrCreate(aConnection)->Connect(aPeerSockAddr); +} + +extern "C" void otPlatDsoSend(otPlatDsoConnection *aConnection, otMessage *aMessage) +{ + auto conn = sDsoAgent->Find(aConnection); + + VerifyOrExit(conn != nullptr); + conn->Send(aMessage); + +exit: + otMessageFree(aMessage); +} + +extern "C" void otPlatDsoDisconnect(otPlatDsoConnection *aConnection, otPlatDsoDisconnectMode aMode) +{ + auto conn = sDsoAgent->Find(aConnection); + + VerifyOrExit(conn != nullptr); + conn->Disconnect(aMode); + + sDsoAgent->Remove(aConnection); + +exit: + return; +} + +extern "C" void platformDsoProcess(otInstance *aInstance) +{ + sDsoAgent->ProcessConnections(); + sDsoAgent->HandleIncomingConnections(aInstance); +} + +namespace otbr { +namespace Dso { + +DsoAgent::DsoAgent(void) +{ + mbedtls_net_init(&mListeningCtx); + sDsoAgent = this; +} + +DsoAgent::DsoConnection *DsoAgent::Find(otPlatDsoConnection *aConnection) +{ + DsoConnection *ret = nullptr; + auto it = mMap.find(aConnection); + + if (it != mMap.end()) + { + ret = it->second.get(); + } + + return ret; +} + +DsoAgent::DsoConnection *DsoAgent::FindOrCreate(otPlatDsoConnection *aConnection) +{ + auto &ret = mMap[aConnection]; + + if (!ret) + { + ret = MakeUnique(aConnection); + } + + return ret.get(); +} + +DsoAgent::DsoConnection *DsoAgent::FindOrCreate(otPlatDsoConnection *aConnection, mbedtls_net_context aCtx) +{ + auto &ret = mMap[aConnection]; + + if (!ret) + { + ret = MakeUnique(aConnection, aCtx); + } + + return ret.get(); +} + +void DsoAgent::ProcessConnections(void) +{ + std::vector connections; + + connections.reserve(mMap.size()); + for (auto &conn : mMap) + { + connections.push_back(conn.second.get()); + } + for (const auto &conn : connections) + { + conn->HandleReceive(); + if (!conn->IsConnected()) + { + Remove(conn->GetOtPlatDsoConnection()); + } + } +} + +void DsoAgent::HandleIncomingConnections(otInstance *aInstance) +{ + mbedtls_net_context incomingCtx; + uint8_t address[sizeof(sockaddr_in6)]; + size_t len; + int ret = 0; + + VerifyOrExit(mListeningEnabled); + + while (!(ret = mbedtls_net_accept(&mListeningCtx, &incomingCtx, &address, sizeof(address), &len))) + { + HandleIncomingConnection(aInstance, incomingCtx, address, len); + } + + if (ret != MBEDTLS_ERR_SSL_WANT_READ) + { + otbrLogWarning("Failed to accept incoming connection: %d", ret); + } + +exit: + + return; +} + +void DsoAgent::Enable(otInstance *aInstance) +{ + OTBR_UNUSED_VARIABLE(aInstance); + + constexpr int kOne = 1; + sockaddr_in6 sockAddr; + + VerifyOrExit(!mListeningEnabled); + + mListeningCtx.fd = socket(AF_INET6, SOCK_STREAM, IPPROTO_TCP); + + VerifyOrExit(!setsockopt(mListeningCtx.fd, SOL_SOCKET, SO_BINDTODEVICE, otSysGetInfraNetifName(), + strlen(otSysGetInfraNetifName()))); + VerifyOrExit(!setsockopt(mListeningCtx.fd, SOL_SOCKET, SO_REUSEADDR, (const uint8_t *)&kOne, sizeof(kOne))); + VerifyOrExit(!setsockopt(mListeningCtx.fd, SOL_SOCKET, SO_REUSEPORT, (const uint8_t *)&kOne, sizeof(kOne))); + + sockAddr.sin6_family = AF_INET6; + sockAddr.sin6_addr = in6addr_any; + sockAddr.sin6_port = htons(kListeningPort); + VerifyOrExit(!bind(mListeningCtx.fd, (struct sockaddr *)&sockAddr, sizeof(sockAddr))); + VerifyOrExit(!mbedtls_net_set_nonblock(&mListeningCtx)); + VerifyOrExit(!listen(mListeningCtx.fd, kMaxQueuedConnections)); + + mListeningEnabled = true; + + otbrLogInfo("DSO socket starts listening"); + +exit: + return; +} + +void DsoAgent::Disable(otInstance *aInstance) +{ + OTBR_UNUSED_VARIABLE(aInstance); + + VerifyOrExit(mListeningEnabled); + + mbedtls_net_close(&mListeningCtx); + mMap.clear(); + mListeningEnabled = false; + +exit: + return; +} + +void DsoAgent::SetEnabled(otInstance *aInstance, bool aEnabled) +{ + if (aEnabled) + { + Enable(aInstance); + } + else + { + Disable(aInstance); + } +} + +void DsoAgent::Remove(otPlatDsoConnection *aConnection) +{ + mMap.erase(aConnection); +} + +otError DsoAgent::DsoConnection::Connect(const otSockAddr *aPeerSockAddr) +{ + otError error = OT_ERROR_NONE; + int ret; + char addrBuf[OT_IP6_ADDRESS_STRING_SIZE]; + std::string portString; + + VerifyOrExit(!mConnected); + + mPeerSockAddr = *aPeerSockAddr; + portString = std::to_string(aPeerSockAddr->mPort); + otIp6AddressToString(&aPeerSockAddr->mAddress, addrBuf, sizeof(addrBuf)); + + otbrLogInfo("Connecting to %s:%s", addrBuf, portString.c_str()); + + VerifyOrExit(!(ret = mbedtls_net_connect(&mCtx, addrBuf, portString.c_str(), MBEDTLS_NET_PROTO_TCP)), + otbrLogWarning("Failed to connect: %d", ret)); + VerifyOrExit(!(ret = mbedtls_net_set_nonblock(&mCtx)), otbrLogWarning("Failed to set non-blocking: %d", ret)); + + mConnected = true; + otPlatDsoHandleConnected(mConnection); + +exit: + if (!mConnected) + { + error = OT_ERROR_FAILED; + otPlatDsoHandleDisconnected(mConnection, OT_PLAT_DSO_DISCONNECT_MODE_FORCIBLY_ABORT); + } + return error; +} + +void DsoAgent::DsoConnection::Disconnect(otPlatDsoDisconnectMode aMode) +{ + struct linger l; + + VerifyOrExit(mConnected); + + switch (aMode) + { + case OT_PLAT_DSO_DISCONNECT_MODE_FORCIBLY_ABORT: + l.l_onoff = 1; + l.l_linger = 0; + setsockopt(mCtx.fd, SOL_SOCKET, SO_LINGER, &l, sizeof(l)); + break; + case OT_PLAT_DSO_DISCONNECT_MODE_GRACEFULLY_CLOSE: + break; + default: + otbrLogWarning("Unknown disconnection mode: %d", aMode); + break; + } + + mbedtls_net_close(&mCtx); + mConnected = false; + mbedtls_net_init(&mCtx); + +exit: + return; +} + +void DsoAgent::DsoConnection::Send(otMessage *aMessage) +{ + uint16_t length = otMessageGetLength(aMessage); + std::vector buf(length + sizeof(uint16_t)); + uint16_t lengthInBigEndian = htons(length); + int writeLen; + + VerifyOrExit(mConnected); + + otbrLogDebug("Sending a message with length %" PRIu16, length); + + memcpy(buf.data(), &lengthInBigEndian, sizeof(uint16_t)); + VerifyOrExit(length == otMessageRead(aMessage, 0, buf.data() + sizeof(uint16_t), length), + otbrLogWarning("Failed to read message data")); + writeLen = mbedtls_net_send(&mCtx, buf.data(), buf.size()); + if (writeLen < length) + { + otbrLogWarning("Failed to send DSO message: %d", writeLen); + } + HandleMbedTlsError(writeLen); + + // TODO: May need to keep sending until all the data is sent +exit: + return; +} + +void DsoAgent::DsoConnection::HandleReceive(void) +{ + int readLen; + uint8_t buf[kRxBufferSize]; + + VerifyOrExit(mConnected); + + while (true) + { + if (mNeedBytes) + { + readLen = mbedtls_net_recv(&mCtx, buf, std::min(sizeof(buf), mNeedBytes)); + + if (readLen <= 0) + { + HandleMbedTlsError(readLen); + VerifyOrExit(readLen != 0); + VerifyOrExit(readLen != MBEDTLS_ERR_SSL_WANT_READ && readLen != MBEDTLS_ERR_SSL_WANT_WRITE); + otbrLogWarning("Failed to receive message: %d", readLen); + ExitNow(); + } + SuccessOrExit(otMessageAppend(mPendingMessage, buf, readLen)); + mNeedBytes -= readLen; + + if (!mNeedBytes) + { + otPlatDsoHandleReceive(mConnection, mPendingMessage); + mPendingMessage = nullptr; + } + } + else + { + assert(mLengthBuffer.size() < sizeof(uint16_t)); + assert(mPendingMessage == nullptr); + + readLen = mbedtls_net_recv(&mCtx, buf, sizeof(uint16_t) - mLengthBuffer.size()); + + if (readLen <= 0) + { + HandleMbedTlsError(readLen); + VerifyOrExit(readLen != 0); + VerifyOrExit(readLen != MBEDTLS_ERR_SSL_WANT_READ && readLen != MBEDTLS_ERR_SSL_WANT_WRITE); + otbrLogWarning("Failed to receive message: %d", readLen); + ExitNow(); + } + for (int i = 0; i < readLen; ++i) + { + mLengthBuffer.push_back(buf[i]); + } + if (mLengthBuffer.size() == sizeof(uint16_t)) + { + mNeedBytes = mLengthBuffer[0] << 8 | mLengthBuffer[1]; + mPendingMessage = otIp6NewMessage(otPlatDsoGetInstance(mConnection), nullptr); + mLengthBuffer.clear(); + } + } + } + +exit: + return; +} + +void DsoAgent::DsoConnection::HandleMbedTlsError(int aError) +{ + VerifyOrExit(aError < 0); + + switch (aError) + { + case MBEDTLS_ERR_SSL_PEER_CLOSE_NOTIFY: + otPlatDsoHandleDisconnected(mConnection, OT_PLAT_DSO_DISCONNECT_MODE_GRACEFULLY_CLOSE); + mConnected = false; + break; + case MBEDTLS_ERR_NET_CONN_RESET: + otPlatDsoHandleDisconnected(mConnection, OT_PLAT_DSO_DISCONNECT_MODE_FORCIBLY_ABORT); + mConnected = false; + break; + case MBEDTLS_ERR_SSL_WANT_READ: + case MBEDTLS_ERR_SSL_WANT_WRITE: + break; + default: + otPlatDsoHandleDisconnected(mConnection, OT_PLAT_DSO_DISCONNECT_MODE_FORCIBLY_ABORT); + mConnected = false; + break; + } + +exit: + return; +} + +void DsoAgent::HandleIncomingConnection(otInstance *aInstance, + mbedtls_net_context aCtx, + uint8_t *aAddress, + size_t aAddressLength) +{ + otSockAddr sockAddr; + otPlatDsoConnection *conn; + in6_addr *addrIn6 = nullptr; + bool successful = false; + + VerifyOrExit(!mbedtls_net_set_nonblock(&aCtx), otbrLogWarning("Failed to set the socket as non-blocking")); + + // TODO: support IPv4 + if (aAddressLength == OT_IP6_ADDRESS_SIZE) + { + Ip6Address address; + + addrIn6 = reinterpret_cast(aAddress); + memcpy(&sockAddr.mAddress.mFields.m8, addrIn6, aAddressLength); + sockAddr.mPort = 0; // Mbed TLS doesn't provide the client's port number. + + address.CopyFrom(*addrIn6); + otbrLogInfo("Receiving connection from %s", address.ToString().c_str()); + } + else + { + otbrLogInfo("Unsupported address length: %zu", aAddressLength); + ExitNow(); + } + + conn = otPlatDsoAccept(aInstance, &sockAddr); + + VerifyOrExit(conn != nullptr, otbrLogInfo("Failed to accept connection")); + + FindOrCreate(conn, aCtx); + otPlatDsoHandleConnected(conn); + successful = true; + +exit: + if (!successful) + { + mbedtls_net_close(&aCtx); + } +} + +} // namespace Dso +} // namespace otbr + +#endif diff --git a/src/dso/dso_transport.hpp b/src/dso/dso_transport.hpp new file mode 100644 index 00000000000..8812e665b6e --- /dev/null +++ b/src/dso/dso_transport.hpp @@ -0,0 +1,240 @@ +/* + * Copyright (c) 2023, 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 DSO agent. + */ + +#ifndef OTBR_AGENT_DSO_TRANSPORT_HPP_ +#define OTBR_AGENT_DSO_TRANSPORT_HPP_ + +#if OTBR_ENABLE_DNS_DSO + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "common/code_utils.hpp" +#include "mbedtls/ctr_drbg.h" +#include "mbedtls/debug.h" +#include "mbedtls/entropy.h" +#include "mbedtls/error.h" +#include "mbedtls/net_sockets.h" +#include "mbedtls/ssl.h" +#include "openthread/logging.h" +#include "openthread/message.h" +#include "openthread/platform/dso_transport.h" + +namespace otbr { +namespace Dso { + +// TODO: Support DNS-over-TLS +/** + * @addtogroup border-router-dso + * + * @brief + * This module includes definitions for DSO agent. + * + * @{ + */ + +class DsoAgent +{ +private: + class DsoConnection; + +public: + /** + * This constructor initializes the DsoAgent instance. + * + */ + explicit DsoAgent(void); + + /** + * This method enables listening for DsoAgent. + * + * @param[in] aInstance The OT instance. + * + */ + void Enable(otInstance *aInstance); + + /** + * This method disables listening for DsoAgent. + * + * @param[in] aInstance The OT instance. + * + */ + void Disable(otInstance *aInstance); + + /** + * This method enables/disables listening for DsoAgent. + * + * @param[in] aInstance The OT instance. + * @param[in] aEnabled A boolean indicating whether to enable listening. + * + */ + void SetEnabled(otInstance *aInstance, bool aEnabled); + + /** + * This method finds the DsoConnection corresponding to the given otPlatDsoConnection. + * + * @param[in] aConnection A pointer to the otPlatDsoConnection. + * + * @returns A pointer to the matching DsoConnection object. + * + */ + DsoConnection *Find(otPlatDsoConnection *aConnection); + + /** + * This method finds the DsoConnection corresponding to the given otPlatDsoConnection. If the DsoConnection doesn't + * exist, it creates a new one. + * + * @param[in] aConnection A pointer to the otPlatDsoConnection. + * + * @returns A pointer to the matching DsoConnection object. + * + */ + DsoConnection *FindOrCreate(otPlatDsoConnection *aConnection); + + /** + * This method finds the DsoConnection corresponding to the given otPlatDsoConnection. If the DsoConnection doesn't + * exist, it creates a new one. + * + * @param[in] aConnection A pointer to the otPlatDsoConnection. + * @param[in] aCtx A mbedtls_net_context object representing the platform connection. This is used for + * creating the DsoConnection. + * + * @returns A pointer to the matching DsoConnection object. + */ + DsoConnection *FindOrCreate(otPlatDsoConnection *aConnection, mbedtls_net_context aCtx); + + /** + * This method removes and destroys the DsoConnection corresponding to the given otPlatDsoConnection. + * + * @param aConnection A pointer to the otPlatDsoConnection. + * + */ + void Remove(otPlatDsoConnection *aConnection); + + /** + * This method processes rx/tx for all DSO connections. + * + */ + void ProcessConnections(void); + + /** + * This method processes the incoming DSO connections. + * + * @param[in] aInstance The OT instance. + * + */ + void HandleIncomingConnections(otInstance *aInstance); + +private: + class DsoConnection : NonCopyable + { + public: + explicit DsoConnection(otPlatDsoConnection *aConnection) + : mConnection(aConnection) + , mCtx() + , mConnected(false) + { + mbedtls_net_init(&mCtx); + } + + DsoConnection(otPlatDsoConnection *aConnection, mbedtls_net_context aCtx) + : mConnection(aConnection) + , mCtx(aCtx) + , mConnected(true) + { + } + + ~DsoConnection(void) + { + Disconnect(OT_PLAT_DSO_DISCONNECT_MODE_FORCIBLY_ABORT); + mbedtls_net_free(&mCtx); + } + + bool IsConnected(void) const { return mConnected; } + + otPlatDsoConnection *GetOtPlatDsoConnection(void) const { return mConnection; } + + otError Connect(const otSockAddr *aPeerSockAddr); + + void Disconnect(otPlatDsoDisconnectMode aMode); + + void Send(otMessage *aMessage); + + void HandleReceive(void); + + void HandleMbedTlsError(int aError); // Returns true if the connection is still alive, false otherwise. + + private: + static constexpr size_t kRxBufferSize = 512; + + otPlatDsoConnection *mConnection; + otSockAddr mPeerSockAddr{}; + otMessage *mPendingMessage = nullptr; + size_t mNeedBytes = 0; + std::vector mLengthBuffer; + mbedtls_net_context mCtx; + + bool mConnected = false; + }; + + static constexpr uint16_t kListeningPort = 853; + static constexpr int kMaxQueuedConnections = 10; + + void HandleIncomingConnection(otInstance *aInstance, + mbedtls_net_context aCtx, + uint8_t *aAddress, + size_t aAddressLength); + + bool mListeningEnabled = false; + mbedtls_net_context mListeningCtx; + + std::map> mMap; +}; + +/** + * @} + */ + +} // namespace Dso +} // namespace otbr + +#endif // OTBR_ENABLE_DNS_DSO + +#endif // OTBR_AGENT_DSO_TRANSPORT_HPP_ diff --git a/src/mdns/mdns.cpp b/src/mdns/mdns.cpp index 9646d5513c8..bd18073fab8 100644 --- a/src/mdns/mdns.cpp +++ b/src/mdns/mdns.cpp @@ -576,6 +576,18 @@ void Publisher::UpdateHostResolutionEmaLatency(const std::string &aHostName, otb } } +const Publisher::ServiceRegistration *Publisher::FindServiceRegistrationByType(const std::string &aType) const +{ + for (const auto &serviceReg : mServiceRegistrations) + { + if (serviceReg.second->mType == aType) + { + return serviceReg.second.get(); + } + } + return nullptr; +} + } // namespace Mdns } // namespace otbr diff --git a/src/mdns/mdns.hpp b/src/mdns/mdns.hpp index a06578a3870..b6134d007e4 100644 --- a/src/mdns/mdns.hpp +++ b/src/mdns/mdns.hpp @@ -563,6 +563,17 @@ class Publisher : private NonCopyable std::map mHostResolutionBeginTime; otbr::MdnsTelemetryInfo mTelemetryInfo{}; + +public: + /** + * Find the service registration of the given type. + * + * @param aType The service type. + * @returns A pointer to the service registration of the given type. If no service registration is found, + * returns a nullptr. + * + */ + const ServiceRegistration *FindServiceRegistrationByType(const std::string &aType) const; }; /** diff --git a/src/sdp_proxy/advertising_proxy.cpp b/src/sdp_proxy/advertising_proxy.cpp index d254ed0dbb6..b0db6da1d6c 100644 --- a/src/sdp_proxy/advertising_proxy.cpp +++ b/src/sdp_proxy/advertising_proxy.cpp @@ -147,6 +147,11 @@ void AdvertisingProxy::AdvertisingHandler(otSrpServerServiceUpdateId aId, error = PublishHostAndItsServices(aHost, update); + if (error == OTBR_ERROR_ABORTED) + { + error = OTBR_ERROR_NONE; + } + if (error != OTBR_ERROR_NONE || update->mCallbackCount == 0) { mOutstandingUpdates.pop_back(); @@ -165,6 +170,10 @@ void AdvertisingProxy::OnMdnsPublishResult(otSrpServerServiceUpdateId aUpdateId, if (aError != OTBR_ERROR_NONE || update->mCallbackCount == 1) { + if (aError == OTBR_ERROR_ABORTED) + { + aError = OTBR_ERROR_NONE; + } // Erase before notifying OpenThread, because there are chances that new // elements may be added to `otSrpServerHandleServiceUpdateResult` and // the iterator will be invalidated. diff --git a/src/srpl_dnssd/CMakeLists.txt b/src/srpl_dnssd/CMakeLists.txt new file mode 100644 index 00000000000..cc8b0a4a98d --- /dev/null +++ b/src/srpl_dnssd/CMakeLists.txt @@ -0,0 +1,37 @@ +# +# Copyright (c) 2023, 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-srpl-dnssd + srpl_dnssd.cpp + srpl_dnssd.hpp + ) + +target_link_libraries(otbr-srpl-dnssd PRIVATE + $<$:otbr-mdns> + otbr-common + ) diff --git a/src/srpl_dnssd/srpl_dnssd.cpp b/src/srpl_dnssd/srpl_dnssd.cpp new file mode 100644 index 00000000000..dd711fc6f31 --- /dev/null +++ b/src/srpl_dnssd/srpl_dnssd.cpp @@ -0,0 +1,190 @@ +/* + * Copyright (c) 2023, 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 SRPL DNS-SD over mDNS. + */ + +#if OTBR_ENABLE_SRP_REPLICATION + +#define OTBR_LOG_TAG "SrplDns" + +#include "srpl_dnssd/srpl_dnssd.hpp" + +#include "openthread/platform/srpl_dnssd.h" + +#include "mdns/mdns.hpp" +#include "openthread/ip6.h" +#include "utils/string_utils.hpp" + +static otbr::SrplDnssd::SrplDnssd *sSrplDnssd = nullptr; + +extern "C" void otPlatSrplDnssdBrowse(otInstance *aInstance, bool aEnable) +{ + OTBR_UNUSED_VARIABLE(aInstance); + + if (aEnable) + { + sSrplDnssd->StartBrowse(); + } + else + { + sSrplDnssd->StopBrowse(); + } +} + +extern "C" void otPlatSrplRegisterDnssdService(otInstance *aInstance, const uint8_t *aTxtData, uint16_t aTxtLength) +{ + OTBR_UNUSED_VARIABLE(aInstance); + + sSrplDnssd->RegisterService(aTxtData, aTxtLength); +} + +extern "C" void otPlatSrplUnregisterDnssdService(otInstance *aInstance) +{ + OTBR_UNUSED_VARIABLE(aInstance); + + sSrplDnssd->UnregisterService(); +} + +namespace otbr { + +namespace SrplDnssd { + +SrplDnssd::SrplDnssd(Ncp::ControllerOpenThread &aNcp, Mdns::Publisher &aPublisher) + : mNcp(aNcp) + , mPublisher(aPublisher) +{ + sSrplDnssd = this; +} + +void SrplDnssd::StartBrowse(void) +{ + VerifyOrExit(!IsBrowsing()); + + otbrLogDebug("Start browsing SRPL services ..."); + + mSubscriberId = mPublisher.AddSubscriptionCallbacks( + [this](const std::string &aType, const DiscoveredInstanceInfo &aInstanceInfo) { + OnServiceInstanceResolved(aType, aInstanceInfo); + }, + nullptr); + mPublisher.SubscribeService(kServiceType, /* aInstanceName */ ""); + +exit: + return; +} + +void SrplDnssd::StopBrowse(void) +{ + VerifyOrExit(IsBrowsing()); + + otbrLogDebug("Stop browsing SRPL services."); + + mPublisher.UnsubscribeService(kServiceType, /* aInstanceName */ ""); + mPublisher.RemoveSubscriptionCallbacks(mSubscriberId); + mSubscriberId = 0; + +exit: + return; +} + +// TODO: handle the case when mDNS publisher is not ready +void SrplDnssd::RegisterService(const uint8_t *aTxtData, uint8_t aTxtLength) +{ + otbr::Mdns::Publisher::TxtList txtList; + + SuccessOrExit(otbr::Mdns::Publisher::DecodeTxtData(txtList, aTxtData, aTxtLength)); + + otbrLogInfo("Publishing SRPL service"); + mPublisher.PublishService(/* aHostName */ "", /* aName */ "", kServiceType, {}, kPort, txtList, + [this](otbrError aError) { + otbrLogResult(aError, "Result of publishing SRPL service"); + if (aError == OTBR_ERROR_NONE) + { + auto serviceRegistration = mPublisher.FindServiceRegistrationByType(kServiceType); + if (serviceRegistration) + { + mServiceInstanceName = serviceRegistration->mName; + otbrLogInfo("SRPL service instance name is %s", mServiceInstanceName.c_str()); + } + } + }); + +exit: + return; +} + +void SrplDnssd::UnregisterService() +{ + otbrLogInfo("Unpublishing SRPL service: %s", mServiceInstanceName.c_str()); + mPublisher.UnpublishService(mServiceInstanceName, kServiceType, [this](otbrError aError) { + if (aError == OTBR_ERROR_NONE) + { + mServiceInstanceName.clear(); + } + }); +} + +void SrplDnssd::OnServiceInstanceResolved(const std::string &aType, const DiscoveredInstanceInfo &aInstanceInfo) +{ + otPlatSrplPartnerInfo partnerInfo; + + VerifyOrExit(IsBrowsing()); + VerifyOrExit(StringUtils::EqualCaseInsensitive(aType, kServiceType)); + VerifyOrExit(!StringUtils::EqualCaseInsensitive(aInstanceInfo.mName, mServiceInstanceName)); + + // TODO: Also need to check by addresses to mark as 'me'. + + partnerInfo.mRemoved = aInstanceInfo.mRemoved; + otbrLogInfo("Discovered SRPL peer: %s", aInstanceInfo.mName.c_str()); + + if (!partnerInfo.mRemoved) + { + VerifyOrExit(!aInstanceInfo.mAddresses.empty()); + // TODO: choose the address with the largest scope + // Currently the mDNS publisher only returns 1 address in every callback, probably we should let BR wait for + // some time to collect all discovered addresses and decide which address to use. + SuccessOrExit(otIp6AddressFromString(aInstanceInfo.mAddresses.front().ToString().c_str(), + &partnerInfo.mSockAddr.mAddress)); + + partnerInfo.mTxtData = aInstanceInfo.mTxtData.data(); + partnerInfo.mTxtLength = aInstanceInfo.mTxtData.size(); + partnerInfo.mSockAddr.mPort = aInstanceInfo.mPort; + } + otPlatSrplHandleDnssdBrowseResult(mNcp.GetInstance(), &partnerInfo); + +exit: + return; +} + +} // namespace SrplDnssd +} // namespace otbr + +#endif diff --git a/src/srpl_dnssd/srpl_dnssd.hpp b/src/srpl_dnssd/srpl_dnssd.hpp new file mode 100644 index 00000000000..c810397b1ca --- /dev/null +++ b/src/srpl_dnssd/srpl_dnssd.hpp @@ -0,0 +1,122 @@ +/* + * Copyright (c) 2023, 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 SRPL DNS-SD over mDNS. + */ + +#ifndef OTBR_AGENT_SRPL_DNSSD_HPP_ +#define OTBR_AGENT_SRPL_DNSSD_HPP_ + +#if OTBR_ENABLE_SRP_REPLICATION + +#include + +#include "common/types.hpp" +#include "mdns/mdns.hpp" +#include "ncp/ncp_openthread.hpp" + +namespace otbr { + +namespace SrplDnssd { + +/** + * + * @addtogroup border-router-srpl-dnssd + * + * @brief + * This module includes definition for SRPL DNS-SD over mDNS. + * + * @{ + */ + +class SrplDnssd +{ +public: + /** + * This constructor initializes the SrplDnssd instance. + * + * @param aNcp + * @param aPublisher + */ + explicit SrplDnssd(Ncp::ControllerOpenThread &aNcp, Mdns::Publisher &aPublisher); + + /** + * This method starts browsing for SRPL peers. + * + */ + void StartBrowse(void); + + /** + * This method stops browsing for SRPL peers. + * + */ + void StopBrowse(void); + + /** + * This method registers the SRPL service to DNS-SD. + * + * @param[in] aTxtData The TXT data of SRPL service. + * @param[in] aTxtLength The TXT length of SRPL service. + */ + void RegisterService(const uint8_t *aTxtData, uint8_t aTxtLength); + + /** + * This method removes the SRPL service from DNS-SD. + * + */ + void UnregisterService(void); + +private: + static constexpr const char *kServiceType = "_srpl-tls._tcp"; + static constexpr uint16_t kPort = 853; + + using DiscoveredInstanceInfo = otbr::Mdns::Publisher::DiscoveredInstanceInfo; + + bool IsBrowsing(void) const { return mSubscriberId != 0; } + + void OnServiceInstanceResolved(const std::string &aType, const DiscoveredInstanceInfo &aInstanceInfo); + + Ncp::ControllerOpenThread &mNcp; + Mdns::Publisher &mPublisher; + std::string mServiceInstanceName; + uint64_t mSubscriberId = 0; +}; + +/** + * @} + */ + +} // namespace SrplDnssd + +} // namespace otbr + +#endif // OTBR_ENABLE_SRP_REPLICATION + +#endif // OTBR_AGENT_SRPL_DNSSD_HPP_ diff --git a/third_party/openthread/CMakeLists.txt b/third_party/openthread/CMakeLists.txt index 6f474e7ef7e..0bcc52c4e74 100644 --- a/third_party/openthread/CMakeLists.txt +++ b/third_party/openthread/CMakeLists.txt @@ -89,6 +89,13 @@ endif() set(OT_NAT64_TRANSLATOR ${OTBR_NAT64} CACHE BOOL "enable NAT64 translator" FORCE) set(OT_NAT64_BORDER_ROUTING ${OTBR_NAT64} CACHE BOOL "enable NAT64 in border routing manager" FORCE) +set(OT_DNS_DSO ${OTBR_DNS_DSO} CACHE BOOL "enable DSO support" FORCE) + +if (OTBR_SRP_REPLICATION) + set(OT_SRP_REPLICATION ON CACHE BOOL "enable SRP replication" FORCE) + set(OT_DNS_DSO ON CACHE BOOL "enable DSO support" FORCE) +endif() + if (NOT OT_POSIX_SETTINGS_PATH) set(OT_POSIX_SETTINGS_PATH "\"/var/lib/thread\"" CACHE STRING "set the directory to store Thread data" FORCE) endif()