From ff4ea4e00123b38db74da8a162b02a11beca7d23 Mon Sep 17 00:00:00 2001 From: Li Cao <irvingcl@google.com> Date: Thu, 5 Dec 2024 14:59:03 +0800 Subject: [PATCH] [ncp] integrate SRP Server and Advertising Proxy for NCP (#2624) This commit integrates the mDNS Publisher with NcpHost to make advertising proxy work under NCP mode. The commit adds an expect test to verify the advertising proxy function under NCP mode. To do the test, `avahi-browser` is used. So it's added into the bootstrap of the CI for ncp_mode. As an abstraction: 1. The commit adds `NcpSpinel::SrpServerSetEnabled` and `NcpSpinel::SrpServerSetAutoEnableMode` to control the SRP server on the NCP. 2. The commit adds the handling of `SPINEL_PROP_DNSSD_HOST`, `SPINEL_PROP_DNSSD_SERVICE` and `SPINEL_PROP_DNSSD_KEY_RECORD` to help NCP to publish the dnssd entries (by using the mDNSPublisher on the host). --- .github/workflows/ncp_mode.yml | 4 +- src/agent/application.cpp | 10 +- src/agent/application.hpp | 1 + src/ncp/ncp_host.cpp | 23 +- src/ncp/ncp_host.hpp | 16 +- src/ncp/ncp_spinel.cpp | 265 +++++++++++++++++- src/ncp/ncp_spinel.hpp | 47 +++- tests/scripts/bootstrap.sh | 4 + .../scripts/expect/ncp_schedule_migration.exp | 0 tests/scripts/expect/ncp_srp_server.exp | 77 +++++ tests/scripts/ncp_mode | 2 + 11 files changed, 441 insertions(+), 8 deletions(-) mode change 100644 => 100755 tests/scripts/expect/ncp_schedule_migration.exp create mode 100755 tests/scripts/expect/ncp_srp_server.exp diff --git a/.github/workflows/ncp_mode.yml b/.github/workflows/ncp_mode.yml index 1584aaab63d..1c6926d40d8 100644 --- a/.github/workflows/ncp_mode.yml +++ b/.github/workflows/ncp_mode.yml @@ -53,7 +53,7 @@ jobs: OTBR_MDNS: ${{ matrix.mdns }} OTBR_COVERAGE: 1 OTBR_VERBOSE: 1 - OTBR_OPTIONS: "-DCMAKE_BUILD_TYPE=Debug -DOT_THREAD_VERSION=1.4 -DOTBR_COVERAGE=ON -DOTBR_DBUS=ON -DOTBR_FEATURE_FLAGS=ON -DOTBR_TELEMETRY_DATA_API=ON -DOTBR_UNSECURE_JOIN=ON -DOTBR_TREL=ON -DBUILD_TESTING=OFF" + OTBR_OPTIONS: "-DCMAKE_BUILD_TYPE=Debug -DOT_THREAD_VERSION=1.4 -DOTBR_COVERAGE=ON -DOTBR_DBUS=ON -DOTBR_FEATURE_FLAGS=ON -DOTBR_TELEMETRY_DATA_API=ON -DOTBR_UNSECURE_JOIN=ON -DOTBR_TREL=ON -DOTBR_SRP_ADVERTISING_PROXY=ON -DBUILD_TESTING=OFF" steps: - uses: actions/checkout@v4 with: @@ -76,6 +76,6 @@ jobs: --build-arg OTBR_OPTIONS="${OTBR_OPTIONS}" - name: Run run: | - top_builddir="./build/temp" tests/scripts/ncp_mode build_ot_sim expect + top_builddir="./build/temp" tests/scripts/ncp_mode build_ot_sim expect - name: Codecov uses: codecov/codecov-action@v5 diff --git a/src/agent/application.cpp b/src/agent/application.cpp index fb6cee2cb46..690fa37cbdb 100644 --- a/src/agent/application.cpp +++ b/src/agent/application.cpp @@ -288,6 +288,12 @@ void Application::DeinitRcpMode(void) void Application::InitNcpMode(void) { +#if OTBR_ENABLE_SRP_ADVERTISING_PROXY + otbr::Ncp::NcpHost &ncpHost = static_cast<otbr::Ncp::NcpHost &>(mHost); + ncpHost.SetMdnsPublisher(mPublisher.get()); + mMdnsStateSubject.AddObserver(ncpHost); + mPublisher->Start(); +#endif #if OTBR_ENABLE_DBUS_SERVER mDBusAgent->Init(*mBorderAgent); #endif @@ -295,7 +301,9 @@ void Application::InitNcpMode(void) void Application::DeinitNcpMode(void) { - /* empty */ +#if OTBR_ENABLE_SRP_ADVERTISING_PROXY + mPublisher->Stop(); +#endif } } // namespace otbr diff --git a/src/agent/application.hpp b/src/agent/application.hpp index 9ae141ba5d3..1c41efb5067 100644 --- a/src/agent/application.hpp +++ b/src/agent/application.hpp @@ -44,6 +44,7 @@ #if OTBR_ENABLE_BORDER_AGENT #include "border_agent/border_agent.hpp" #endif +#include "ncp/ncp_host.hpp" #include "ncp/rcp_host.hpp" #if OTBR_ENABLE_BACKBONE_ROUTER #include "backbone_router/backbone_agent.hpp" diff --git a/src/ncp/ncp_host.cpp b/src/ncp/ncp_host.cpp index 347de367553..568cc54585f 100644 --- a/src/ncp/ncp_host.cpp +++ b/src/ncp/ncp_host.cpp @@ -38,7 +38,6 @@ #include <openthread/openthread-system.h> #include "lib/spinel/spinel_driver.hpp" - #include "ncp/async_task.hpp" namespace otbr { @@ -134,6 +133,16 @@ void NcpHost::Init(void) { mInfraIf.SetInfraIf(mConfig.mBackboneInterfaceName); } + +#if OTBR_ENABLE_SRP_ADVERTISING_PROXY +#if OTBR_ENABLE_SRP_SERVER_AUTO_ENABLE_MODE + // Let SRP server use auto-enable mode. The auto-enable mode delegates the control of SRP server to the Border + // Routing Manager. SRP server automatically starts when bi-directional connectivity is ready. + mNcpSpinel.SrpServerSetAutoEnableMode(/* aEnabled */ true); +#else + mNcpSpinel.SrpServerSetEnabled(/* aEnabled */ true); +#endif +#endif } void NcpHost::Deinit(void) @@ -264,5 +273,17 @@ void NcpHost::Update(MainloopContext &aMainloop) mNetif.UpdateFdSet(&aMainloop); } +#if OTBR_ENABLE_SRP_ADVERTISING_PROXY +void NcpHost::SetMdnsPublisher(Mdns::Publisher *aPublisher) +{ + mNcpSpinel.SetMdnsPublisher(aPublisher); +} + +void NcpHost::HandleMdnsState(Mdns::Publisher::State aState) +{ + mNcpSpinel.DnssdSetState(aState); +} +#endif + } // namespace Ncp } // namespace otbr diff --git a/src/ncp/ncp_host.hpp b/src/ncp/ncp_host.hpp index 3e68a524b59..8fec83aa2a0 100644 --- a/src/ncp/ncp_host.hpp +++ b/src/ncp/ncp_host.hpp @@ -72,7 +72,13 @@ class NcpNetworkProperties : virtual public NetworkProperties, public PropsObser otOperationalDatasetTlvs mDatasetActiveTlvs; }; -class NcpHost : public MainloopProcessor, public ThreadHost, public NcpNetworkProperties +class NcpHost : public MainloopProcessor, + public ThreadHost, + public NcpNetworkProperties +#if OTBR_ENABLE_SRP_ADVERTISING_PROXY + , + public Mdns::StateObserver +#endif { public: /** @@ -119,7 +125,15 @@ class NcpHost : public MainloopProcessor, public ThreadHost, public NcpNetworkPr void Update(MainloopContext &aMainloop) override; void Process(const MainloopContext &aMainloop) override; +#if OTBR_ENABLE_SRP_ADVERTISING_PROXY + void SetMdnsPublisher(Mdns::Publisher *aPublisher); +#endif + private: +#if OTBR_ENABLE_SRP_ADVERTISING_PROXY + void HandleMdnsState(Mdns::Publisher::State aState) override; +#endif + ot::Spinel::SpinelDriver &mSpinelDriver; otPlatformConfig mConfig; NcpSpinel mNcpSpinel; diff --git a/src/ncp/ncp_spinel.cpp b/src/ncp/ncp_spinel.cpp index ed40d54d823..6b29350e861 100644 --- a/src/ncp/ncp_spinel.cpp +++ b/src/ncp/ncp_spinel.cpp @@ -36,6 +36,7 @@ #include <openthread/dataset.h> #include <openthread/thread.h> +#include <openthread/platform/dnssd.h> #include "common/code_utils.hpp" #include "common/logging.hpp" @@ -44,6 +45,7 @@ #include "lib/spinel/spinel_driver.hpp" #include "lib/spinel/spinel_encoder.hpp" #include "lib/spinel/spinel_helper.hpp" +#include "lib/spinel/spinel_prop_codec.hpp" namespace otbr { namespace Ncp { @@ -58,6 +60,9 @@ NcpSpinel::NcpSpinel(void) , mEncoder(mNcpBuffer) , mIid(SPINEL_HEADER_INVALID_IID) , mPropsObserver(nullptr) +#if OTBR_ENABLE_SRP_ADVERTISING_PROXY + , mPublisher(nullptr) +#endif { std::fill_n(mWaitingKeyTable, SPINEL_PROP_LAST_STATUS, sizeof(mWaitingKeyTable)); memset(mCmdTable, 0, sizeof(mCmdTable)); @@ -76,6 +81,9 @@ void NcpSpinel::Deinit(void) mSpinelDriver = nullptr; mIp6AddressTableCallback = nullptr; mNetifStateChangedCallback = nullptr; +#if OTBR_ENABLE_SRP_ADVERTISING_PROXY + mPublisher = nullptr; +#endif } otbrError NcpSpinel::SpinelDataUnpack(const uint8_t *aDataIn, spinel_size_t aDataLen, const char *aPackFormat, ...) @@ -224,6 +232,45 @@ void NcpSpinel::ThreadErasePersistentInfo(AsyncTaskPtr aAsyncTask) } } +#if OTBR_ENABLE_SRP_ADVERTISING_PROXY +void NcpSpinel::SrpServerSetAutoEnableMode(bool aEnabled) +{ + otError error; + EncodingFunc encodingFunc = [aEnabled](ot::Spinel::Encoder &aEncoder) { return aEncoder.WriteBool(aEnabled); }; + + error = SetProperty(SPINEL_PROP_SRP_SERVER_AUTO_ENABLE_MODE, encodingFunc); + if (error != OT_ERROR_NONE) + { + otbrLogWarning("Failed to call SrpServerSetAutoEnableMode, %s", otThreadErrorToString(error)); + } +} + +void NcpSpinel::SrpServerSetEnabled(bool aEnabled) +{ + otError error; + EncodingFunc encodingFunc = [aEnabled](ot::Spinel::Encoder &aEncoder) { return aEncoder.WriteBool(aEnabled); }; + + error = SetProperty(SPINEL_PROP_SRP_SERVER_ENABLED, encodingFunc); + if (error != OT_ERROR_NONE) + { + otbrLogWarning("Failed to call SrpServerSetEnabled, %s", otThreadErrorToString(error)); + } +} + +void NcpSpinel::DnssdSetState(Mdns::Publisher::State aState) +{ + otError error; + otPlatDnssdState state = (aState == Mdns::Publisher::State::kReady) ? OT_PLAT_DNSSD_READY : OT_PLAT_DNSSD_STOPPED; + EncodingFunc encodingFunc = [state](ot::Spinel::Encoder &aEncoder) { return aEncoder.WriteUint8(state); }; + + error = SetProperty(SPINEL_PROP_DNSSD_STATE, encodingFunc); + if (error != OT_ERROR_NONE) + { + otbrLogWarning("Failed to call DnssdSetState, %s", otThreadErrorToString(error)); + } +} +#endif // OTBR_ENABLE_SRP_ADVERTISING_PROXY + void NcpSpinel::HandleReceivedFrame(const uint8_t *aFrame, uint16_t aLength, uint8_t aHeader, @@ -272,8 +319,19 @@ void NcpSpinel::HandleNotification(const uint8_t *aFrame, uint16_t aLength) SuccessOrExit(error = SpinelDataUnpack(aFrame, aLength, kSpinelDataUnpackFormat, &header, &cmd, &key, &data, &len)); VerifyOrExit(SPINEL_HEADER_GET_TID(header) == 0, error = OTBR_ERROR_PARSE); - VerifyOrExit(cmd == SPINEL_CMD_PROP_VALUE_IS); - HandleValueIs(key, data, static_cast<uint16_t>(len)); + + switch (cmd) + { + case SPINEL_CMD_PROP_VALUE_IS: + HandleValueIs(key, data, static_cast<uint16_t>(len)); + break; + case SPINEL_CMD_PROP_VALUE_INSERTED: + HandleValueInserted(key, data, static_cast<uint16_t>(len)); + break; + case SPINEL_CMD_PROP_VALUE_REMOVED: + HandleValueRemoved(key, data, static_cast<uint16_t>(len)); + break; + } exit: otbrLogResult(error, "%s", __FUNCTION__); @@ -439,6 +497,184 @@ void NcpSpinel::HandleValueIs(spinel_prop_key_t aKey, const uint8_t *aBuffer, ui return; } +#if OTBR_ENABLE_SRP_ADVERTISING_PROXY +static std::string KeyNameFor(const otPlatDnssdKey &aKey) +{ + std::string name(aKey.mName); + + if (aKey.mServiceType != nullptr) + { + // TODO: current code would not work with service instance labels that include a '.' + name += "."; + name += aKey.mServiceType; + } + return name; +} +#endif + +void NcpSpinel::HandleValueInserted(spinel_prop_key_t aKey, const uint8_t *aBuffer, uint16_t aLength) +{ + otbrError error = OTBR_ERROR_NONE; + ot::Spinel::Decoder decoder; + + VerifyOrExit(aBuffer != nullptr, error = OTBR_ERROR_INVALID_ARGS); + decoder.Init(aBuffer, aLength); + + switch (aKey) + { +#if OTBR_ENABLE_SRP_ADVERTISING_PROXY + case SPINEL_PROP_DNSSD_HOST: + { + Mdns::Publisher::AddressList addressList; + otPlatDnssdHost host; + otPlatDnssdRequestId requestId; + const uint8_t *callbackData; + uint16_t callbackDataSize; + std::vector<uint8_t> callbackDataCopy; + + SuccessOrExit(ot::Spinel::DecodeDnssdHost(decoder, host, requestId, callbackData, callbackDataSize)); + for (uint16_t i = 0; i < host.mAddressesLength; i++) + { + addressList.push_back(Ip6Address(host.mAddresses[i].mFields.m8)); + } + callbackDataCopy.assign(callbackData, callbackData + callbackDataSize); + + mPublisher->PublishHost(host.mHostName, addressList, [this, requestId, callbackDataCopy](otbrError aError) { + OT_UNUSED_VARIABLE(SendDnssdResult(requestId, callbackDataCopy, OtbrErrorToOtError(aError))); + }); + break; + } + case SPINEL_PROP_DNSSD_SERVICE: + { + otPlatDnssdService service; + Mdns::Publisher::SubTypeList subTypeList; + const char *subTypeArray[kMaxSubTypes]; + uint16_t subTypeCount; + Mdns::Publisher::TxtData txtData; + otPlatDnssdRequestId requestId; + const uint8_t *callbackData; + uint16_t callbackDataSize; + std::vector<uint8_t> callbackDataCopy; + + SuccessOrExit(ot::Spinel::DecodeDnssdService(decoder, service, subTypeArray, subTypeCount, requestId, + callbackData, callbackDataSize)); + for (uint16_t i = 0; i < subTypeCount; i++) + { + subTypeList.push_back(subTypeArray[i]); + } + txtData.assign(service.mTxtData, service.mTxtData + service.mTxtDataLength); + callbackDataCopy.assign(callbackData, callbackData + callbackDataSize); + + mPublisher->PublishService(service.mHostName, service.mServiceInstance, service.mServiceType, subTypeList, + service.mPort, txtData, [this, requestId, callbackDataCopy](otbrError aError) { + OT_UNUSED_VARIABLE( + SendDnssdResult(requestId, callbackDataCopy, OtbrErrorToOtError(aError))); + }); + break; + } + case SPINEL_PROP_DNSSD_KEY_RECORD: + { + otPlatDnssdKey key; + Mdns::Publisher::KeyData keyData; + otPlatDnssdRequestId requestId; + const uint8_t *callbackData; + uint16_t callbackDataSize; + std::vector<uint8_t> callbackDataCopy; + + SuccessOrExit(ot::Spinel::DecodeDnssdKey(decoder, key, requestId, callbackData, callbackDataSize)); + keyData.assign(key.mKeyData, key.mKeyData + key.mKeyDataLength); + callbackDataCopy.assign(callbackData, callbackData + callbackDataSize); + + mPublisher->PublishKey(KeyNameFor(key), keyData, [this, requestId, callbackDataCopy](otbrError aError) { + OT_UNUSED_VARIABLE(SendDnssdResult(requestId, callbackDataCopy, OtbrErrorToOtError(aError))); + }); + break; + } +#endif // OTBR_ENABLE_SRP_ADVERTISING_PROXY + default: + error = OTBR_ERROR_DROPPED; + break; + } + +exit: + otbrLogResult(error, "HandleValueInserted, key:%u", aKey); + return; +} + +void NcpSpinel::HandleValueRemoved(spinel_prop_key_t aKey, const uint8_t *aBuffer, uint16_t aLength) +{ + otbrError error = OTBR_ERROR_NONE; + ot::Spinel::Decoder decoder; + + VerifyOrExit(aBuffer != nullptr, error = OTBR_ERROR_INVALID_ARGS); + decoder.Init(aBuffer, aLength); + + switch (aKey) + { +#if OTBR_ENABLE_SRP_ADVERTISING_PROXY + case SPINEL_PROP_DNSSD_HOST: + { + otPlatDnssdHost host; + otPlatDnssdRequestId requestId; + const uint8_t *callbackData; + uint16_t callbackDataSize; + std::vector<uint8_t> callbackDataCopy; + + SuccessOrExit(ot::Spinel::DecodeDnssdHost(decoder, host, requestId, callbackData, callbackDataSize)); + callbackDataCopy.assign(callbackData, callbackData + callbackDataSize); + + mPublisher->UnpublishHost(host.mHostName, [this, requestId, callbackDataCopy](otbrError aError) { + OT_UNUSED_VARIABLE(SendDnssdResult(requestId, callbackDataCopy, OtbrErrorToOtError(aError))); + }); + break; + } + case SPINEL_PROP_DNSSD_SERVICE: + { + otPlatDnssdService service; + const char *subTypeArray[kMaxSubTypes]; + uint16_t subTypeCount; + otPlatDnssdRequestId requestId; + const uint8_t *callbackData; + uint16_t callbackDataSize; + std::vector<uint8_t> callbackDataCopy; + + SuccessOrExit(ot::Spinel::DecodeDnssdService(decoder, service, subTypeArray, subTypeCount, requestId, + callbackData, callbackDataSize)); + callbackDataCopy.assign(callbackData, callbackData + callbackDataSize); + + mPublisher->UnpublishService( + service.mHostName, service.mServiceType, [this, requestId, callbackDataCopy](otbrError aError) { + OT_UNUSED_VARIABLE(SendDnssdResult(requestId, callbackDataCopy, OtbrErrorToOtError(aError))); + }); + break; + } + case SPINEL_PROP_DNSSD_KEY_RECORD: + { + otPlatDnssdKey key; + otPlatDnssdRequestId requestId; + const uint8_t *callbackData; + uint16_t callbackDataSize; + std::vector<uint8_t> callbackDataCopy; + + SuccessOrExit(ot::Spinel::DecodeDnssdKey(decoder, key, requestId, callbackData, callbackDataSize)); + callbackDataCopy.assign(callbackData, callbackData + callbackDataSize); + + mPublisher->UnpublishKey(KeyNameFor(key), [this, requestId, callbackDataCopy](otbrError aError) { + OT_UNUSED_VARIABLE(SendDnssdResult(requestId, callbackDataCopy, OtbrErrorToOtError(aError))); + }); + break; + } +#endif // OTBR_ENABLE_SRP_ADVERTISING_PROXY + default: + error = OTBR_ERROR_DROPPED; + break; + } + +exit: + otbrLogResult(error, "HandleValueRemoved, key:%u", aKey); + return; +} + otbrError NcpSpinel::HandleResponseForPropSet(spinel_tid_t aTid, spinel_prop_key_t aKey, const uint8_t *aData, @@ -801,6 +1037,31 @@ otError NcpSpinel::ParseInfraIfIcmp6Nd(const uint8_t *aBuf, return error; } +otError NcpSpinel::SendDnssdResult(otPlatDnssdRequestId aRequestId, + const std::vector<uint8_t> &aCallbackData, + otError aError) +{ + otError error; + EncodingFunc encodingFunc = [aRequestId, &aCallbackData, aError](ot::Spinel::Encoder &aEncoder) { + otError error = OT_ERROR_NONE; + + SuccessOrExit(aEncoder.WriteUint8(aError)); + SuccessOrExit(aEncoder.WriteUint32(aRequestId)); + SuccessOrExit(aEncoder.WriteData(aCallbackData.data(), aCallbackData.size())); + + exit: + return error; + }; + + error = SetProperty(SPINEL_PROP_DNSSD_REQUEST_RESULT, encodingFunc); + if (error != OT_ERROR_NONE) + { + otbrLogWarning("Failed to SendDnssdResult, %s", otThreadErrorToString(error)); + } + + return error; +} + otbrError NcpSpinel::SetInfraIf(uint32_t aInfraIfIndex, bool aIsRunning, const std::vector<Ip6Address> &aIp6Addresses) { otbrError error = OTBR_ERROR_NONE; diff --git a/src/ncp/ncp_spinel.hpp b/src/ncp/ncp_spinel.hpp index bb79274413b..0b4ea9bbe7d 100644 --- a/src/ncp/ncp_spinel.hpp +++ b/src/ncp/ncp_spinel.hpp @@ -37,10 +37,13 @@ #include <functional> #include <memory> +#include <vector> + #include <openthread/dataset.h> #include <openthread/error.h> #include <openthread/link.h> #include <openthread/thread.h> +#include <openthread/platform/dnssd.h> #include "lib/spinel/spinel.h" #include "lib/spinel/spinel_buffer.hpp" @@ -49,6 +52,7 @@ #include "common/task_runner.hpp" #include "common/types.hpp" +#include "mdns/mdns.hpp" #include "ncp/async_task.hpp" #include "ncp/posix/infra_if.hpp" #include "ncp/posix/netif.hpp" @@ -245,10 +249,45 @@ class NcpSpinel : public Netif::Dependencies, public InfraIf::Dependencies mInfraIfIcmp6NdCallback = aCallback; } +#if OTBR_ENABLE_SRP_ADVERTISING_PROXY + /** + * This method enables/disables the SRP Server on NCP. + * + * @param[in] aEnable A boolean to enable/disable the SRP server. + */ + void SrpServerSetEnabled(bool aEnabled); + + /** + * This method enables/disables the auto-enable mode on SRP Server on NCP. + * + * @param[in] aEnable A boolean to enable/disable the SRP server. + */ + void SrpServerSetAutoEnableMode(bool aEnabled); + + /** + * This method sets the dnssd state on NCP. + * + * @param[in] aState The dnssd state. + */ + void DnssdSetState(Mdns::Publisher::State aState); + + /** + * This method sets the mDNS Publisher object. + * + * @param[in] aPublisher A pointer to the mDNS Publisher object. + */ + void SetMdnsPublisher(otbr::Mdns::Publisher *aPublisher) + { + mPublisher = aPublisher; + } +#endif // OTBR_ENABLE_SRP_ADVERTISING_PROXY + private: using FailureHandler = std::function<void(otError)>; - static constexpr uint8_t kMaxTids = 16; + static constexpr uint8_t kMaxTids = 16; + static constexpr uint16_t kCallbackDataMaxSize = sizeof(uint64_t); // Maximum size of a function pointer. + static constexpr uint16_t kMaxSubTypes = 8; // Maximum number of sub types in a MDNS service. template <typename Function, typename... Args> static void SafeInvoke(Function &aFunc, Args &&...aArgs) { @@ -282,6 +321,8 @@ class NcpSpinel : public Netif::Dependencies, public InfraIf::Dependencies void HandleNotification(const uint8_t *aFrame, uint16_t aLength); void HandleResponse(spinel_tid_t aTid, const uint8_t *aFrame, uint16_t aLength); void HandleValueIs(spinel_prop_key_t aKey, const uint8_t *aBuffer, uint16_t aLength); + void HandleValueInserted(spinel_prop_key_t aKey, const uint8_t *aBuffer, uint16_t aLength); + void HandleValueRemoved(spinel_prop_key_t aKey, const uint8_t *aBuffer, uint16_t aLength); otbrError HandleResponseForPropSet(spinel_tid_t aTid, spinel_prop_key_t aKey, const uint8_t *aData, @@ -320,6 +361,7 @@ class NcpSpinel : public Netif::Dependencies, public InfraIf::Dependencies const otIp6Address *&aAddr, const uint8_t *&aData, uint16_t &aDataLen); + otError SendDnssdResult(otPlatDnssdRequestId aRequestId, const std::vector<uint8_t> &aCallbackData, otError aError); otbrError SetInfraIf(uint32_t aInfraIfIndex, bool aIsRunning, @@ -346,6 +388,9 @@ class NcpSpinel : public Netif::Dependencies, public InfraIf::Dependencies TaskRunner mTaskRunner; PropsObserver *mPropsObserver; +#if OTBR_ENABLE_SRP_ADVERTISING_PROXY + otbr::Mdns::Publisher *mPublisher; +#endif AsyncTaskPtr mDatasetSetActiveTask; AsyncTaskPtr mDatasetMgmtSetPendingTask; diff --git a/tests/scripts/bootstrap.sh b/tests/scripts/bootstrap.sh index 6d32cea0a77..99e4b969b20 100755 --- a/tests/scripts/bootstrap.sh +++ b/tests/scripts/bootstrap.sh @@ -118,6 +118,10 @@ case "$(uname)" in configure_network fi + if [ "$BUILD_TARGET" == ncp_mode ]; then + sudo apt-get install --no-install-recommends -y avahi-daemon avahi-utils + fi + if [ "$BUILD_TARGET" == scan-build ]; then pip3 install -U cmake sudo apt-get install --no-install-recommends -y clang clang-tools diff --git a/tests/scripts/expect/ncp_schedule_migration.exp b/tests/scripts/expect/ncp_schedule_migration.exp old mode 100644 new mode 100755 diff --git a/tests/scripts/expect/ncp_srp_server.exp b/tests/scripts/expect/ncp_srp_server.exp new file mode 100755 index 00000000000..b7d6ab24529 --- /dev/null +++ b/tests/scripts/expect/ncp_srp_server.exp @@ -0,0 +1,77 @@ +#!/usr/bin/expect -f +# +# Copyright (c) 2024, 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. +# +source "tests/scripts/expect/_common.exp" + +set ptys [create_socat 1] +set pty1 [lindex $ptys 0] +set pty2 [lindex $ptys 1] +set container "otbr-ncp" + +set dataset "0e080000000000010000000300001435060004001fffe002087d61eb42cdc48d6a0708fd0d07fca1b9f0500510ba088fc2bd6c3b3897f7a10f58263ff3030f4f70656e5468726561642d353234660102524f04109dc023ccd447b12b50997ef68020f19e0c0402a0f7f8" +set dataset_dbus "0x0e,0x08,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x03,0x00,0x00,0x14,0x35,0x06,0x00,0x04,0x00,0x1f,0xff,0xe0,0x02,0x08,0x7d,0x61,0xeb,0x42,0xcd,0xc4,0x8d,0x6a,0x07,0x08,0xfd,0x0d,0x07,0xfc,0xa1,0xb9,0xf0,0x50,0x05,0x10,0xba,0x08,0x8f,0xc2,0xbd,0x6c,0x3b,0x38,0x97,0xf7,0xa1,0x0f,0x58,0x26,0x3f,0xf3,0x03,0x0f,0x4f,0x70,0x65,0x6e,0x54,0x68,0x72,0x65,0x61,0x64,0x2d,0x35,0x32,0x34,0x66,0x01,0x02,0x52,0x4f,0x04,0x10,0x9d,0xc0,0x23,0xcc,0xd4,0x47,0xb1,0x2b,0x50,0x99,0x7e,0xf6,0x80,0x20,0xf1,0x9e,0x0c,0x04,0x02,0xa0,0xf7,0xf8" + +start_otbr_docker $container $::env(EXP_OT_NCP_PATH) 2 $pty1 $pty2 +spawn_node 3 otbr-docker $container +sleep 5 + +send "dbus-send --system --dest=io.openthread.BorderRouter.wpan0 --type=method_call --print-reply /io/openthread/BorderRouter/wpan0 io.openthread.BorderRouter.Join \"array:byte:${dataset_dbus}\"\n" +expect "app#" +sleep 20 + +spawn_node 4 cli $::env(EXP_OT_CLI_PATH) +send "dataset set active ${dataset}\n" +expect_line "Done" +send "mode rn\r\n" +expect_line "Done" +send "ifconfig up\r\n" +expect_line "Done" +send "thread start\r\n" +expect_line "Done" +wait_for "state" "child" +set omr_addr [get_omr_addr] +sleep 1 + +send "srp client autostart enable\r\n" +expect_line "Done" +send "srp client host name otbr-ncp-test\r\n" +expect_line "Done" +send "srp client host address $omr_addr\r\n" +expect_line "Done" +send "srp client service add ot-service _ipps._tcp 12345\r\n" +expect_line "Done" +sleep 1 + +spawn avahi-browse -r _ipps._tcp +expect backbone1 +send "\003" +expect eof + +exec sudo docker stop $container +exec sudo docker rm $container +dispose_all diff --git a/tests/scripts/ncp_mode b/tests/scripts/ncp_mode index b555f3fdec9..2544bf5b397 100755 --- a/tests/scripts/ncp_mode +++ b/tests/scripts/ncp_mode @@ -136,6 +136,7 @@ do_build_ot_simulation() OT_CMAKE_BUILD_DIR=${ABS_TOP_OT_BUILDDIR}/ncp "${ABS_TOP_OT_SRCDIR}"/script/cmake-build simulation \ -DOT_MTD=OFF -DOT_RCP=OFF -DOT_APP_CLI=OFF -DOT_APP_RCP=OFF \ -DOT_BORDER_ROUTING=ON -DOT_NCP_INFRA_IF=ON -DOT_SIMULATION_INFRA_IF=OFF \ + -DOT_SRP_SERVER=ON -DOT_SRP_ADV_PROXY=ON -DOT_PLATFORM_DNSSD=ON -DOT_SIMULATION_DNSSD=OFF -DOT_NCP_DNSSD=ON \ -DBUILD_TESTING=OFF OT_CMAKE_BUILD_DIR=${ABS_TOP_OT_BUILDDIR}/cli "${ABS_TOP_OT_SRCDIR}"/script/cmake-build simulation \ -DOT_MTD=OFF -DOT_RCP=OFF -DOT_APP_NCP=OFF -DOT_APP_RCP=OFF \ @@ -152,6 +153,7 @@ do_build_otbr_docker() "-DOTBR_TELEMETRY_DATA_API=ON" "-DOTBR_TREL=ON" "-DOTBR_LINK_METRICS_TELEMETRY=ON" + "-DOTBR_SRP_ADVERTISING_PROXY=ON" ) sudo docker build -t "${OTBR_DOCKER_IMAGE}" \ -f ./etc/docker/Dockerfile . \