[ncp] implement ncp platform dnssd discovery methods (#12068)

This commit implements `otPlatDnssdStartBrowser` and
`otPlatDnssdStopBrowser` to support service discovery on NCP.

The platform APIs are intended to be used by
`ServiceDiscovery::Server::DiscoveryProxy` for discovery
functionality. This commit only supports browser for now and will
suppport Srv/Txt/Address resolver in other commits to avoid this
commit from being too large.

This commit contains these changes:
* Add new spinel property for starting / stopping service browser
* Implement encoding/decoding of the new property
* Add unit test for encoding/decoding
* Implement ncp version of dnssd platform API
  `otPlatDnssdStartBrowser` and `otPlatDnssdStopBrowser`
* Add property handler to get browser result on NCP side
* Add unit test to verify that the browser callback is correctly
  invoked after getting browser result.
This commit is contained in:
Li Cao
2025-10-31 07:37:18 -07:00
committed by GitHub
parent 4e2a570d0e
commit bbc73280ba
10 changed files with 405 additions and 3 deletions
+2
View File
@@ -1419,6 +1419,8 @@ const char *spinel_prop_key_to_cstr(spinel_prop_key_t prop_key)
{SPINEL_PROP_DNSSD_HOST, "DNSSD_HOST"},
{SPINEL_PROP_DNSSD_SERVICE, "DNSSD_SERVICE"},
{SPINEL_PROP_DNSSD_KEY_RECORD, "DNSSD_KEY_RECORD"},
{SPINEL_PROP_DNSSD_BROWSER, "DNSSD_BROWSER"},
{SPINEL_PROP_DNSSD_BROWSE_RESULT, "DNSSD_BROWSE_RESULT"},
{SPINEL_PROP_BORDER_AGENT_MESHCOP_SERVICE_STATE, "BORDER_AGENT_MESHCOP_SERVICE_STATE"},
{SPINEL_PROP_BORDER_AGENT_EPHEMERAL_KEY_STATE, "SPINEL_PROP_BORDER_AGENT_EPHEMERAL_KEY_STATE"},
{SPINEL_PROP_BORDER_AGENT_EPHEMERAL_KEY_ENABLE, "SPINEL_PROP_BORDER_AGENT_EPHEMERAL_KEY_ENABLE"},
+28
View File
@@ -4897,6 +4897,34 @@ enum
*/
SPINEL_PROP_DNSSD_KEY_RECORD = SPINEL_PROP_DNSSD__BEGIN + 5,
/// DNS-SD Service Browser
/**
* Format : Inserted/Removed
*
* `U` : The service type (e.g., "_mt._udp"). MUST NOT include domain name.
* `t(U)` : The sub-type label if browsing for sub-type, empty otherwise.
* `L` : The infrastructure network interface index to browse the service.
* `D` : The context of the request. A pointer to the browse callback to receive the result.
*
* NCP uses this property to browse services.
*/
SPINEL_PROP_DNSSD_BROWSER = SPINEL_PROP_DNSSD__BEGIN + 6,
/// DNS-SD Service Browser Result
/**
* Format : Set
*
* `U` : The service type (e.g., "_mt._udp"). MUST NOT include domain name.
* `t(U)` : The sub-type label if browsing for sub-type, empty otherwise.
* `U` : Service instance label.
* `L` : TTL in seconds. Zero TTL indicates that service is removed.
* `L` : The infrastructure network interface index.
* `D` : The context of the request. A pointer to the browse callback to receive the result.
*
* Host uses this property to send the result of the browser to NCP.
*/
SPINEL_PROP_DNSSD_BROWSE_RESULT = SPINEL_PROP_DNSSD__BEGIN + 7,
SPINEL_PROP_DNSSD__END = 0x950,
SPINEL_PROP_BORDER_AGENT__BEGIN = 0x950,
+94
View File
@@ -196,5 +196,99 @@ exit:
return error;
}
template <> otError EncodeDnssdDiscovery<otPlatDnssdBrowser>(Encoder &aEncoder, const otPlatDnssdBrowser &aDiscovery)
{
otError error = OT_ERROR_NONE;
SuccessOrExit(error = aEncoder.WriteUtf8(aDiscovery.mServiceType));
SuccessOrExit(error = aEncoder.OpenStruct());
if (aDiscovery.mSubTypeLabel != nullptr)
{
SuccessOrExit(error = aEncoder.WriteUtf8(aDiscovery.mSubTypeLabel));
}
SuccessOrExit(error = aEncoder.CloseStruct());
SuccessOrExit(error = aEncoder.WriteUint32(aDiscovery.mInfraIfIndex));
SuccessOrExit(error = aEncoder.WriteData(reinterpret_cast<const uint8_t *>(&aDiscovery.mCallback),
sizeof(aDiscovery.mCallback)));
exit:
return error;
}
otError EncodeDnssdBrowseResult(Encoder &aEncoder,
const otPlatDnssdBrowseResult &aBrowseResult,
const uint8_t *aCallbackData,
uint16_t aCallbackDataLen)
{
otError error = OT_ERROR_NONE;
SuccessOrExit(error = aEncoder.WriteUtf8(aBrowseResult.mServiceType));
SuccessOrExit(error = aEncoder.OpenStruct());
if (aBrowseResult.mSubTypeLabel != nullptr)
{
SuccessOrExit(error = aEncoder.WriteUtf8(aBrowseResult.mSubTypeLabel));
}
SuccessOrExit(error = aEncoder.CloseStruct());
SuccessOrExit(error = aEncoder.WriteUtf8(aBrowseResult.mServiceInstance));
SuccessOrExit(error = aEncoder.WriteUint32(aBrowseResult.mTtl));
SuccessOrExit(error = aEncoder.WriteUint32(aBrowseResult.mInfraIfIndex));
SuccessOrExit(error = aEncoder.WriteData(aCallbackData, aCallbackDataLen));
exit:
return error;
}
otError DecodeDnssdBrowser(Decoder &aDecoder,
otPlatDnssdBrowser &aBrowser,
const uint8_t *&aCallbackData,
uint16_t &aCallbackDataLen)
{
otError error = OT_ERROR_NONE;
SuccessOrExit(error = aDecoder.ReadUtf8(aBrowser.mServiceType));
SuccessOrExit(error = aDecoder.OpenStruct());
if (!aDecoder.IsAllReadInStruct())
{
SuccessOrExit(error = aDecoder.ReadUtf8(aBrowser.mSubTypeLabel));
}
else
{
aBrowser.mSubTypeLabel = nullptr;
}
SuccessOrExit(error = aDecoder.CloseStruct());
SuccessOrExit(error = aDecoder.ReadUint32(aBrowser.mInfraIfIndex));
SuccessOrExit(error = aDecoder.ReadData(aCallbackData, aCallbackDataLen));
exit:
return error;
}
otError DecodeDnssdBrowseResult(Decoder &aDecoder,
otPlatDnssdBrowseResult &aBrowseResult,
const uint8_t *&aCallbackData,
uint16_t &aCallbackDataLen)
{
otError error = OT_ERROR_NONE;
SuccessOrExit(error = aDecoder.ReadUtf8(aBrowseResult.mServiceType));
SuccessOrExit(error = aDecoder.OpenStruct());
if (!aDecoder.IsAllReadInStruct())
{
SuccessOrExit(error = aDecoder.ReadUtf8(aBrowseResult.mSubTypeLabel));
}
else
{
aBrowseResult.mSubTypeLabel = nullptr;
}
SuccessOrExit(error = aDecoder.CloseStruct());
SuccessOrExit(error = aDecoder.ReadUtf8(aBrowseResult.mServiceInstance));
SuccessOrExit(error = aDecoder.ReadUint32(aBrowseResult.mTtl));
SuccessOrExit(error = aDecoder.ReadUint32(aBrowseResult.mInfraIfIndex));
SuccessOrExit(error = aDecoder.ReadData(aCallbackData, aCallbackDataLen));
exit:
return error;
}
} // namespace Spinel
} // namespace ot
+59
View File
@@ -58,6 +58,35 @@ otError EncodeDnssd(Encoder &aEncoder,
otPlatDnssdRequestId aRequestId,
otPlatDnssdRegisterCallback aCallback);
/**
* Use Spinel::Encode to encode a Dnssd Discovery.
*
* DnssdDiscoveryType can be: otPlatDnssdBrowser, otPlatDnssdSrvResolver, otPlatDnssdTxtResolver,
* otPlatDnssdAddressResolver and otPlatDnssdRecordQuerier.
*
* A Spinel header and command MUST have been encoded by the encoder.
*
* @param[in] aEncoder A reference to the encoder object.
* @param[in] aDiscovery A reference to the dnssd discovery object.
*/
template <typename DnssdDiscoveryType>
otError EncodeDnssdDiscovery(Encoder &aEncoder, const DnssdDiscoveryType &aDiscovery);
/**
* Use Spinel::Encode to encode a Dnssd Browser result.
*
* A Spinel header and command MUST have been encoded by the encoder.
*
* @param[in] aEncoder A reference to the encoder object.
* @param[in] aBrowseResult A reference to the browse result object.
* @param[in] aCallbackData A pointer to the callback data.
* @param[in] aCallbackDataLen The data length.
*/
otError EncodeDnssdBrowseResult(Encoder &aEncoder,
const otPlatDnssdBrowseResult &aBrowseResult,
const uint8_t *aCallbackData,
uint16_t aCallbackDataLen);
/**
* Use Spinel::Decoder to decode a SPINEL_PROP_DNSSD_HOST message to a otPlatDnssdHost.
*
@@ -113,6 +142,36 @@ otError DecodeDnssdKey(Decoder &aDecoder,
const uint8_t *&aCallbackData,
uint16_t &aCallbackDataLen);
/**
* Use Spinel::Decoder to decode a SPINEL_PROP_DNSSD_BROWSER message to a otPlatDnssdBrowser.
*
* The decoder MUST have read the header, command and property key of the frame.
*
* @param[in] aDecoder A reference to the decoder object.
* @param[out] aBrowser A reference to the dnssd browser.
* @param[out] aCallbackData A reference to the pointer to the callback data.
* @param[out] aCallbackDataLen A reference to the callback data length.
*/
otError DecodeDnssdBrowser(Decoder &aDecoder,
otPlatDnssdBrowser &aBrowser,
const uint8_t *&aCallbackData,
uint16_t &aCallbackDataLen);
/**
* Use Spinel::Decoder to decode a Dnssd Browser result.
*
* The decoder MUST have read the header, command and property key of the frame.
*
* @param[in] aDecoder A reference to the decoder object.
* @param[out] aBrowseResult A reference to the browse result.
* @param[out] aCallbackData A reference to the pointer to the callback data.
* @param[out] aCallbackDataLen A reference to the callback data length.
*/
otError DecodeDnssdBrowseResult(Decoder &aDecoder,
otPlatDnssdBrowseResult &aBrowseResult,
const uint8_t *&aCallbackData,
uint16_t &aCallbackDataLen);
} // namespace Spinel
} // namespace ot
+46 -1
View File
@@ -310,13 +310,27 @@ public:
otPlatDnssdRequestId aRequestId,
otPlatDnssdRegisterCallback aCallback);
/**
* Starts a service browser.
*
* @param[in] aBrowser The browser to be started.
*/
void DnssdStartBrowser(const otPlatDnssdBrowser *aBrowser);
/**
* Stops a service browser.
*
* @param[in] aBrowser The browser to be stopped.
*/
void DnssdStopBrowser(const otPlatDnssdBrowser *aBrowser);
/**
* Gets the Dnssd state.
*
* Returns the platform dnssd state.
*/
otPlatDnssdState DnssdGetState(void);
#endif
#endif // OPENTHREAD_FTD && OPENTHREAD_CONFIG_NCP_DNSSD_ENABLE && OPENTHREAD_CONFIG_PLATFORM_DNSSD_ENABLE
protected:
static constexpr uint8_t kBitsPerByte = 8; ///< Number of bits in a byte.
@@ -851,6 +865,8 @@ protected:
#if OPENTHREAD_CONFIG_NCP_DNSSD_ENABLE && OPENTHREAD_CONFIG_PLATFORM_DNSSD_ENABLE
template <typename DnssdObjType> struct DnssdDiscoveryPropKeyFor;
template <typename DnssdObjType>
void DnssdUpdate(const DnssdObjType *aObj,
otPlatDnssdRequestId aRequestId,
@@ -875,6 +891,28 @@ protected:
}
}
/**
* Template for making service discovery.
*
* DnssdDiscoveryType can be: otPlatDnssdBrowser, otPlatDnssdSrvResolver, otPlatDnssdTxtResolver,
* otPlatDnssdAddressResolver and otPlatDnssdRecordQuerier.
*/
template <typename DnssdDiscoveryType> void DnssdUpdateDiscovery(const DnssdDiscoveryType *aDiscovery, bool aStart)
{
uint8_t header = SPINEL_HEADER_FLAG | SPINEL_HEADER_TX_NOTIFICATION_IID;
spinel_command_t cmd = aStart ? SPINEL_CMD_PROP_VALUE_INSERTED : SPINEL_CMD_PROP_VALUE_REMOVED;
VerifyOrExit(aDiscovery != nullptr);
VerifyOrExit(mDnssdState == OT_PLAT_DNSSD_READY);
SuccessOrExit(mEncoder.BeginFrame(header, cmd, DnssdDiscoveryPropKeyFor<DnssdDiscoveryType>::Key));
SuccessOrExit(Spinel::EncodeDnssdDiscovery(mEncoder, *aDiscovery));
SuccessOrExit(mEncoder.EndFrame());
exit:
return;
}
otPlatDnssdState mDnssdState;
#endif // OPENTHREAD_CONFIG_NCP_DNSSD_ENABLE && OPENTHREAD_CONFIG_PLATFORM_DNSSD_ENABLE
@@ -899,6 +937,13 @@ protected:
#endif
};
#if OPENTHREAD_FTD && OPENTHREAD_CONFIG_NCP_DNSSD_ENABLE && OPENTHREAD_CONFIG_PLATFORM_DNSSD_ENABLE
template <> struct NcpBase::DnssdDiscoveryPropKeyFor<otPlatDnssdBrowser>
{
static constexpr spinel_prop_key_t Key = SPINEL_PROP_DNSSD_BROWSER;
};
#endif
} // namespace Ncp
} // namespace ot
+1
View File
@@ -545,6 +545,7 @@ NcpBase::PropertyHandler NcpBase::FindSetPropertyHandler(spinel_prop_key_t aKey)
#if OPENTHREAD_CONFIG_PLATFORM_DNSSD_ENABLE && OPENTHREAD_CONFIG_NCP_DNSSD_ENABLE
OT_NCP_SET_HANDLER_ENTRY(SPINEL_PROP_DNSSD_STATE),
OT_NCP_SET_HANDLER_ENTRY(SPINEL_PROP_DNSSD_REQUEST_RESULT),
OT_NCP_SET_HANDLER_ENTRY(SPINEL_PROP_DNSSD_BROWSE_RESULT),
#endif
#if OPENTHREAD_CONFIG_BORDER_AGENT_ENABLE && OPENTHREAD_CONFIG_BORDER_AGENT_EPHEMERAL_KEY_ENABLE
OT_NCP_SET_HANDLER_ENTRY(SPINEL_PROP_BORDER_AGENT_EPHEMERAL_KEY_ENABLE),
+27
View File
@@ -1692,6 +1692,16 @@ void NcpBase::DnssdUnregisterKey(const otPlatDnssdKey *aKey,
DnssdUpdate(aKey, aRequestId, aCallback, /* aRegister */ false);
}
void NcpBase::DnssdStartBrowser(const otPlatDnssdBrowser *aBrowser)
{
DnssdUpdateDiscovery(aBrowser, /* aStart */ true);
}
void NcpBase::DnssdStopBrowser(const otPlatDnssdBrowser *aBrowser)
{
DnssdUpdateDiscovery(aBrowser, /* aStart */ false);
}
otPlatDnssdState NcpBase::DnssdGetState(void) { return mDnssdState; }
template <> otError NcpBase::HandlePropertySet<SPINEL_PROP_DNSSD_STATE>(void)
@@ -1731,6 +1741,23 @@ exit:
return error;
}
template <> otError NcpBase::HandlePropertySet<SPINEL_PROP_DNSSD_BROWSE_RESULT>(void)
{
otError error = OT_ERROR_NONE;
otPlatDnssdBrowseResult browseResult;
otPlatDnssdBrowseCallback callback = nullptr;
const uint8_t *context;
uint16_t contextLen;
SuccessOrExit(error = DecodeDnssdBrowseResult(mDecoder, browseResult, context, contextLen));
VerifyOrExit(contextLen == sizeof(otPlatDnssdBrowseCallback), error = OT_ERROR_PARSE);
callback = *reinterpret_cast<const otPlatDnssdBrowseCallback *>(context);
callback(mInstance, &browseResult);
exit:
return error;
}
#endif // OPENTHREAD_CONFIG_NCP_DNSSD_ENABLE && OPENTHREAD_CONFIG_PLATFORM_DNSSD_ENABLE
#if OPENTHREAD_CONFIG_BORDER_AGENT_ENABLE
+6 -2
View File
@@ -108,13 +108,17 @@ void otPlatDnssdUnregisterKey(otInstance *aInstance,
void otPlatDnssdStartBrowser(otInstance *aInstance, const otPlatDnssdBrowser *aBrowser)
{
OT_UNUSED_VARIABLE(aInstance);
OT_UNUSED_VARIABLE(aBrowser);
ot::Ncp::NcpBase *ncp = ot::Ncp::NcpBase::GetNcpInstance();
ncp->DnssdStartBrowser(aBrowser);
}
void otPlatDnssdStopBrowser(otInstance *aInstance, const otPlatDnssdBrowser *aBrowser)
{
OT_UNUSED_VARIABLE(aInstance);
OT_UNUSED_VARIABLE(aBrowser);
ot::Ncp::NcpBase *ncp = ot::Ncp::NcpBase::GetNcpInstance();
ncp->DnssdStopBrowser(aBrowser);
}
void otPlatDnssdStartSrvResolver(otInstance *aInstance, const otPlatDnssdSrvResolver *aResolver)