From e587d43e4b7bd808a45f565244b745194beb8f05 Mon Sep 17 00:00:00 2001 From: Abtin Keshavarzian Date: Wed, 5 Apr 2023 11:00:23 -0700 Subject: [PATCH 01/10] [tmf] skip copy of `MessageInfo` for empty ack (#8926) --- src/core/meshcop/panid_query_client.cpp | 7 +++---- src/core/thread/announce_begin_server.cpp | 9 ++++----- src/core/thread/panid_query_server.cpp | 7 +++---- 3 files changed, 10 insertions(+), 13 deletions(-) diff --git a/src/core/meshcop/panid_query_client.cpp b/src/core/meshcop/panid_query_client.cpp index b80ff4ef08a..4f9d9371cc0 100644 --- a/src/core/meshcop/panid_query_client.cpp +++ b/src/core/meshcop/panid_query_client.cpp @@ -97,9 +97,8 @@ Error PanIdQueryClient::SendQuery(uint16_t aPanId, template <> void PanIdQueryClient::HandleTmf(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo) { - uint16_t panId; - Ip6::MessageInfo responseInfo(aMessageInfo); - uint32_t mask; + uint16_t panId; + uint32_t mask; VerifyOrExit(aMessage.IsConfirmablePostRequest()); @@ -111,7 +110,7 @@ void PanIdQueryClient::HandleTmf(Coap::Message &aMessage, con mCallback.InvokeIfSet(panId, mask); - SuccessOrExit(Get().SendEmptyAck(aMessage, responseInfo)); + SuccessOrExit(Get().SendEmptyAck(aMessage, aMessageInfo)); LogInfo("sent panid query conflict response"); diff --git a/src/core/thread/announce_begin_server.cpp b/src/core/thread/announce_begin_server.cpp index ba22e023c02..80b74473fb9 100644 --- a/src/core/thread/announce_begin_server.cpp +++ b/src/core/thread/announce_begin_server.cpp @@ -66,10 +66,9 @@ void AnnounceBeginServer::SendAnnounce(uint32_t aChannelMask, uint8_t aCount, ui template <> void AnnounceBeginServer::HandleTmf(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo) { - uint32_t mask; - uint8_t count; - uint16_t period; - Ip6::MessageInfo responseInfo(aMessageInfo); + uint32_t mask; + uint8_t count; + uint16_t period; VerifyOrExit(aMessage.IsPostRequest()); VerifyOrExit((mask = MeshCoP::ChannelMaskTlv::GetChannelMask(aMessage)) != 0); @@ -81,7 +80,7 @@ void AnnounceBeginServer::HandleTmf(Coap::Message &aMessage, if (aMessage.IsConfirmable() && !aMessageInfo.GetSockAddr().IsMulticast()) { - SuccessOrExit(Get().SendEmptyAck(aMessage, responseInfo)); + SuccessOrExit(Get().SendEmptyAck(aMessage, aMessageInfo)); LogInfo("Sent announce begin response"); } diff --git a/src/core/thread/panid_query_server.cpp b/src/core/thread/panid_query_server.cpp index ae56cf64b75..2288c597827 100644 --- a/src/core/thread/panid_query_server.cpp +++ b/src/core/thread/panid_query_server.cpp @@ -60,9 +60,8 @@ PanIdQueryServer::PanIdQueryServer(Instance &aInstance) template <> void PanIdQueryServer::HandleTmf(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo) { - uint16_t panId; - Ip6::MessageInfo responseInfo(aMessageInfo); - uint32_t mask; + uint16_t panId; + uint32_t mask; VerifyOrExit(aMessage.IsPostRequest()); VerifyOrExit((mask = MeshCoP::ChannelMaskTlv::GetChannelMask(aMessage)) != 0); @@ -76,7 +75,7 @@ void PanIdQueryServer::HandleTmf(Coap::Message &aMessage, const if (aMessage.IsConfirmable() && !aMessageInfo.GetSockAddr().IsMulticast()) { - SuccessOrExit(Get().SendEmptyAck(aMessage, responseInfo)); + SuccessOrExit(Get().SendEmptyAck(aMessage, aMessageInfo)); LogInfo("sent panid query response"); } From 3ffe8516f7d943f0c2e6279a00b1291c046c82b3 Mon Sep 17 00:00:00 2001 From: Abtin Keshavarzian Date: Wed, 5 Apr 2023 15:49:08 -0700 Subject: [PATCH 02/10] [dns-client] add `ServiceMode` to control service resolution (#8772) This commit updates DNS client to add `otDnsServiceMode` to the `otDnsQueryConfig`. This new config property determines which records to query and allow the API user to control the behavior during service resolution: We can query for SRV record only or TXT record only, or query for both SRV and TXT records in the same message, or in parallel in different messages, or an "optimized" mode where client will first try to query for both records together in the same message but if server responds with an error, it then retries using two parallel separate queries. This gives flexibility and control to the API user. It also helps address situations where the server (DNS resolver) may not accept queries with more than one questions. To support this new feature, this commit updates and enhances the internal design of the `Dns::Client`. A new mechanism is added to allow multiple `Query` instances to be associated with each other under a main `Query` and responses for the related queries are saved until all are received and validated before finalizing the main `Query` and invoking the callback and allowing caller to retrieve the info. This commit also adds a detailed unit test `test_dns_client` which covers DNS client browse and service resolution validating behavior under all service modes. In order to test `Client` functionality, this commit also adds a `TestMode` in `Dns::ServiceDiscovery::Server` allowing us to change server behavior, e.g., reject messages with more than one question in query. --- include/openthread/dns_client.h | 31 +- include/openthread/instance.h | 2 +- src/cli/README.md | 32 +- src/cli/cli.cpp | 77 +- src/cli/cli.hpp | 2 + src/core/config/dns_client.h | 10 + src/core/net/dns_client.cpp | 774 +++++++++++++----- src/core/net/dns_client.hpp | 88 +- src/core/net/dnssd_server.cpp | 10 + src/core/net/dnssd_server.hpp | 25 +- .../expect/tun-dns-over-tcp-client.exp | 2 +- tests/unit/CMakeLists.txt | 21 + tests/unit/test_dns_client.cpp | 682 +++++++++++++++ 13 files changed, 1494 insertions(+), 262 deletions(-) create mode 100644 tests/unit/test_dns_client.cpp diff --git a/include/openthread/dns_client.h b/include/openthread/dns_client.h index c7f25b772b4..87ed2bac4f7 100644 --- a/include/openthread/dns_client.h +++ b/include/openthread/dns_client.h @@ -80,6 +80,23 @@ typedef enum OT_DNS_NAT64_DISALLOW = 2, ///< Do not allow NAT64 address translation during DNS client address resolution. } otDnsNat64Mode; +/** + * This enumeration type represents the service resolution mode in an `otDnsQueryConfig`. + * + * This is only used during DNS client service resolution `otDnsClientResolveService()`. It determines which + * record types to query. + * + */ +typedef enum +{ + OT_DNS_SERVICE_MODE_UNSPECIFIED = 0, ///< Mode is not specified. Use default service mode. + OT_DNS_SERVICE_MODE_SRV = 1, ///< Query for SRV record only. + OT_DNS_SERVICE_MODE_TXT = 2, ///< Query for TXT record only. + OT_DNS_SERVICE_MODE_SRV_TXT = 3, ///< Query for both SRV and TXT records in same message. + OT_DNS_SERVICE_MODE_SRV_TXT_SEPARATE = 4, ///< Query in parallel for SRV and TXT using separate messages. + OT_DNS_SERVICE_MODE_SRV_TXT_OPTIMIZE = 5, ///< Query for TXT/SRV together first, if fails then query separately. +} otDnsServiceMode; + /** * This enumeration type represents the DNS transport protocol in an `otDnsQueryConfig`. * @@ -102,11 +119,12 @@ typedef enum */ typedef struct otDnsQueryConfig { - otSockAddr mServerSockAddr; ///< Server address (IPv6 address/port). All zero or zero port for unspecified. + otSockAddr mServerSockAddr; ///< Server address (IPv6 addr/port). All zero or zero port for unspecified. uint32_t mResponseTimeout; ///< Wait time (in msec) to rx response. Zero indicates unspecified value. uint8_t mMaxTxAttempts; ///< Maximum tx attempts before reporting failure. Zero for unspecified value. otDnsRecursionFlag mRecursionFlag; ///< Indicates whether the server can resolve the query recursively or not. otDnsNat64Mode mNat64Mode; ///< Allow/Disallow NAT64 address translation during address resolution. + otDnsServiceMode mServiceMode; ///< Determines which records to query during service resolution. otDnsTransportProto mTransportProto; ///< Select default transport protocol. } otDnsQueryConfig; @@ -420,7 +438,8 @@ otError otDnsBrowseResponseGetServiceInstance(const otDnsBrowseResponse *aRespon * (note that it is a SHOULD and not a MUST requirement). This function tries to retrieve this info for a given service * instance when available. * - * - If no matching SRV record is found in @p aResponse, `OT_ERROR_NOT_FOUND` is returned. + * - If no matching SRV record is found in @p aResponse, `OT_ERROR_NOT_FOUND` is returned. In this case, no additional + * records (no TXT and/or AAAA) are read. * - If a matching SRV record is found in @p aResponse, @p aServiceInfo is updated and `OT_ERROR_NONE` is returned. * - If no matching TXT record is found in @p aResponse, `mTxtDataSize` in @p aServiceInfo is set to zero. * - If TXT data length is greater than `mTxtDataSize`, it is read partially and `mTxtDataTruncated` is set to true. @@ -550,8 +569,10 @@ otError otDnsServiceResponseGetServiceName(const otDnsServiceResponse *aResponse * * This function MUST only be used from `otDnsServiceCallback`. * - * - If no matching SRV record is found in @p aResponse, `OT_ERROR_NOT_FOUND` is returned. - * - If a matching SRV record is found in @p aResponse, @p aServiceInfo is updated and `OT_ERROR_NONE` is returned. + * - If a matching SRV record is found in @p aResponse, @p aServiceInfo is updated. + * - If no matching SRV record is found, `OT_ERROR_NOT_FOUND` is returned unless the query config for this query + * used `OT_DNS_SERVICE_MODE_TXT` for `mServiceMode` (meaning the request was only for TXT record). In this case, we + * still try to parse the SRV record from Additional Data Section of response (in case server provided the info). * - If no matching TXT record is found in @p aResponse, `mTxtDataSize` in @p aServiceInfo is set to zero. * - If TXT data length is greater than `mTxtDataSize`, it is read partially and `mTxtDataTruncated` is set to true. * - If no matching AAAA record is found in @p aResponse, `mHostAddress is set to all zero or unspecified address. @@ -562,7 +583,7 @@ otError otDnsServiceResponseGetServiceName(const otDnsServiceResponse *aResponse * @param[out] aServiceInfo A `ServiceInfo` to output the service instance information (MUST NOT be NULL). * * @retval OT_ERROR_NONE The service instance info was read. @p aServiceInfo is updated. - * @retval OT_ERROR_NOT_FOUND Could not find a matching SRV record in @p aResponse. + * @retval OT_ERROR_NOT_FOUND Could not find a required record in @p aResponse. * @retval OT_ERROR_NO_BUFS The host name and/or TXT data could not fit in the given buffers. * @retval OT_ERROR_PARSE Could not parse the records in the @p aResponse. * diff --git a/include/openthread/instance.h b/include/openthread/instance.h index f6eb33de173..367d8e9751c 100644 --- a/include/openthread/instance.h +++ b/include/openthread/instance.h @@ -53,7 +53,7 @@ extern "C" { * @note This number versions both OpenThread platform and user APIs. * */ -#define OPENTHREAD_API_VERSION (306) +#define OPENTHREAD_API_VERSION (307) /** * @addtogroup api-instance diff --git a/src/cli/README.md b/src/cli/README.md index 6d8369fd2ce..22da85b09c9 100644 --- a/src/cli/README.md +++ b/src/cli/README.md @@ -1113,7 +1113,20 @@ Done Get the default query config used by DNS client. -The config includes the server IPv6 address and port, response timeout in msec (wait time to rx response), maximum tx attempts before reporting failure, boolean flag to indicate whether the server can resolve the query recursively or not. +The config includes + +- Server IPv6 address and port +- Response timeout in msec (wait time to rx response) +- Maximum tx attempts before reporting failure +- Boolean flag to indicate whether the server can resolve the query recursively or not. +- Service resolution mode which specifies which records to query. Possible options are: + - `srv` : Query for SRV record only. + - `txt` : Query for TXT record only. + - `srv_txt` : Query for both SRV and TXT records in the same message. + - `srv_txt_sep`: Query in parallel for SRV and TXT using separate messages. + - `srv_txt_opt`: Query for TXT/SRV together first, if it fails then query separately. +- Whether to allow/disallow NAT64 address translation during address resolution (requires `OPENTHREAD_CONFIG_DNS_CLIENT_NAT64_ENABLE`) +- Transport protocol UDP or TCP (requires `OPENTHREAD_CONFIG_DNS_CLIENT_OVER_TCP_ENABLE`) ```bash > dns config @@ -1121,19 +1134,30 @@ Server: [fd00:0:0:0:0:0:0:1]:1234 ResponseTimeout: 5000 ms MaxTxAttempts: 2 RecursionDesired: no +ServiceMode: srv_txt_opt +Nat64Mode: allow TransportProtocol: udp Done > ``` -### dns config \[DNS server IP\] \[DNS server port\] \[response timeout (ms)\] \[max tx attempts\] \[recursion desired (boolean)\] \[transport protocol\] +### dns config \[DNS server IP\] \[DNS server port\] \[response timeout (ms)\] \[max tx attempts\] \[recursion desired (boolean)\] \[service mode] Set the default query config. +Service mode specifies which records to query. Possible options are: + +- `def` : Use default option. +- `srv` : Query for SRV record only. +- `txt` : Query for TXT record only. +- `srv_txt` : Query for both SRV and TXT records in the same message. +- `srv_txt_sep`: Query in parallel for SRV and TXT using separate messages. +- `srv_txt_opt`: Query for TXT/SRV together first, if it fails then query separately. + To set protocol effectively to tcp `OPENTHREAD_CONFIG_DNS_CLIENT_OVER_TCP_ENABLE` is required. ```bash -> dns config fd00::1 1234 5000 2 0 tcp +> dns config fd00::1 1234 5000 2 0 srv_txt_sep tcp Done > dns config @@ -1141,6 +1165,8 @@ Server: [fd00:0:0:0:0:0:0:1]:1234 ResponseTimeout: 5000 ms MaxTxAttempts: 2 RecursionDesired: no +ServiceMode: srv_txt_sep +Nat64Mode: allow TransportProtocol: tcp Done ``` diff --git a/src/cli/cli.cpp b/src/cli/cli.cpp index d0b2eae6676..dc248a46a22 100644 --- a/src/cli/cli.cpp +++ b/src/cli/cli.cpp @@ -2990,6 +2990,8 @@ template <> otError Interpreter::Process(Arg aArgs[]) * ResponseTimeout: 5000 ms * MaxTxAttempts: 2 * RecursionDesired: no + * ServiceMode: srv + * Nat64Mode: allow * Done * @endcode * @par api_copy @@ -3011,6 +3013,10 @@ template <> otError Interpreter::Process(Arg aArgs[]) OutputLine("MaxTxAttempts: %u", defaultConfig->mMaxTxAttempts); OutputLine("RecursionDesired: %s", (defaultConfig->mRecursionFlag == OT_DNS_FLAG_RECURSION_DESIRED) ? "yes" : "no"); + OutputLine("ServiceMode: %s", DnsConfigServiceModeToString(defaultConfig->mServiceMode)); +#if OPENTHREAD_CONFIG_DNS_CLIENT_NAT64_ENABLE + OutputLine("Nat64Mode: %s", (defaultConfig->mNat64Mode == OT_DNS_NAT64_ALLOW) ? "allow" : "disallow"); +#endif #if OPENTHREAD_CONFIG_DNS_CLIENT_OVER_TCP_ENABLE OutputLine("TransportProtocol: %s", (defaultConfig->mTransportProto == OT_DNS_TRANSPORT_UDP) ? "udp" : "tcp"); @@ -3046,7 +3052,7 @@ template <> otError Interpreter::Process(Arg aArgs[]) * #otDnsClientSetDefaultConfig * @cparam dns config [@ca{dns-server-IP}] [@ca{dns-server-port}] [@ca{response-timeout-ms}] [@ca{max-tx-attempts}] [@ca{recursion-desired-boolean}] + * --> [@ca{recursion-desired-boolean}] [@ca{service-mode}] * @par * We can leave some of the fields as unspecified (or use value zero). The * unspecified fields are replaced by the corresponding OT config option @@ -3256,11 +3262,69 @@ template <> otError Interpreter::Process(Arg aArgs[]) #if OPENTHREAD_CONFIG_DNS_CLIENT_ENABLE +const char *Interpreter::DnsConfigServiceModeToString(otDnsServiceMode aMode) const +{ + static const char *const kServiceModeStrings[] = { + "unspec", // OT_DNS_SERVICE_MODE_UNSPECIFIED (0) + "srv", // OT_DNS_SERVICE_MODE_SRV (1) + "txt", // OT_DNS_SERVICE_MODE_TXT (2) + "srv_txt", // OT_DNS_SERVICE_MODE_SRV_TXT (3) + "srv_txt_sep", // OT_DNS_SERVICE_MODE_SRV_TXT_SEPARATE (4) + "srv_txt_opt", // OT_DNS_SERVICE_MODE_SRV_TXT_OPTIMIZE (5) + }; + + static_assert(OT_DNS_SERVICE_MODE_UNSPECIFIED == 0, "OT_DNS_SERVICE_MODE_UNSPECIFIED value is incorrect"); + static_assert(OT_DNS_SERVICE_MODE_SRV == 1, "OT_DNS_SERVICE_MODE_SRV value is incorrect"); + static_assert(OT_DNS_SERVICE_MODE_TXT == 2, "OT_DNS_SERVICE_MODE_TXT value is incorrect"); + static_assert(OT_DNS_SERVICE_MODE_SRV_TXT == 3, "OT_DNS_SERVICE_MODE_SRV_TXT value is incorrect"); + static_assert(OT_DNS_SERVICE_MODE_SRV_TXT_SEPARATE == 4, "OT_DNS_SERVICE_MODE_SRV_TXT_SEPARATE value is incorrect"); + static_assert(OT_DNS_SERVICE_MODE_SRV_TXT_OPTIMIZE == 5, "OT_DNS_SERVICE_MODE_SRV_TXT_OPTIMIZE value is incorrect"); + + return Stringify(aMode, kServiceModeStrings); +} + +otError Interpreter::ParseDnsServiceMode(const Arg &aArg, otDnsServiceMode &aMode) const +{ + otError error = OT_ERROR_NONE; + + if (aArg == "def") + { + aMode = OT_DNS_SERVICE_MODE_UNSPECIFIED; + } + else if (aArg == "srv") + { + aMode = OT_DNS_SERVICE_MODE_SRV; + } + else if (aArg == "txt") + { + aMode = OT_DNS_SERVICE_MODE_TXT; + } + else if (aArg == "srv_txt") + { + aMode = OT_DNS_SERVICE_MODE_SRV_TXT; + } + else if (aArg == "srv_txt_sep") + { + aMode = OT_DNS_SERVICE_MODE_SRV_TXT_SEPARATE; + } + else if (aArg == "srv_txt_opt") + { + aMode = OT_DNS_SERVICE_MODE_SRV_TXT_OPTIMIZE; + } + else + { + error = OT_ERROR_INVALID_ARGS; + } + + return error; +} + otError Interpreter::GetDnsConfig(Arg aArgs[], otDnsQueryConfig *&aConfig) { // This method gets the optional DNS config from `aArgs[]`. // The format: `[server IP address] [server port] [timeout] - // [max tx attempt] [recursion desired]`. + // [max tx attempt] [recursion desired] [service mode] + // [transport]` otError error = OT_ERROR_NONE; bool recursionDesired; @@ -3292,11 +3356,15 @@ otError Interpreter::GetDnsConfig(Arg aArgs[], otDnsQueryConfig *&aConfig) aConfig->mRecursionFlag = recursionDesired ? OT_DNS_FLAG_RECURSION_DESIRED : OT_DNS_FLAG_NO_RECURSION; VerifyOrExit(!aArgs[5].IsEmpty()); - if (aArgs[5] == "tcp") + SuccessOrExit(error = ParseDnsServiceMode(aArgs[5], aConfig->mServiceMode)); + + VerifyOrExit(!aArgs[6].IsEmpty()); + + if (aArgs[6] == "tcp") { aConfig->mTransportProto = OT_DNS_TRANSPORT_TCP; } - else if (aArgs[5] == "udp") + else if (aArgs[6] == "udp") { aConfig->mTransportProto = OT_DNS_TRANSPORT_UDP; } @@ -3304,6 +3372,7 @@ otError Interpreter::GetDnsConfig(Arg aArgs[], otDnsQueryConfig *&aConfig) { error = OT_ERROR_INVALID_ARGS; } + exit: return error; } diff --git a/src/cli/cli.hpp b/src/cli/cli.hpp index bb220a8f23b..79faa376db8 100644 --- a/src/cli/cli.hpp +++ b/src/cli/cli.hpp @@ -459,6 +459,8 @@ class Interpreter : public OutputImplementer, public Output otError GetDnsConfig(Arg aArgs[], otDnsQueryConfig *&aConfig); static void HandleDnsAddressResponse(otError aError, const otDnsAddressResponse *aResponse, void *aContext); void HandleDnsAddressResponse(otError aError, const otDnsAddressResponse *aResponse); + const char *DnsConfigServiceModeToString(otDnsServiceMode aMode) const; + otError ParseDnsServiceMode(const Arg &aArg, otDnsServiceMode &aMode) const; #if OPENTHREAD_CONFIG_DNS_CLIENT_SERVICE_DISCOVERY_ENABLE void OutputDnsServiceInfo(uint8_t aIndentSize, const otDnsServiceInfo &aServiceInfo); static void HandleDnsBrowseResponse(otError aError, const otDnsBrowseResponse *aResponse, void *aContext); diff --git a/src/core/config/dns_client.h b/src/core/config/dns_client.h index 99a6520cfcc..d727261d8fe 100644 --- a/src/core/config/dns_client.h +++ b/src/core/config/dns_client.h @@ -151,6 +151,16 @@ #define OPENTHREAD_CONFIG_DNS_CLIENT_DEFAULT_RECURSION_DESIRED_FLAG 1 #endif +/** + * @def OPENTHREAD_CONFIG_DNS_CLIENT_DEFAULT_SERVICE_MODE + * + * Specifies the default `otDnsServiceMode` to use. The value MUST be from `otDnsServiceMode` enumeration. + * + */ +#ifndef OPENTHREAD_CONFIG_DNS_CLIENT_DEFAULT_SERVICE_MODE +#define OPENTHREAD_CONFIG_DNS_CLIENT_DEFAULT_SERVICE_MODE OT_DNS_SERVICE_MODE_SRV_TXT_OPTIMIZE +#endif + /** * @def OPENTHREAD_CONFIG_DNS_CLIENT_OVER_TCP_ENABLE * diff --git a/src/core/net/dns_client.cpp b/src/core/net/dns_client.cpp index e25732ed50e..531a6b20813 100644 --- a/src/core/net/dns_client.cpp +++ b/src/core/net/dns_client.cpp @@ -70,19 +70,27 @@ Client::QueryConfig::QueryConfig(InitMode aMode) SetResponseTimeout(kDefaultResponseTimeout); SetMaxTxAttempts(kDefaultMaxTxAttempts); SetRecursionFlag(kDefaultRecursionDesired ? kFlagRecursionDesired : kFlagNoRecursion); + SetServiceMode(kDefaultServiceMode); #if OPENTHREAD_CONFIG_DNS_CLIENT_NAT64_ENABLE SetNat64Mode(kDefaultNat64Allowed ? kNat64Allow : kNat64Disallow); #endif SetTransportProto(kDnsTransportUdp); } -void Client::QueryConfig::SetFrom(const QueryConfig &aConfig, const QueryConfig &aDefaultConfig) +void Client::QueryConfig::SetFrom(const QueryConfig *aConfig, const QueryConfig &aDefaultConfig) { // This method sets the config from `aConfig` replacing any // unspecified fields (value zero) with the fields from - // `aDefaultConfig`. + // `aDefaultConfig`. If `aConfig` is `nullptr` then + // `aDefaultConfig` is used. - *this = aConfig; + if (aConfig == nullptr) + { + *this = aDefaultConfig; + ExitNow(); + } + + *this = *aConfig; if (GetServerSockAddr().GetAddress().IsUnspecified()) { @@ -115,10 +123,19 @@ void Client::QueryConfig::SetFrom(const QueryConfig &aConfig, const QueryConfig SetNat64Mode(aDefaultConfig.GetNat64Mode()); } #endif + + if (GetServiceMode() == kServiceModeUnspecified) + { + SetServiceMode(aDefaultConfig.GetServiceMode()); + } + if (GetTransportProto() == kDnsTransportUnspecified) { SetTransportProto(aDefaultConfig.GetTransportProto()); } + +exit: + return; } //--------------------------------------------------------------------------------------------------------------------- @@ -230,22 +247,42 @@ Error Client::Response::FindARecord(Section aSection, const Name &aHostName, uin #if OPENTHREAD_CONFIG_DNS_CLIENT_SERVICE_DISCOVERY_ENABLE -Error Client::Response::FindServiceInfo(Section aSection, const Name &aName, ServiceInfo &aServiceInfo) const +void Client::Response::InitServiceInfo(ServiceInfo &aServiceInfo) const { - // This method searches for SRV and TXT records in the given - // section matching the record name against `aName`, and updates - // the `aServiceInfo` accordingly. It also searches for AAAA - // record for host name associated with the service (from SRV - // record). The search for AAAA record is always performed in - // Additional Data section (independent of the value given in - // `aSection`). + // This method initializes `aServiceInfo` setting all + // TTLs to zero and host name to empty string. - Error error; + aServiceInfo.mTtl = 0; + aServiceInfo.mHostAddressTtl = 0; + aServiceInfo.mTxtDataTtl = 0; + aServiceInfo.mTxtDataTruncated = false; + + AsCoreType(&aServiceInfo.mHostAddress).Clear(); + + if ((aServiceInfo.mHostNameBuffer != nullptr) && (aServiceInfo.mHostNameBufferSize > 0)) + { + aServiceInfo.mHostNameBuffer[0] = '\0'; + } +} + +Error Client::Response::ReadServiceInfo(Section aSection, const Name &aName, ServiceInfo &aServiceInfo) const +{ + // This method searches for SRV record in the given `aSection` + // matching the record name against `aName`, and updates the + // `aServiceInfo` accordingly. It also searches for AAAA record + // for host name associated with the service (from SRV record). + // The search for AAAA record is always performed in Additional + // Data section (independent of the value given in `aSection`). + + Error error = kErrorNone; uint16_t offset; uint16_t numRecords; Name hostName; SrvRecord srvRecord; - TxtRecord txtRecord; + + // A non-zero `mTtl` indicates that SRV record is already found + // and parsed from a previous response. + VerifyOrExit(aServiceInfo.mTtl == 0); VerifyOrExit(mMessage != nullptr, error = kErrorNotFound); @@ -277,56 +314,97 @@ Error Client::Response::FindServiceInfo(Section aSection, const Name &aName, Ser if (error == kErrorNotFound) { - AsCoreType(&aServiceInfo.mHostAddress).Clear(); - aServiceInfo.mHostAddressTtl = 0; - error = kErrorNone; + error = kErrorNone; } - SuccessOrExit(error); +exit: + return error; +} - // A null `mTxtData` indicates that caller does not want to retrieve TXT data. +Error Client::Response::ReadTxtRecord(Section aSection, const Name &aName, ServiceInfo &aServiceInfo) const +{ + // This method searches a TXT record in the given `aSection` + // matching the record name against `aName` and updates the TXT + // related properties in `aServicesInfo`. + // + // If no match is found `mTxtDataTtl` (which is initialized to zero) + // remains unchanged to indicate this. In this case this method still + // returns `kErrorNone`. + + Error error = kErrorNone; + uint16_t offset; + uint16_t numRecords; + TxtRecord txtRecord; + + // A non-zero `mTxtDataTtl` indicates that TXT record is already + // found and parsed from a previous response. + VerifyOrExit(aServiceInfo.mTxtDataTtl == 0); + + // A null `mTxtData` indicates that caller does not want to retrieve + // TXT data. VerifyOrExit(aServiceInfo.mTxtData != nullptr); - // Search for a matching TXT record. If not found, indicate this by - // setting `aServiceInfo.mTxtDataSize` to zero. + VerifyOrExit(mMessage != nullptr, error = kErrorNotFound); SelectSection(aSection, offset, numRecords); aServiceInfo.mTxtDataTruncated = false; - error = ResourceRecord::FindRecord(*mMessage, offset, numRecords, /* aIndex */ 0, aName, txtRecord); + SuccessOrExit(error = ResourceRecord::FindRecord(*mMessage, offset, numRecords, /* aIndex */ 0, aName, txtRecord)); - switch (error) - { - case kErrorNone: - error = txtRecord.ReadTxtData(*mMessage, offset, aServiceInfo.mTxtData, aServiceInfo.mTxtDataSize); + error = txtRecord.ReadTxtData(*mMessage, offset, aServiceInfo.mTxtData, aServiceInfo.mTxtDataSize); - if (error == kErrorNoBufs) - { - error = kErrorNone; - aServiceInfo.mTxtDataTruncated = true; - } + if (error == kErrorNoBufs) + { + error = kErrorNone; - SuccessOrExit(error); - aServiceInfo.mTxtDataTtl = txtRecord.GetTtl(); - break; + // Mark `mTxtDataTruncated` to indicate that we could not read + // the full TXT record into the given `mTxtData` buffer. + aServiceInfo.mTxtDataTruncated = true; + } - case kErrorNotFound: - aServiceInfo.mTxtDataSize = 0; - aServiceInfo.mTxtDataTtl = 0; - error = kErrorNone; - break; + SuccessOrExit(error); + aServiceInfo.mTxtDataTtl = txtRecord.GetTtl(); - default: - ExitNow(); +exit: + if (error == kErrorNotFound) + { + error = kErrorNone; } -exit: return error; } #endif // OPENTHREAD_CONFIG_DNS_CLIENT_SERVICE_DISCOVERY_ENABLE +void Client::Response::PopulateFrom(const Message &aMessage) +{ + // Populate `Response` with info from `aMessage`. + + uint16_t offset = aMessage.GetOffset(); + Header header; + + mMessage = &aMessage; + + IgnoreError(aMessage.Read(offset, header)); + offset += sizeof(Header); + + for (uint16_t num = 0; num < header.GetQuestionCount(); num++) + { + IgnoreError(Name::ParseName(aMessage, offset)); + offset += sizeof(Question); + } + + mAnswerOffset = offset; + IgnoreError(ResourceRecord::ParseRecords(aMessage, offset, header.GetAnswerCount())); + IgnoreError(ResourceRecord::ParseRecords(aMessage, offset, header.GetAuthorityRecordCount())); + mAdditionalOffset = offset; + IgnoreError(ResourceRecord::ParseRecords(aMessage, offset, header.GetAdditionalRecordCount())); + + mAnswerRecordCount = header.GetAnswerCount(); + mAdditionalRecordCount = header.GetAdditionalRecordCount(); +} + //--------------------------------------------------------------------------------------------------------------------- // Client::AddressResponse @@ -400,12 +478,20 @@ Error Client::BrowseResponse::GetServiceInfo(const char *aInstanceLabel, Service Error error; Name instanceName; - // Find a matching PTR record for the service instance label. - // Then search and read SRV, TXT and AAAA records in Additional Data section - // matching the same name to populate `aServiceInfo`. + // Find a matching PTR record for the service instance label. Then + // search and read SRV, TXT and AAAA records in Additional Data + // section matching the same name to populate `aServiceInfo`. SuccessOrExit(error = FindPtrRecord(aInstanceLabel, instanceName)); - error = FindServiceInfo(kAdditionalDataSection, instanceName, aServiceInfo); + + InitServiceInfo(aServiceInfo); + SuccessOrExit(error = ReadServiceInfo(kAdditionalDataSection, instanceName, aServiceInfo)); + SuccessOrExit(error = ReadTxtRecord(kAdditionalDataSection, instanceName, aServiceInfo)); + + if (aServiceInfo.mTxtDataTtl == 0) + { + aServiceInfo.mTxtDataSize = 0; + } exit: return error; @@ -497,10 +583,51 @@ Error Client::ServiceResponse::GetServiceName(char *aLabelBuffer, Error Client::ServiceResponse::GetServiceInfo(ServiceInfo &aServiceInfo) const { - // Search and read SRV, TXT records in Answer Section - // matching name from query. + // Search and read SRV, TXT records matching name from query. + + Error error = kErrorNotFound; + + InitServiceInfo(aServiceInfo); + + for (const Response *response = this; response != nullptr; response = response->mNext) + { + Name name(*response->mQuery, kNameOffsetInQuery); + QueryInfo info; + Section srvSection; + Section txtSection; + + info.ReadFrom(*response->mQuery); + + // Determine from which section we should try to read the SRV and + // TXT records based on the query type. + // + // In `kServiceQuerySrv` or `kServiceQueryTxt` we expect to see + // only one record (SRV or TXT) in the answer section, but we + // still try to read the other records from additional data + // section in case server provided them. + + srvSection = (info.mQueryType != kServiceQueryTxt) ? kAnswerSection : kAdditionalDataSection; + txtSection = (info.mQueryType != kServiceQuerySrv) ? kAnswerSection : kAdditionalDataSection; + + error = response->ReadServiceInfo(srvSection, name, aServiceInfo); + + if ((srvSection == kAdditionalDataSection) && (error == kErrorNotFound)) + { + error = kErrorNone; + } + + SuccessOrExit(error); + + SuccessOrExit(error = response->ReadTxtRecord(txtSection, name, aServiceInfo)); + } + + if (aServiceInfo.mTxtDataTtl == 0) + { + aServiceInfo.mTxtDataSize = 0; + } - return FindServiceInfo(kAnswerSection, Name(*mQuery, kNameOffsetInQuery), aServiceInfo); +exit: + return error; } Error Client::ServiceResponse::GetHostAddress(const char *aHostName, @@ -508,7 +635,19 @@ Error Client::ServiceResponse::GetHostAddress(const char *aHostName, Ip6::Address &aAddress, uint32_t &aTtl) const { - return FindHostAddress(kAdditionalDataSection, Name(aHostName), aIndex, aAddress, aTtl); + Error error = kErrorNotFound; + + for (const Response *response = this; response != nullptr; response = response->mNext) + { + error = FindHostAddress(kAdditionalDataSection, Name(aHostName), aIndex, aAddress, aTtl); + + if (error == kErrorNone) + { + break; + } + } + + return error; } #endif // OPENTHREAD_CONFIG_DNS_CLIENT_SERVICE_DISCOVERY_ENABLE @@ -526,24 +665,29 @@ const uint16_t Client::kServiceQueryRecordTypes[] = {ResourceRecord::kTypeSrv, R #endif const uint8_t Client::kQuestionCount[] = { - /* kIp6AddressQuery -> */ GetArrayLength(kIp6AddressQueryRecordTypes), // AAAA records + /* kIp6AddressQuery -> */ GetArrayLength(kIp6AddressQueryRecordTypes), // AAAA record #if OPENTHREAD_CONFIG_DNS_CLIENT_NAT64_ENABLE - /* kIp4AddressQuery -> */ GetArrayLength(kIp4AddressQueryRecordTypes), // A records + /* kIp4AddressQuery -> */ GetArrayLength(kIp4AddressQueryRecordTypes), // A record #endif #if OPENTHREAD_CONFIG_DNS_CLIENT_SERVICE_DISCOVERY_ENABLE - /* kBrowseQuery -> */ GetArrayLength(kBrowseQueryRecordTypes), // PTR records - /* kServiceQuery -> */ GetArrayLength(kServiceQueryRecordTypes), // SRV and TXT records + /* kBrowseQuery -> */ GetArrayLength(kBrowseQueryRecordTypes), // PTR record + /* kServiceQuerySrvTxt -> */ GetArrayLength(kServiceQueryRecordTypes), // SRV and TXT records + /* kServiceQuerySrv -> */ 1, // SRV record only + /* kServiceQueryTxt -> */ 1, // TXT record only #endif }; -const uint16_t *Client::kQuestionRecordTypes[] = { +const uint16_t *const Client::kQuestionRecordTypes[] = { /* kIp6AddressQuery -> */ kIp6AddressQueryRecordTypes, #if OPENTHREAD_CONFIG_DNS_CLIENT_NAT64_ENABLE /* kIp4AddressQuery -> */ kIp4AddressQueryRecordTypes, #endif #if OPENTHREAD_CONFIG_DNS_CLIENT_SERVICE_DISCOVERY_ENABLE /* kBrowseQuery -> */ kBrowseQueryRecordTypes, - /* kServiceQuery -> */ kServiceQueryRecordTypes, + /* kServiceQuerySrvTxt -> */ kServiceQueryRecordTypes, + /* kServiceQuerySrv -> */ &kServiceQueryRecordTypes[0], + /* kServiceQueryTxt -> */ &kServiceQueryRecordTypes[1], + #endif }; @@ -564,11 +708,15 @@ Client::Client(Instance &aInstance) static_assert(kIp4AddressQuery == 1, "kIp4AddressQuery value is not correct"); #if OPENTHREAD_CONFIG_DNS_CLIENT_SERVICE_DISCOVERY_ENABLE static_assert(kBrowseQuery == 2, "kBrowseQuery value is not correct"); - static_assert(kServiceQuery == 3, "kServiceQuery value is not correct"); + static_assert(kServiceQuerySrvTxt == 3, "kServiceQuerySrvTxt value is not correct"); + static_assert(kServiceQuerySrv == 4, "kServiceQuerySrv value is not correct"); + static_assert(kServiceQueryTxt == 5, "kServiceQueryTxt value is not correct"); #endif #elif OPENTHREAD_CONFIG_DNS_CLIENT_SERVICE_DISCOVERY_ENABLE static_assert(kBrowseQuery == 1, "kBrowseQuery value is not correct"); - static_assert(kServiceQuery == 2, "kServiceQuery value is not correct"); + static_assert(kServiceQuerySrvTxt == 2, "kServiceQuerySrvTxt value is not correct"); + static_assert(kServiceQuerySrv == 3, "kServiceQuerySrv value is not correct"); + static_assert(kServiceQueryTxt == 4, "kServiceQuerySrv value is not correct"); #endif } @@ -587,7 +735,7 @@ void Client::Stop(void) { Query *query; - while ((query = mQueries.GetHead()) != nullptr) + while ((query = mMainQueries.GetHead()) != nullptr) { FinalizeQuery(*query, kErrorAbort); } @@ -630,7 +778,7 @@ void Client::SetDefaultConfig(const QueryConfig &aQueryConfig) { QueryConfig startingDefault(QueryConfig::kInitFromDefaults); - mDefaultConfig.SetFrom(aQueryConfig, startingDefault); + mDefaultConfig.SetFrom(&aQueryConfig, startingDefault); #if OPENTHREAD_CONFIG_DNS_CLIENT_DEFAULT_SERVER_ADDRESS_AUTO_SET_ENABLE mUserDidSetDefaultAddress = !aQueryConfig.GetServerSockAddr().GetAddress().IsUnspecified(); @@ -669,10 +817,12 @@ Error Client::ResolveAddress(const char *aHostName, QueryInfo info; info.Clear(); - info.mQueryType = kIp6AddressQuery; + info.mQueryType = kIp6AddressQuery; + info.mConfig.SetFrom(aConfig, mDefaultConfig); info.mCallback.mAddressCallback = aCallback; + info.mCallbackContext = aContext; - return StartQuery(info, aConfig, nullptr, aHostName, aContext); + return StartQuery(info, nullptr, aHostName); } #if OPENTHREAD_CONFIG_DNS_CLIENT_NAT64_ENABLE @@ -684,10 +834,12 @@ Error Client::ResolveIp4Address(const char *aHostName, QueryInfo info; info.Clear(); - info.mQueryType = kIp4AddressQuery; + info.mQueryType = kIp4AddressQuery; + info.mConfig.SetFrom(aConfig, mDefaultConfig); info.mCallback.mAddressCallback = aCallback; + info.mCallbackContext = aContext; - return StartQuery(info, aConfig, nullptr, aHostName, aContext); + return StartQuery(info, nullptr, aHostName); } #endif @@ -698,10 +850,12 @@ Error Client::Browse(const char *aServiceName, BrowseCallback aCallback, void *a QueryInfo info; info.Clear(); - info.mQueryType = kBrowseQuery; + info.mQueryType = kBrowseQuery; + info.mConfig.SetFrom(aConfig, mDefaultConfig); info.mCallback.mBrowseCallback = aCallback; + info.mCallbackContext = aContext; - return StartQuery(info, aConfig, nullptr, aServiceName, aContext); + return StartQuery(info, nullptr, aServiceName); } Error Client::ResolveService(const char *aInstanceLabel, @@ -712,14 +866,40 @@ Error Client::ResolveService(const char *aInstanceLabel, { QueryInfo info; Error error; + QueryType secondQueryType = kNoQuery; VerifyOrExit(aInstanceLabel != nullptr, error = kErrorInvalidArgs); info.Clear(); - info.mQueryType = kServiceQuery; + + info.mConfig.SetFrom(aConfig, mDefaultConfig); + + switch (info.mConfig.GetServiceMode()) + { + case QueryConfig::kServiceModeSrvTxtSeparate: + secondQueryType = kServiceQueryTxt; + + OT_FALL_THROUGH; + + case QueryConfig::kServiceModeSrv: + info.mQueryType = kServiceQuerySrv; + break; + + case QueryConfig::kServiceModeTxt: + info.mQueryType = kServiceQueryTxt; + break; + + case QueryConfig::kServiceModeSrvTxt: + case QueryConfig::kServiceModeSrvTxtOptimize: + default: + info.mQueryType = kServiceQuerySrvTxt; + break; + } + info.mCallback.mServiceCallback = aCallback; + info.mCallbackContext = aContext; - error = StartQuery(info, aConfig, aInstanceLabel, aServiceName, aContext); + error = StartQuery(info, aInstanceLabel, aServiceName, secondQueryType); exit: return error; @@ -727,37 +907,17 @@ Error Client::ResolveService(const char *aInstanceLabel, #endif // OPENTHREAD_CONFIG_DNS_CLIENT_SERVICE_DISCOVERY_ENABLE -Error Client::StartQuery(QueryInfo &aInfo, - const QueryConfig *aConfig, - const char *aLabel, - const char *aName, - void *aContext) +Error Client::StartQuery(QueryInfo &aInfo, const char *aLabel, const char *aName, QueryType aSecondType) { - // This method assumes that `mQueryType` and `mCallback` to be - // already set by caller on `aInfo`. The `aLabel` can be `nullptr` - // and then `aName` provides the full name, otherwise the name is - // appended as `{aLabel}.{aName}`. + // The `aLabel` can be `nullptr` and then `aName` provides the + // full name, otherwise the name is appended as `{aLabel}. + // {aName}`. Error error; Query *query; VerifyOrExit(mSocket.IsBound(), error = kErrorInvalidState); - if (aConfig == nullptr) - { - aInfo.mConfig = mDefaultConfig; - } - else - { - // To form the config for this query, replace any unspecified - // fields (zero value) in the given `aConfig` with the fields - // from `mDefaultConfig`. - - aInfo.mConfig.SetFrom(*aConfig, mDefaultConfig); - } - - aInfo.mCallbackContext = aContext; - #if OPENTHREAD_CONFIG_DNS_CLIENT_NAT64_ENABLE if (aInfo.mQueryType == kIp4AddressQuery) { @@ -770,10 +930,33 @@ Error Client::StartQuery(QueryInfo &aInfo, #endif SuccessOrExit(error = AllocateQuery(aInfo, aLabel, aName, query)); - mQueries.Enqueue(*query); - if ((error = SendQuery(*query, aInfo, /* aUpdateTimer */ true)) != kErrorNone) + + mMainQueries.Enqueue(*query); + + error = SendQuery(*query, aInfo, /* aUpdateTimer */ true); + VerifyOrExit(error == kErrorNone, FreeQuery(*query)); + + if (aSecondType != kNoQuery) { - FreeQuery(*query); + Query *secondQuery; + + aInfo.mQueryType = aSecondType; + aInfo.mMessageId = 0; + aInfo.mTransmissionCount = 0; + aInfo.mMainQuery = query; + + // We intentionally do not use `error` here so in the unlikely + // case where we cannot allocate the second query we can proceed + // with the first one. + SuccessOrExit(AllocateQuery(aInfo, aLabel, aName, secondQuery)); + + IgnoreError(SendQuery(*secondQuery, aInfo, /* aUpdateTiemr */ true)); + + // Update first query to link to second one by updating + // its `mNextQuery`. + aInfo.ReadFrom(*query); + aInfo.mNextQuery = secondQuery; + UpdateQuery(*query, aInfo); } exit: @@ -805,7 +988,29 @@ Error Client::AllocateQuery(const QueryInfo &aInfo, const char *aLabel, const ch return error; } -void Client::FreeQuery(Query &aQuery) { mQueries.DequeueAndFree(aQuery); } +Client::Query &Client::FindMainQuery(Query &aQuery) +{ + QueryInfo info; + + info.ReadFrom(aQuery); + + return (info.mMainQuery == nullptr) ? aQuery : *info.mMainQuery; +} + +void Client::FreeQuery(Query &aQuery) +{ + Query &mainQuery = FindMainQuery(aQuery); + QueryInfo info; + + mMainQueries.Dequeue(mainQuery); + + for (Query *query = &mainQuery; query != nullptr; query = info.mNextQuery) + { + info.ReadFrom(*query); + FreeMessage(info.mSavedResponse); + query->Free(); + } +} Error Client::SendQuery(Query &aQuery, QueryInfo &aInfo, bool aUpdateTimer) { @@ -944,24 +1149,24 @@ Error Client::AppendNameFromQuery(const Query &aQuery, Message &aMessage) void Client::FinalizeQuery(Query &aQuery, Error aError) { - Response response; - QueryInfo info; + Response response; + Query &mainQuery = FindMainQuery(aQuery); response.mInstance = &Get(); - response.mQuery = &aQuery; - info.ReadFrom(aQuery); + response.mQuery = &mainQuery; - FinalizeQuery(response, info.mQueryType, aError); + FinalizeQuery(response, aError); } -void Client::FinalizeQuery(Response &aResponse, QueryType aType, Error aError) +void Client::FinalizeQuery(Response &aResponse, Error aError) { - Callback callback; - void *context; + QueryType type; + Callback callback; + void *context; - GetCallback(*aResponse.mQuery, callback, context); + GetQueryTypeAndCallback(*aResponse.mQuery, type, callback, context); - switch (aType) + switch (type) { case kIp6AddressQuery: #if OPENTHREAD_CONFIG_DNS_CLIENT_NAT64_ENABLE @@ -981,24 +1186,29 @@ void Client::FinalizeQuery(Response &aResponse, QueryType aType, Error aError) } break; - case kServiceQuery: + case kServiceQuerySrvTxt: + case kServiceQuerySrv: + case kServiceQueryTxt: if (callback.mServiceCallback != nullptr) { callback.mServiceCallback(aError, &aResponse, context); } break; #endif + case kNoQuery: + break; } FreeQuery(*aResponse.mQuery); } -void Client::GetCallback(const Query &aQuery, Callback &aCallback, void *&aContext) +void Client::GetQueryTypeAndCallback(const Query &aQuery, QueryType &aType, Callback &aCallback, void *&aContext) { QueryInfo info; info.ReadFrom(aQuery); + aType = info.mQueryType; aCallback = info.mCallback; aContext = info.mCallbackContext; } @@ -1008,17 +1218,21 @@ Client::Query *Client::FindQueryById(uint16_t aMessageId) Query *matchedQuery = nullptr; QueryInfo info; - for (Query &query : mQueries) + for (Query &mainQuery : mMainQueries) { - info.ReadFrom(query); - - if (info.mMessageId == aMessageId) + for (Query *query = &mainQuery; query != nullptr; query = info.mNextQuery) { - matchedQuery = &query; - break; + info.ReadFrom(*query); + + if (info.mMessageId == aMessageId) + { + matchedQuery = query; + ExitNow(); + } } } +exit: return matchedQuery; } @@ -1029,58 +1243,78 @@ void Client::HandleUdpReceive(void *aContext, otMessage *aMessage, const otMessa static_cast(aContext)->ProcessResponse(AsCoreType(aMessage)); } -void Client::ProcessResponse(const Message &aMessage) +void Client::ProcessResponse(const Message &aResponseMessage) { - Response response; - QueryType type; - Error responseError; + Error responseError; + Query *query; - response.mInstance = &Get(); - response.mMessage = &aMessage; + SuccessOrExit(ParseResponse(aResponseMessage, query, responseError)); + + if (responseError != kErrorNone) + { + // Received an error from server, check if we can replace + // the query. + +#if OPENTHREAD_CONFIG_DNS_CLIENT_NAT64_ENABLE + if (ReplaceWithIp4Query(*query) == kErrorNone) + { + ExitNow(); + } +#endif +#if OPENTHREAD_CONFIG_DNS_CLIENT_SERVICE_DISCOVERY_ENABLE + if (ReplaceWithSeparateSrvTxtQueries(*query) == kErrorNone) + { + ExitNow(); + } +#endif - // We intentionally parse the response in a separate method - // `ParseResponse()` to free all the stack allocated variables - // (e.g., `QueryInfo`) used during parsing of the message before - // finalizing the query and invoking the user's callback. + FinalizeQuery(*query, responseError); + ExitNow(); + } - SuccessOrExit(ParseResponse(response, type, responseError)); - FinalizeQuery(response, type, responseError); + // Received successful response from server. + + if (!CanFinalizeQuery(*query)) + { + SaveQueryResponse(*query, aResponseMessage); + ExitNow(); + } + + PrepareResponseAndFinalize(FindMainQuery(*query), aResponseMessage, nullptr); exit: return; } -Error Client::ParseResponse(Response &aResponse, QueryType &aType, Error &aResponseError) +Error Client::ParseResponse(const Message &aResponseMessage, Query *&aQuery, Error &aResponseError) { - Error error = kErrorNone; - const Message &message = *aResponse.mMessage; - uint16_t offset = message.GetOffset(); - Header header; - QueryInfo info; - Name queryName; - - SuccessOrExit(error = message.Read(offset, header)); + Error error = kErrorNone; + uint16_t offset = aResponseMessage.GetOffset(); + Header header; + QueryInfo info; + Name queryName; + + SuccessOrExit(error = aResponseMessage.Read(offset, header)); offset += sizeof(Header); VerifyOrExit((header.GetType() == Header::kTypeResponse) && (header.GetQueryType() == Header::kQueryTypeStandard) && !header.IsTruncationFlagSet(), error = kErrorDrop); - aResponse.mQuery = FindQueryById(header.GetMessageId()); - VerifyOrExit(aResponse.mQuery != nullptr, error = kErrorNotFound); + aQuery = FindQueryById(header.GetMessageId()); + VerifyOrExit(aQuery != nullptr, error = kErrorNotFound); - info.ReadFrom(*aResponse.mQuery); - aType = info.mQueryType; + info.ReadFrom(*aQuery); - queryName.SetFromMessage(*aResponse.mQuery, kNameOffsetInQuery); + queryName.SetFromMessage(*aQuery, kNameOffsetInQuery); // Check the Question Section - if (header.GetQuestionCount() == kQuestionCount[aType]) + if (header.GetQuestionCount() == kQuestionCount[info.mQueryType]) { - for (uint8_t num = 0; num < kQuestionCount[aType]; num++) + for (uint8_t num = 0; num < kQuestionCount[info.mQueryType]; num++) { - SuccessOrExit(error = Name::CompareName(message, offset, queryName)); + SuccessOrExit(error = Name::CompareName(aResponseMessage, offset, queryName)); offset += sizeof(Question); } } @@ -1092,74 +1326,103 @@ Error Client::ParseResponse(Response &aResponse, QueryType &aType, Error &aRespo // Check the answer, authority and additional record sections - aResponse.mAnswerOffset = offset; - SuccessOrExit(error = ResourceRecord::ParseRecords(message, offset, header.GetAnswerCount())); - SuccessOrExit(error = ResourceRecord::ParseRecords(message, offset, header.GetAuthorityRecordCount())); - aResponse.mAdditionalOffset = offset; - SuccessOrExit(error = ResourceRecord::ParseRecords(message, offset, header.GetAdditionalRecordCount())); - - aResponse.mAnswerRecordCount = header.GetAnswerCount(); - aResponse.mAdditionalRecordCount = header.GetAdditionalRecordCount(); + SuccessOrExit(error = ResourceRecord::ParseRecords(aResponseMessage, offset, header.GetAnswerCount())); + SuccessOrExit(error = ResourceRecord::ParseRecords(aResponseMessage, offset, header.GetAuthorityRecordCount())); + SuccessOrExit(error = ResourceRecord::ParseRecords(aResponseMessage, offset, header.GetAdditionalRecordCount())); - // Check the response code from server + // Read the response code aResponseError = Header::ResponseCodeToError(header.GetResponseCode()); -#if OPENTHREAD_CONFIG_DNS_CLIENT_NAT64_ENABLE +exit: + return error; +} - if (aType == kIp6AddressQuery) - { - Ip6::Address ip6ddress; - uint32_t ttl; - ARecord aRecord; +bool Client::CanFinalizeQuery(Query &aQuery) +{ + // Determines whether we can finalize a main query by checking if + // we have received and saved responses for all other related + // queries associated with `aQuery`. Note that this method is + // called when we receive a response for `aQeury`, so no need to + // check for a saved response for `aQuery` itself. - // If the response does not contain an answer for the IPv6 address - // resolution query and if NAT64 is allowed for this query, we can - // perform IPv4 to IPv6 address translation. + bool canFinalize = true; + QueryInfo info; - VerifyOrExit(aResponse.FindHostAddress(Response::kAnswerSection, queryName, /* aIndex */ 0, ip6ddress, ttl) != - kErrorNone); - VerifyOrExit(info.mConfig.GetNat64Mode() == QueryConfig::kNat64Allow); + for (Query *query = &FindMainQuery(aQuery); query != nullptr; query = info.mNextQuery) + { + info.ReadFrom(*query); - // First, we check if the response already contains an A record - // (IPv4 address) for the query name. + if (query == &aQuery) + { + continue; + } - if (aResponse.FindARecord(Response::kAdditionalDataSection, queryName, /* aIndex */ 0, aRecord) == kErrorNone) + if (info.mSavedResponse == nullptr) { - aResponse.mIp6QueryResponseRequiresNat64 = true; - aResponseError = kErrorNone; + canFinalize = false; ExitNow(); } + } - // Otherwise, we send a new query for IPv4 address resolution - // for the same host name. We reuse the existing `query` - // instance and keep all the info but clear `mTransmissionCount` - // and `mMessageId` (so that a new random message ID is - // selected). The new `info` will be saved in the query in - // `SendQuery()`. Note that the current query is still in the - // `mQueries` list when `SendQuery()` selects a new random - // message ID, so the existing message ID for this query will - // not be reused. Since the query is not yet resolved, we - // return `kErrorPending`. +exit: + return canFinalize; +} - info.mQueryType = kIp4AddressQuery; - info.mMessageId = 0; - info.mTransmissionCount = 0; +void Client::SaveQueryResponse(Query &aQuery, const Message &aResponseMessage) +{ + QueryInfo info; - IgnoreReturnValue(SendQuery(*aResponse.mQuery, info, /* aUpdateTimer */ true)); + info.ReadFrom(aQuery); + VerifyOrExit(info.mSavedResponse == nullptr); - error = kErrorPending; - } + // If `Clone()` fails we let retry or timeout handle the error. + info.mSavedResponse = aResponseMessage.Clone(); -#endif // OPENTHREAD_CONFIG_DNS_CLIENT_NAT64_ENABLE + UpdateQuery(aQuery, info); exit: - if (error != kErrorNone) + return; +} + +Client::Query *Client::PopulateResponse(Response &aResponse, Query &aQuery, const Message &aResponseMessage) +{ + // Populate `aResponse` for `aQuery`. If there is a saved response + // message for `aQuery` we use it, otherwise, we use + // `aResponseMessage`. + + QueryInfo info; + + info.ReadFrom(aQuery); + + aResponse.mInstance = &Get(); + aResponse.mQuery = &aQuery; + aResponse.PopulateFrom((info.mSavedResponse == nullptr) ? aResponseMessage : *info.mSavedResponse); + + return info.mNextQuery; +} + +void Client::PrepareResponseAndFinalize(Query &aQuery, const Message &aResponseMessage, Response *aPrevResponse) +{ + // This method prepares a list of chained `Response` instances + // corresponding to all related (chained) queries. It uses + // recursion to go through the queries and construct the + // `Response` chain. + + Response response; + Query *nextQuery; + + nextQuery = PopulateResponse(response, aQuery, aResponseMessage); + response.mNext = aPrevResponse; + + if (nextQuery != nullptr) { - LogInfo("Failed to parse response %s", ErrorToString(error)); + PrepareResponseAndFinalize(*nextQuery, aResponseMessage, &response); + } + else + { + FinalizeQuery(response, kErrorNone); } - - return error; } void Client::HandleTimer(void) @@ -1171,29 +1434,40 @@ void Client::HandleTimer(void) bool hasTcpQuery = false; #endif - for (Query &query : mQueries) + for (Query &mainQuery : mMainQueries) { - info.ReadFrom(query); - - if (now >= info.mRetransmissionTime) + for (Query *query = &mainQuery; query != nullptr; query = info.mNextQuery) { - if (info.mTransmissionCount >= info.mConfig.GetMaxTxAttempts()) + info.ReadFrom(*query); + + if (info.mSavedResponse != nullptr) { - FinalizeQuery(query, kErrorResponseTimeout); continue; } - IgnoreReturnValue(SendQuery(query, info, /* aUpdateTimer */ false)); - } + if (now >= info.mRetransmissionTime) + { + if (info.mTransmissionCount >= info.mConfig.GetMaxTxAttempts()) + { + FinalizeQuery(*query, kErrorResponseTimeout); + continue; + } - nextTime = Min(nextTime, info.mRetransmissionTime); + IgnoreError(SendQuery(*query, info, /* aUpdateTimer */ false)); + } + + if (nextTime > info.mRetransmissionTime) + { + nextTime = info.mRetransmissionTime; + } #if OPENTHREAD_CONFIG_DNS_CLIENT_OVER_TCP_ENABLE - if (info.mConfig.GetTransportProto() == QueryConfig::kDnsTransportTcp) - { - hasTcpQuery = true; - } + if (info.mConfig.GetTransportProto() == QueryConfig::kDnsTransportTcp) + { + hasTcpQuery = true; + } #endif + } } if (nextTime < now.GetDistantFuture()) @@ -1209,6 +1483,76 @@ void Client::HandleTimer(void) #endif } +#if OPENTHREAD_CONFIG_DNS_CLIENT_NAT64_ENABLE + +Error Client::ReplaceWithIp4Query(Query &aQuery) +{ + Error error = kErrorFailed; + QueryInfo info; + + info.ReadFrom(aQuery); + + VerifyOrExit(info.mQueryType == kIp4AddressQuery); + VerifyOrExit(info.mConfig.GetNat64Mode() == QueryConfig::kNat64Allow); + + // We send a new query for IPv4 address resolution + // for the same host name. We reuse the existing `aQuery` + // instance and keep all the info but clear `mTransmissionCount` + // and `mMessageId` (so that a new random message ID is + // selected). The new `info` will be saved in the query in + // `SendQuery()`. Note that the current query is still in the + // `mMainQueries` list when `SendQuery()` selects a new random + // message ID, so the existing message ID for this query will + // not be reused. + + info.mQueryType = kIp4AddressQuery; + info.mMessageId = 0; + info.mTransmissionCount = 0; + + IgnoreError(SendQuery(aQuery, info, /* aUpdateTimer */ true)); + error = kErrorNone; + +exit: + return error; +} + +#endif // OPENTHREAD_CONFIG_DNS_CLIENT_NAT64_ENABLE + +#if OPENTHREAD_CONFIG_DNS_CLIENT_SERVICE_DISCOVERY_ENABLE + +Error Client::ReplaceWithSeparateSrvTxtQueries(Query &aQuery) +{ + Error error = kErrorFailed; + QueryInfo info; + Query *secondQuery; + + info.ReadFrom(aQuery); + + VerifyOrExit(info.mQueryType == kServiceQuerySrvTxt); + VerifyOrExit(info.mConfig.GetServiceMode() == QueryConfig::kServiceModeSrvTxtOptimize); + + secondQuery = aQuery.Clone(); + VerifyOrExit(secondQuery != nullptr); + + info.mQueryType = kServiceQueryTxt; + info.mMessageId = 0; + info.mTransmissionCount = 0; + info.mMainQuery = &aQuery; + IgnoreError(SendQuery(*secondQuery, info, /* aUpdateTimer */ true)); + + info.mQueryType = kServiceQuerySrv; + info.mMessageId = 0; + info.mTransmissionCount = 0; + info.mNextQuery = secondQuery; + IgnoreError(SendQuery(aQuery, info, /* aUpdateTimer */ true)); + error = kErrorNone; + +exit: + return error; +} + +#endif // OPENTHREAD_CONFIG_DNS_CLIENT_SERVICE_DISCOVERY_ENABLE + #if OPENTHREAD_CONFIG_DNS_CLIENT_OVER_TCP_ENABLE void Client::PrepareTcpMessage(Message &aMessage) { @@ -1334,24 +1678,7 @@ void Client::HandleTcpReceiveAvailable(otTcpEndpoint *aEndpoint, totalRead += length + sizeof(uint16_t); // Now process the read message as query response. - { - Response response; - QueryType type; - Error responseError; - - response.mInstance = &Get(); - response.mMessage = message; - - if (ParseResponse(response, type, responseError) == kErrorNone) - { - if (responseError == kErrorNone && length > OPENTHREAD_CONFIG_DNS_CLIENT_OVER_TCP_QUERY_MAX_SIZE) - { - LogWarn("Dns query over TCP wasn't received - message is too big."); - responseError = kErrorNoBufs; - } - FinalizeQuery(response, type, responseError); - } - } + ProcessResponse(*message); IgnoreError(message->SetLength(0)); @@ -1383,12 +1710,13 @@ void Client::HandleTcpDisconnected(otTcpEndpoint *aEndpoint, otTcpDisconnectedRe mTcpState = kTcpUninitialized; // Abort queries in case of connection failures - for (Query &query : mQueries) + for (Query &mainQuery : mMainQueries) { - info.ReadFrom(query); + info.ReadFrom(mainQuery); + if (info.mConfig.GetTransportProto() == QueryConfig::kDnsTransportTcp) { - FinalizeQuery(query, kErrorAbort); + FinalizeQuery(mainQuery, kErrorAbort); } } } diff --git a/src/core/net/dns_client.hpp b/src/core/net/dns_client.hpp index e3147455b24..74a74036de5 100644 --- a/src/core/net/dns_client.hpp +++ b/src/core/net/dns_client.hpp @@ -145,6 +145,20 @@ class Client : public InstanceLocator, private NonCopyable }; #endif + /** + * This enumeration type represents the service resolution mode. + * + */ + enum ServiceMode : uint8_t + { + kServiceModeUnspecified = OT_DNS_SERVICE_MODE_UNSPECIFIED, ///< Unspecified. Use default. + kServiceModeSrv = OT_DNS_SERVICE_MODE_SRV, ///< SRV record only. + kServiceModeTxt = OT_DNS_SERVICE_MODE_TXT, ///< TXT record only. + kServiceModeSrvTxt = OT_DNS_SERVICE_MODE_SRV_TXT, ///< SRV and TXT same msg. + kServiceModeSrvTxtSeparate = OT_DNS_SERVICE_MODE_SRV_TXT_SEPARATE, ///< SRV and TXT separate msgs. + kServiceModeSrvTxtOptimize = OT_DNS_SERVICE_MODE_SRV_TXT_OPTIMIZE, ///< Same msg first, if fail separate. + }; + /** * This enumeration type represents the DNS transport protocol selection. * @@ -206,6 +220,13 @@ class Client : public InstanceLocator, private NonCopyable */ Nat64Mode GetNat64Mode(void) const { return static_cast(mNat64Mode); } #endif + /** + * This method gets the service resolution mode. + * + * @returns The service resolution mode. + * + */ + ServiceMode GetServiceMode(void) const { return static_cast(mServiceMode); } /** * This method gets the transport protocol. @@ -220,6 +241,10 @@ class Client : public InstanceLocator, private NonCopyable static constexpr uint16_t kDefaultServerPort = OPENTHREAD_CONFIG_DNS_CLIENT_DEFAULT_SERVER_PORT; static constexpr uint8_t kDefaultMaxTxAttempts = OPENTHREAD_CONFIG_DNS_CLIENT_DEFAULT_MAX_TX_ATTEMPTS; static constexpr bool kDefaultRecursionDesired = OPENTHREAD_CONFIG_DNS_CLIENT_DEFAULT_RECURSION_DESIRED_FLAG; + static constexpr ServiceMode kDefaultServiceMode = + static_cast(OPENTHREAD_CONFIG_DNS_CLIENT_DEFAULT_SERVICE_MODE); + + static_assert(kDefaultServiceMode != kServiceModeUnspecified, "Invalid default service mode"); #if OPENTHREAD_CONFIG_DNS_CLIENT_NAT64_ENABLE static constexpr bool kDefaultNat64Allowed = OPENTHREAD_CONFIG_DNS_CLIENT_DEFAULT_NAT64_ALLOWED; @@ -239,6 +264,7 @@ class Client : public InstanceLocator, private NonCopyable void SetResponseTimeout(uint32_t aResponseTimeout) { mResponseTimeout = aResponseTimeout; } void SetMaxTxAttempts(uint8_t aMaxTxAttempts) { mMaxTxAttempts = aMaxTxAttempts; } void SetRecursionFlag(RecursionFlag aFlag) { mRecursionFlag = static_cast(aFlag); } + void SetServiceMode(ServiceMode aMode) { mServiceMode = static_cast(aMode); } #if OPENTHREAD_CONFIG_DNS_CLIENT_NAT64_ENABLE void SetNat64Mode(Nat64Mode aMode) { mNat64Mode = static_cast(aMode); } #endif @@ -246,7 +272,8 @@ class Client : public InstanceLocator, private NonCopyable { mTransportProto = static_cast(aTransportProto); } - void SetFrom(const QueryConfig &aConfig, const QueryConfig &aDefaultConfig); + + void SetFrom(const QueryConfig *aConfig, const QueryConfig &aDefaultConfig); }; #if OPENTHREAD_CONFIG_DNS_CLIENT_SERVICE_DISCOVERY_ENABLE @@ -292,12 +319,16 @@ class Client : public InstanceLocator, private NonCopyable #endif #if OPENTHREAD_CONFIG_DNS_CLIENT_SERVICE_DISCOVERY_ENABLE - Error FindServiceInfo(Section aSection, const Name &aName, ServiceInfo &aServiceInfo) const; + void InitServiceInfo(ServiceInfo &aServiceInfo) const; + Error ReadServiceInfo(Section aSection, const Name &aName, ServiceInfo &aServiceInfo) const; + Error ReadTxtRecord(Section aSection, const Name &aName, ServiceInfo &aServiceInfo) const; #endif + void PopulateFrom(const Message &aMessage); Instance *mInstance; // The OpenThread instance. Query *mQuery; // The associated query. const Message *mMessage; // The response message. + Response *mNext; // The next response when we have related queries. uint16_t mAnswerOffset; // Answer section offset in `mMessage`. uint16_t mAnswerRecordCount; // Number of records in answer section. uint16_t mAdditionalOffset; // Additional data section offset in `mMessage`. @@ -720,9 +751,12 @@ class Client : public InstanceLocator, private NonCopyable kIp4AddressQuery, // IPv4 Address resolution #endif #if OPENTHREAD_CONFIG_DNS_CLIENT_SERVICE_DISCOVERY_ENABLE - kBrowseQuery, // Browse (service instance enumeration). - kServiceQuery, // Service instance resolution. + kBrowseQuery, // Browse (service instance enumeration). + kServiceQuerySrvTxt, // Service instance resolution both SRV and TXT records. + kServiceQuerySrv, // Service instance resolution SRV record only. + kServiceQueryTxt, // Service instance resolution TXT record only. #endif + kNoQuery, }; #if OPENTHREAD_CONFIG_DNS_CLIENT_OVER_TCP_ENABLE @@ -757,30 +791,45 @@ class Client : public InstanceLocator, private NonCopyable TimeMilli mRetransmissionTime; QueryConfig mConfig; uint8_t mTransmissionCount; + Query *mMainQuery; + Query *mNextQuery; + Message *mSavedResponse; // Followed by the name (service, host, instance) encoded as a `Dns::Name`. }; static constexpr uint16_t kNameOffsetInQuery = sizeof(QueryInfo); - Error StartQuery(QueryInfo &aInfo, - const QueryConfig *aConfig, - const char *aLabel, - const char *aName, - void *aContext); + Error StartQuery(QueryInfo &aInfo, const char *aLabel, const char *aName, QueryType aSecondType = kNoQuery); Error AllocateQuery(const QueryInfo &aInfo, const char *aLabel, const char *aName, Query *&aQuery); void FreeQuery(Query &aQuery); void UpdateQuery(Query &aQuery, const QueryInfo &aInfo) { aQuery.Write(0, aInfo); } + Query &FindMainQuery(Query &aQuery); Error SendQuery(Query &aQuery, QueryInfo &aInfo, bool aUpdateTimer); void FinalizeQuery(Query &aQuery, Error aError); - void FinalizeQuery(Response &Response, QueryType aType, Error aError); - static void GetCallback(const Query &aQuery, Callback &aCallback, void *&aContext); + void FinalizeQuery(Response &Response, Error aError); + static void GetQueryTypeAndCallback(const Query &aQuery, QueryType &aType, Callback &aCallback, void *&aContext); Error AppendNameFromQuery(const Query &aQuery, Message &aMessage); Query *FindQueryById(uint16_t aMessageId); static void HandleUdpReceive(void *aContext, otMessage *aMessage, const otMessageInfo *aMsgInfo); - void ProcessResponse(const Message &aMessage); - Error ParseResponse(Response &aResponse, QueryType &aType, Error &aResponseError); + void ProcessResponse(const Message &aResponseMessage); + Error ParseResponse(const Message &aResponseMessage, Query *&aQuery, Error &aResponseError); + bool CanFinalizeQuery(Query &aQuery); + void SaveQueryResponse(Query &aQuery, const Message &aResponseMessage); + Query *PopulateResponse(Response &aResponse, Query &aQuery, const Message &aResponseMessage); + void PrepareResponseAndFinalize(Query &aQuery, const Message &aResponseMessage, Response *aPrevResponse); void HandleTimer(void); +#if OPENTHREAD_CONFIG_DNS_CLIENT_NAT64_ENABLE + Error ReplaceWithIp4Query(Query &aQuery); +#endif +#if OPENTHREAD_CONFIG_DNS_CLIENT_SERVICE_DISCOVERY_ENABLE + Error ReplaceWithSeparateSrvTxtQueries(Query &aQuery); +#endif + +#if OPENTHREAD_CONFIG_DNS_CLIENT_DEFAULT_SERVER_ADDRESS_AUTO_SET_ENABLE + void UpdateDefaultConfigAddress(void); +#endif + #if OPENTHREAD_CONFIG_DNS_CLIENT_OVER_TCP_ENABLE static void HandleTcpEstablishedCallback(otTcpEndpoint *aEndpoint); static void HandleTcpSendDoneCallback(otTcpEndpoint *aEndpoint, otLinkedBuffer *aData); @@ -805,15 +854,8 @@ class Client : public InstanceLocator, private NonCopyable void PrepareTcpMessage(Message &aMessage); #endif // OPENTHREAD_CONFIG_DNS_CLIENT_OVER_TCP_ENABLE -#if OPENTHREAD_CONFIG_DNS_CLIENT_NAT64_ENABLE - Error CheckAddressResponse(Response &aResponse, Error aResponseError) const; -#endif -#if OPENTHREAD_CONFIG_DNS_CLIENT_DEFAULT_SERVER_ADDRESS_AUTO_SET_ENABLE - void UpdateDefaultConfigAddress(void); -#endif - - static const uint8_t kQuestionCount[]; - static const uint16_t *kQuestionRecordTypes[]; + static const uint8_t kQuestionCount[]; + static const uint16_t *const kQuestionRecordTypes[]; static const uint16_t kIp6AddressQueryRecordTypes[]; #if OPENTHREAD_CONFIG_DNS_CLIENT_NAT64_ENABLE @@ -840,7 +882,7 @@ class Client : public InstanceLocator, private NonCopyable TcpState mTcpState; #endif - QueryList mQueries; + QueryList mMainQueries; RetryTimer mTimer; QueryConfig mDefaultConfig; #if OPENTHREAD_CONFIG_DNS_CLIENT_DEFAULT_SERVER_ADDRESS_AUTO_SET_ENABLE diff --git a/src/core/net/dnssd_server.cpp b/src/core/net/dnssd_server.cpp index f2c41525970..31efd31596e 100644 --- a/src/core/net/dnssd_server.cpp +++ b/src/core/net/dnssd_server.cpp @@ -70,6 +70,7 @@ Server::Server(Instance &aInstance) , mEnableUpstreamQuery(false) #endif , mTimer(aInstance) + , mTestMode(kTestModeDisabled) { mCounters.Clear(); } @@ -203,6 +204,15 @@ void Server::ProcessQuery(const Header &aRequestHeader, Message &aRequestMessage VerifyOrExit(!aRequestHeader.IsTruncationFlagSet(), response = Header::kResponseFormatError); VerifyOrExit(aRequestHeader.GetQuestionCount() > 0, response = Header::kResponseFormatError); + switch (mTestMode) + { + case kTestModeDisabled: + break; + case kTestModeSingleQuestionOnly: + VerifyOrExit(aRequestHeader.GetQuestionCount() == 1, response = Header::kResponseFormatError); + break; + } + response = AddQuestions(aRequestHeader, aRequestMessage, responseHeader, *responseMessage, compressInfo); VerifyOrExit(response == Header::kResponseSuccess); diff --git a/src/core/net/dnssd_server.hpp b/src/core/net/dnssd_server.hpp index cf38ffc4fa8..bebcf41c503 100644 --- a/src/core/net/dnssd_server.hpp +++ b/src/core/net/dnssd_server.hpp @@ -262,6 +262,27 @@ class Server : public InstanceLocator, private NonCopyable */ const Counters &GetCounters(void) const { return mCounters; }; + /** + * This enumeration represents different test modes. + * + * The test mode is intended for testing the client by having server behave in certain ways, e.g., reject messages + * with certain format (e.g., more than one question in query). + * + */ + enum TestMode : uint8_t + { + kTestModeDisabled, ///< Test mode is disabled. + kTestModeSingleQuestionOnly, ///< Allow single question in query message, send `FormatError` for two or more. + }; + + /** + * This method sets the test mode for `Server`. + * + * @param[in] aTestMode The new test mode. + * + */ + void SetTestMode(TestMode aTestMode) { mTestMode = aTestMode; } + private: class NameCompressInfo : public Clearable { @@ -531,8 +552,8 @@ class Server : public InstanceLocator, private NonCopyable #endif ServerTimer mTimer; - - Counters mCounters; + Counters mCounters; + TestMode mTestMode; }; } // namespace ServiceDiscovery diff --git a/tests/scripts/expect/tun-dns-over-tcp-client.exp b/tests/scripts/expect/tun-dns-over-tcp-client.exp index 3e74bfdb81a..3da428a583c 100755 --- a/tests/scripts/expect/tun-dns-over-tcp-client.exp +++ b/tests/scripts/expect/tun-dns-over-tcp-client.exp @@ -39,7 +39,7 @@ switch_node 1 set addr_1 [get_ipaddr mleid] switch_node 2 -send "dns resolve ipv6.google.com $addr_1 2000 6000 4 1 tcp\n" +send "dns resolve ipv6.google.com $addr_1 2000 6000 4 1 def tcp\n" expect "DNS response for ipv6.google.com" expect_line "Done" diff --git a/tests/unit/CMakeLists.txt b/tests/unit/CMakeLists.txt index aeda3cc732f..adc0647a144 100644 --- a/tests/unit/CMakeLists.txt +++ b/tests/unit/CMakeLists.txt @@ -258,6 +258,27 @@ target_link_libraries(ot-test-dns add_test(NAME ot-test-dns COMMAND ot-test-dns) +add_executable(ot-test-dns-client + test_dns_client.cpp +) + +target_include_directories(ot-test-dns-client + PRIVATE + ${COMMON_INCLUDES} +) + +target_compile_options(ot-test-dns-client + PRIVATE + ${COMMON_COMPILE_OPTIONS} +) + +target_link_libraries(ot-test-dns-client + PRIVATE + ${COMMON_LIBS} +) + +add_test(NAME ot-test-dns-client COMMAND ot-test-dns-client) + add_executable(ot-test-dso test_dso.cpp ) diff --git a/tests/unit/test_dns_client.cpp b/tests/unit/test_dns_client.cpp new file mode 100644 index 00000000000..1b27a5904e2 --- /dev/null +++ b/tests/unit/test_dns_client.cpp @@ -0,0 +1,682 @@ +/* + * 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. + */ + +#include + +#include "test_platform.h" +#include "test_util.hpp" + +#include +#include +#include +#include + +#include "common/arg_macros.hpp" +#include "common/array.hpp" +#include "common/instance.hpp" +#include "common/string.hpp" +#include "common/time.hpp" + +#if OPENTHREAD_CONFIG_DNS_CLIENT_ENABLE && OPENTHREAD_CONFIG_DNS_CLIENT_SERVICE_DISCOVERY_ENABLE && \ + OPENTHREAD_CONFIG_DNS_CLIENT_DEFAULT_SERVER_ADDRESS_AUTO_SET_ENABLE && OPENTHREAD_CONFIG_DNSSD_SERVER_ENABLE && \ + OPENTHREAD_CONFIG_SRP_SERVER_ENABLE && OPENTHREAD_CONFIG_SRP_CLIENT_ENABLE && \ + !OPENTHREAD_CONFIG_TIME_SYNC_ENABLE && !OPENTHREAD_PLATFORM_POSIX +#define ENABLE_DNS_TEST 1 +#else +#define ENABLE_DNS_TEST 0 +#endif + +#if ENABLE_DNS_TEST + +using namespace ot; + +// Logs a message and adds current time (sNow) as "::." +#define Log(...) \ + printf("%02u:%02u:%02u.%03u " OT_FIRST_ARG(__VA_ARGS__) "\n", (sNow / 36000000), (sNow / 60000) % 60, \ + (sNow / 1000) % 60, sNow % 1000 OT_REST_ARGS(__VA_ARGS__)) + +static constexpr uint16_t kMaxRaSize = 800; + +static ot::Instance *sInstance; + +static uint32_t sNow = 0; +static uint32_t sAlarmTime; +static bool sAlarmOn = false; + +static otRadioFrame sRadioTxFrame; +static uint8_t sRadioTxFramePsdu[OT_RADIO_FRAME_MAX_SIZE]; +static bool sRadioTxOngoing = false; + +//---------------------------------------------------------------------------------------------------------------------- +// Function prototypes + +void ProcessRadioTxAndTasklets(void); +void AdvanceTime(uint32_t aDuration); + +//---------------------------------------------------------------------------------------------------------------------- +// `otPlatRadio` + +extern "C" { + +otError otPlatRadioTransmit(otInstance *, otRadioFrame *) +{ + sRadioTxOngoing = true; + + return OT_ERROR_NONE; +} + +otRadioFrame *otPlatRadioGetTransmitBuffer(otInstance *) { return &sRadioTxFrame; } + +//---------------------------------------------------------------------------------------------------------------------- +// `otPlatAlaram` + +void otPlatAlarmMilliStop(otInstance *) { sAlarmOn = false; } + +void otPlatAlarmMilliStartAt(otInstance *, uint32_t aT0, uint32_t aDt) +{ + sAlarmOn = true; + sAlarmTime = aT0 + aDt; +} + +uint32_t otPlatAlarmMilliGetNow(void) { return sNow; } + +//---------------------------------------------------------------------------------------------------------------------- + +Array sHeapAllocatedPtrs; + +#if OPENTHREAD_CONFIG_HEAP_EXTERNAL_ENABLE +void *otPlatCAlloc(size_t aNum, size_t aSize) +{ + void *ptr = calloc(aNum, aSize); + + SuccessOrQuit(sHeapAllocatedPtrs.PushBack(ptr)); + + return ptr; +} + +void otPlatFree(void *aPtr) +{ + if (aPtr != nullptr) + { + void **entry = sHeapAllocatedPtrs.Find(aPtr); + + VerifyOrQuit(entry != nullptr, "A heap allocated item is freed twice"); + sHeapAllocatedPtrs.Remove(*entry); + } + + free(aPtr); +} +#endif + +#if OPENTHREAD_CONFIG_LOG_OUTPUT == OPENTHREAD_CONFIG_LOG_OUTPUT_PLATFORM_DEFINED +void otPlatLog(otLogLevel aLogLevel, otLogRegion aLogRegion, const char *aFormat, ...) +{ + OT_UNUSED_VARIABLE(aLogLevel); + OT_UNUSED_VARIABLE(aLogRegion); + + va_list args; + + printf(" "); + va_start(args, aFormat); + vprintf(aFormat, args); + va_end(args); + printf("\n"); +} +#endif + +} // extern "C" + +//--------------------------------------------------------------------------------------------------------------------- + +void ProcessRadioTxAndTasklets(void) +{ + do + { + if (sRadioTxOngoing) + { + sRadioTxOngoing = false; + otPlatRadioTxStarted(sInstance, &sRadioTxFrame); + otPlatRadioTxDone(sInstance, &sRadioTxFrame, nullptr, OT_ERROR_NONE); + } + + otTaskletsProcess(sInstance); + } while (otTaskletsArePending(sInstance)); +} + +void AdvanceTime(uint32_t aDuration) +{ + uint32_t time = sNow + aDuration; + + Log("AdvanceTime for %u.%03u", aDuration / 1000, aDuration % 1000); + + while (TimeMilli(sAlarmTime) <= TimeMilli(time)) + { + ProcessRadioTxAndTasklets(); + sNow = sAlarmTime; + otPlatAlarmMilliFired(sInstance); + } + + ProcessRadioTxAndTasklets(); + sNow = time; +} + +void InitTest(void) +{ + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Initialize OT instance. + + sNow = 0; + sInstance = static_cast(testInitInstance()); + + memset(&sRadioTxFrame, 0, sizeof(sRadioTxFrame)); + sRadioTxFrame.mPsdu = sRadioTxFramePsdu; + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Initialize Border Router and start Thread operation. + + SuccessOrQuit(otLinkSetPanId(sInstance, 0x1234)); + SuccessOrQuit(otIp6SetEnabled(sInstance, true)); + SuccessOrQuit(otThreadSetEnabled(sInstance, true)); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Ensure device starts as leader. + + AdvanceTime(10000); + + VerifyOrQuit(otThreadGetDeviceRole(sInstance) == OT_DEVICE_ROLE_LEADER); +} + +void FinalizeTest(void) +{ + SuccessOrQuit(otIp6SetEnabled(sInstance, false)); + SuccessOrQuit(otThreadSetEnabled(sInstance, false)); + // Make sure there is no message/buffer leak + VerifyOrQuit(sInstance->Get().GetFreeBufferCount() == + sInstance->Get().GetTotalBufferCount()); + SuccessOrQuit(otInstanceErasePersistentInfo(sInstance)); + testFreeInstance(sInstance); +} + +//--------------------------------------------------------------------------------------------------------------------- + +static const char kHostName[] = "elden"; +static const char kHostFullName[] = "elden.default.service.arpa."; + +static const char kService1Name[] = "_srv._udp"; +static const char kService1FullName[] = "_srv._udp.default.service.arpa."; +static const char kInstance1Label[] = "srv-instance"; + +static const char kService2Name[] = "_game._udp"; +static const char kService2FullName[] = "_game._udp.default.service.arpa."; +static const char kInstance2Label[] = "last-ninja"; + +void PrepareService1(Srp::Client::Service &aService) +{ + static const char kSub1[] = "_sub1"; + static const char kSub2[] = "_V1234567"; + static const char kSub3[] = "_XYZWS"; + static const char *kSubLabels[] = {kSub1, kSub2, kSub3, nullptr}; + static const char kTxtKey1[] = "ABCD"; + static const uint8_t kTxtValue1[] = {'a', '0'}; + static const char kTxtKey2[] = "Z0"; + static const uint8_t kTxtValue2[] = {'1', '2', '3'}; + static const char kTxtKey3[] = "D"; + static const uint8_t kTxtValue3[] = {0}; + static const otDnsTxtEntry kTxtEntries[] = { + {kTxtKey1, kTxtValue1, sizeof(kTxtValue1)}, + {kTxtKey2, kTxtValue2, sizeof(kTxtValue2)}, + {kTxtKey3, kTxtValue3, sizeof(kTxtValue3)}, + }; + + memset(&aService, 0, sizeof(aService)); + aService.mName = kService1Name; + aService.mInstanceName = kInstance1Label; + aService.mSubTypeLabels = kSubLabels; + aService.mTxtEntries = kTxtEntries; + aService.mNumTxtEntries = 3; + aService.mPort = 777; + aService.mWeight = 1; + aService.mPriority = 2; +} + +void PrepareService2(Srp::Client::Service &aService) +{ + static const char kSub4[] = "_44444444"; + static const char *kSubLabels2[] = {kSub4, nullptr}; + + memset(&aService, 0, sizeof(aService)); + aService.mName = kService2Name; + aService.mInstanceName = kInstance2Label; + aService.mSubTypeLabels = kSubLabels2; + aService.mTxtEntries = nullptr; + aService.mNumTxtEntries = 0; + aService.mPort = 555; + aService.mWeight = 0; + aService.mPriority = 3; +} + +void ValidateHost(Srp::Server &aServer, const char *aHostName) +{ + // Validate that only a host with `aHostName` is + // registered on SRP server. + + const Srp::Server::Host *host; + const char *name; + + Log("ValidateHost()"); + + host = aServer.GetNextHost(nullptr); + VerifyOrQuit(host != nullptr); + + name = host->GetFullName(); + Log("Hostname: %s", name); + + VerifyOrQuit(StringStartsWith(name, aHostName, kStringCaseInsensitiveMatch)); + VerifyOrQuit(name[strlen(aHostName)] == '.'); + + // Only one host on server + VerifyOrQuit(aServer.GetNextHost(host) == nullptr); +} + +//--------------------------------------------------------------------------------------------------------------------- + +void LogServiceInfo(const Dns::Client::ServiceInfo &aInfo) +{ + Log(" TTL: %u", aInfo.mTtl); + Log(" Port: %u", aInfo.mPort); + Log(" Weight: %u", aInfo.mWeight); + Log(" HostName: %s", aInfo.mHostNameBuffer); + Log(" HostAddr: %s", AsCoreType(&aInfo.mHostAddress).ToString().AsCString()); + Log(" TxtDataLength: %u", aInfo.mTxtDataSize); + Log(" TxtDataTTL: %u", aInfo.mTxtDataTtl); +} + +const char *ServiceModeToString(Dns::Client::QueryConfig::ServiceMode aMode) +{ + static const char *const kServiceModeStrings[] = { + "unspec", // kServiceModeUnspecified (0) + "srv", // kServiceModeSrv (1) + "txt", // kServiceModeTxt (2) + "srv_txt", // kServiceModeSrvTxt (3) + "srv_txt_sep", // kServiceModeSrvTxtSeparate (4) + "srv_txt_opt", // kServiceModeSrvTxtOptimize (5) + }; + + static_assert(Dns::Client::QueryConfig::kServiceModeUnspecified == 0, "Unspecified value is incorrect"); + static_assert(Dns::Client::QueryConfig::kServiceModeSrv == 1, "Srv value is incorrect"); + static_assert(Dns::Client::QueryConfig::kServiceModeTxt == 2, "Txt value is incorrect"); + static_assert(Dns::Client::QueryConfig::kServiceModeSrvTxt == 3, "SrvTxt value is incorrect"); + static_assert(Dns::Client::QueryConfig::kServiceModeSrvTxtSeparate == 4, "SrvTxtSeparate value is incorrect"); + static_assert(Dns::Client::QueryConfig::kServiceModeSrvTxtOptimize == 5, "SrvTxtOptimize value is incorrect"); + + return kServiceModeStrings[aMode]; +} + +//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +struct BrowseInfo +{ + void Reset(void) { mCallbackCount = 0; } + + uint16_t mCallbackCount; + Error mError; + char mServiceName[Dns::Name::kMaxNameSize]; + uint16_t mNumInstances; +}; + +static BrowseInfo sBrowseInfo; + +void BrowseCallback(otError aError, const otDnsBrowseResponse *aResponse, void *aContext) +{ + const Dns::Client::BrowseResponse &response = AsCoreType(aResponse); + + Log("BrowseCallback"); + Log(" Error: %s", ErrorToString(aError)); + + VerifyOrQuit(aContext == sInstance); + + sBrowseInfo.mCallbackCount++; + sBrowseInfo.mError = aError; + + SuccessOrExit(aError); + + SuccessOrQuit(response.GetServiceName(sBrowseInfo.mServiceName, sizeof(sBrowseInfo.mServiceName))); + Log(" ServiceName: %s", sBrowseInfo.mServiceName); + + for (uint16_t index = 0;; index++) + { + char instLabel[Dns::Name::kMaxLabelSize]; + Error error; + + error = response.GetServiceInstance(index, instLabel, sizeof(instLabel)); + + if (error == kErrorNotFound) + { + sBrowseInfo.mNumInstances = index; + break; + } + + SuccessOrQuit(error); + + Log(" %2u) %s", index + 1, instLabel); + } + +exit: + return; +} + +//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +struct ResolveServiceInfo +{ + void Reset(void) + { + memset(this, 0, sizeof(*this)); + mInfo.mHostNameBuffer = mNameBuffer; + mInfo.mHostNameBufferSize = sizeof(mNameBuffer); + mInfo.mTxtData = mTxtBuffer; + mInfo.mTxtDataSize = sizeof(mTxtBuffer); + }; + + uint16_t mCallbackCount; + Error mError; + Dns::Client::ServiceInfo mInfo; + char mNameBuffer[Dns::Name::kMaxNameSize]; + uint8_t mTxtBuffer[256]; +}; + +static ResolveServiceInfo sResolveServiceInfo; + +void ServiceCallback(otError aError, const otDnsServiceResponse *aResponse, void *aContext) +{ + const Dns::Client::ServiceResponse &response = AsCoreType(aResponse); + char instLabel[Dns::Name::kMaxLabelSize]; + char serviceName[Dns::Name::kMaxNameSize]; + + Log("ServiceCallback"); + Log(" Error: %s", ErrorToString(aError)); + + VerifyOrQuit(aContext == sInstance); + + SuccessOrQuit(response.GetServiceName(instLabel, sizeof(instLabel), serviceName, sizeof(serviceName))); + Log(" InstLabel: %s", instLabel); + Log(" ServiceName: %s", serviceName); + + sResolveServiceInfo.mCallbackCount++; + sResolveServiceInfo.mError = aError; + + SuccessOrExit(aError); + SuccessOrQuit(response.GetServiceInfo(sResolveServiceInfo.mInfo)); + LogServiceInfo(sResolveServiceInfo.mInfo); + +exit: + return; +} + +//---------------------------------------------------------------------------------------------------------------------- + +void TestDnsClient(void) +{ + const Dns::Client::QueryConfig::ServiceMode kServiceModes[] = { + Dns::Client::QueryConfig::kServiceModeSrv, + Dns::Client::QueryConfig::kServiceModeTxt, + Dns::Client::QueryConfig::kServiceModeSrvTxt, + Dns::Client::QueryConfig::kServiceModeSrvTxtSeparate, + Dns::Client::QueryConfig::kServiceModeSrvTxtOptimize, + }; + + Srp::Server *srpServer; + Srp::Client *srpClient; + Srp::Client::Service service1; + Srp::Client::Service service2; + Dns::Client *dnsClient; + Dns::Client::QueryConfig queryConfig; + Dns::ServiceDiscovery::Server *dnsServer; + uint16_t heapAllocations; + + Log("--------------------------------------------------------------------------------------------"); + Log("TestDnsClient"); + + InitTest(); + + srpServer = &sInstance->Get(); + srpClient = &sInstance->Get(); + dnsClient = &sInstance->Get(); + dnsServer = &sInstance->Get(); + + heapAllocations = sHeapAllocatedPtrs.GetLength(); + + PrepareService1(service1); + PrepareService2(service2); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Start SRP server. + + SuccessOrQuit(srpServer->SetAddressMode(Srp::Server::kAddressModeUnicast)); + VerifyOrQuit(srpServer->GetState() == Srp::Server::kStateDisabled); + + srpServer->SetEnabled(true); + VerifyOrQuit(srpServer->GetState() != Srp::Server::kStateDisabled); + + AdvanceTime(10000); + VerifyOrQuit(srpServer->GetState() == Srp::Server::kStateRunning); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Start SRP client. + + srpClient->EnableAutoStartMode(nullptr, nullptr); + VerifyOrQuit(srpClient->IsAutoStartModeEnabled()); + + AdvanceTime(2000); + VerifyOrQuit(srpClient->IsRunning()); + + SuccessOrQuit(srpClient->SetHostName(kHostName)); + SuccessOrQuit(srpClient->EnableAutoHostAddress()); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Register two services on SRP. + + SuccessOrQuit(srpClient->AddService(service1)); + SuccessOrQuit(srpClient->AddService(service2)); + + AdvanceTime(2 * 1000); + + VerifyOrQuit(service1.GetState() == Srp::Client::kRegistered); + VerifyOrQuit(service2.GetState() == Srp::Client::kRegistered); + ValidateHost(*srpServer, kHostName); + + Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - "); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Check DNS Client's default config + + VerifyOrQuit(dnsClient->GetDefaultConfig().GetServiceMode() == + Dns::Client::QueryConfig::kServiceModeSrvTxtOptimize); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Validate DNS Client `Browse()` + + sBrowseInfo.Reset(); + Log("Browse(%s)", kService1FullName); + SuccessOrQuit(dnsClient->Browse(kService1FullName, BrowseCallback, sInstance)); + AdvanceTime(100); + VerifyOrQuit(sBrowseInfo.mCallbackCount == 1); + SuccessOrQuit(sBrowseInfo.mError); + VerifyOrQuit(sBrowseInfo.mNumInstances == 1); + + sBrowseInfo.Reset(); + + Log("Browse(%s)", kService2FullName); + SuccessOrQuit(dnsClient->Browse(kService2FullName, BrowseCallback, sInstance)); + AdvanceTime(100); + VerifyOrQuit(sBrowseInfo.mCallbackCount == 1); + SuccessOrQuit(sBrowseInfo.mError); + VerifyOrQuit(sBrowseInfo.mNumInstances == 1); + + sBrowseInfo.Reset(); + Log("Browse() for unknwon service"); + SuccessOrQuit(dnsClient->Browse("_unknown._udp.default.service.arpa.", BrowseCallback, sInstance)); + AdvanceTime(100); + VerifyOrQuit(sBrowseInfo.mCallbackCount == 1); + VerifyOrQuit(sBrowseInfo.mError == kErrorNotFound); + + Log("Issue four parallel `Browse()` at the same time"); + sBrowseInfo.Reset(); + SuccessOrQuit(dnsClient->Browse(kService1FullName, BrowseCallback, sInstance)); + SuccessOrQuit(dnsClient->Browse(kService2FullName, BrowseCallback, sInstance)); + SuccessOrQuit(dnsClient->Browse("_unknown._udp.default.service.arpa.", BrowseCallback, sInstance)); + SuccessOrQuit(dnsClient->Browse("_unknown2._udp.default.service.arpa.", BrowseCallback, sInstance)); + AdvanceTime(100); + VerifyOrQuit(sBrowseInfo.mCallbackCount == 4); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Validate DNS Client `ResolveService()` using all service modes + + for (Dns::Client::QueryConfig::ServiceMode mode : kServiceModes) + { + Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - "); + Log("ResolveService(%s,%s) with ServiceMode: %s", kInstance1Label, kService1FullName, + ServiceModeToString(mode)); + + queryConfig.Clear(); + queryConfig.mServiceMode = static_cast(mode); + + sResolveServiceInfo.Reset(); + SuccessOrQuit( + dnsClient->ResolveService(kInstance1Label, kService1FullName, ServiceCallback, sInstance, &queryConfig)); + AdvanceTime(100); + + VerifyOrQuit(sResolveServiceInfo.mCallbackCount == 1); + SuccessOrQuit(sResolveServiceInfo.mError); + + if (mode != Dns::Client::QueryConfig::kServiceModeTxt) + { + VerifyOrQuit(sResolveServiceInfo.mInfo.mTtl != 0); + VerifyOrQuit(sResolveServiceInfo.mInfo.mPort == service1.mPort); + VerifyOrQuit(sResolveServiceInfo.mInfo.mWeight == service1.mWeight); + VerifyOrQuit(strcmp(sResolveServiceInfo.mInfo.mHostNameBuffer, kHostFullName) == 0); + } + + if (mode != Dns::Client::QueryConfig::kServiceModeSrv) + { + VerifyOrQuit(sResolveServiceInfo.mInfo.mTxtDataTtl != 0); + VerifyOrQuit(sResolveServiceInfo.mInfo.mTxtDataSize != 0); + } + } + + Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - "); + + Log("Set TestMode on server to only accept single question"); + dnsServer->SetTestMode(Dns::ServiceDiscovery::Server::kTestModeSingleQuestionOnly); + + Log("ResolveService(%s,%s) with ServiceMode %s", kInstance1Label, kService1FullName, + ServiceModeToString(Dns::Client::QueryConfig::kServiceModeSrvTxtOptimize)); + + queryConfig.Clear(); + queryConfig.mServiceMode = static_cast(Dns::Client::QueryConfig::kServiceModeSrvTxtOptimize); + + sResolveServiceInfo.Reset(); + SuccessOrQuit( + dnsClient->ResolveService(kInstance1Label, kService1FullName, ServiceCallback, sInstance, &queryConfig)); + AdvanceTime(200); + + VerifyOrQuit(sResolveServiceInfo.mCallbackCount == 1); + SuccessOrQuit(sResolveServiceInfo.mError); + + // Use `kServiceModeSrvTxt` and check that server does reject two questions. + + Log("ResolveService(%s,%s) with ServiceMode %s", kInstance1Label, kService1FullName, + ServiceModeToString(Dns::Client::QueryConfig::kServiceModeSrvTxt)); + + queryConfig.Clear(); + queryConfig.mServiceMode = static_cast(Dns::Client::QueryConfig::kServiceModeSrvTxt); + + sResolveServiceInfo.Reset(); + SuccessOrQuit( + dnsClient->ResolveService(kInstance1Label, kService1FullName, ServiceCallback, sInstance, &queryConfig)); + AdvanceTime(200); + + VerifyOrQuit(sResolveServiceInfo.mCallbackCount == 1); + VerifyOrQuit(sResolveServiceInfo.mError != kErrorNone); + + Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - "); + + Log("Stop DNS-SD server"); + dnsServer->Stop(); + + Log("ResolveService(%s,%s) with ServiceMode %s", kInstance1Label, kService1FullName, + ServiceModeToString(Dns::Client::QueryConfig::kServiceModeSrv)); + + queryConfig.Clear(); + queryConfig.mServiceMode = static_cast(Dns::Client::QueryConfig::kServiceModeSrv); + + sResolveServiceInfo.Reset(); + SuccessOrQuit( + dnsClient->ResolveService(kInstance1Label, kService1FullName, ServiceCallback, sInstance, &queryConfig)); + AdvanceTime(25 * 1000); + + VerifyOrQuit(sResolveServiceInfo.mCallbackCount == 1); + VerifyOrQuit(sResolveServiceInfo.mError == kErrorResponseTimeout); + + Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - "); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Disable SRP server, verify that all heap allocations by SRP server + // and/or by DNS Client are freed. + + Log("Disabling SRP server"); + + srpServer->SetEnabled(false); + AdvanceTime(100); + + VerifyOrQuit(heapAllocations == sHeapAllocatedPtrs.GetLength()); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Finalize OT instance and validate all heap allocations are freed. + + Log("Finalizing OT instance"); + FinalizeTest(); + + VerifyOrQuit(sHeapAllocatedPtrs.IsEmpty()); + + Log("End of TestDnsClient"); +} + +#endif // ENABLE_DNS_TEST + +int main(void) +{ +#if ENABLE_DNS_TEST + TestDnsClient(); + printf("All tests passed\n"); +#else + printf("DNS_CLIENT or DSNSSD_SERVER feature is not enabled\n"); +#endif + + return 0; +} From cbdabbc542d670e55db84f3bf334917439c5d07f Mon Sep 17 00:00:00 2001 From: Zhanglong Xia Date: Thu, 6 Apr 2023 06:54:18 +0800 Subject: [PATCH 03/10] [spinel] add openthread config header file (#8928) This commit adds the openthread config header file to the spinel config file to allow developers to configure the spinel configurations in the openthread config file. --- src/lib/spinel/openthread-spinel-config.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/lib/spinel/openthread-spinel-config.h b/src/lib/spinel/openthread-spinel-config.h index 58ff993bab5..d8dec50ba92 100644 --- a/src/lib/spinel/openthread-spinel-config.h +++ b/src/lib/spinel/openthread-spinel-config.h @@ -34,6 +34,8 @@ #ifndef OPENTHREAD_SPINEL_CONFIG_H_ #define OPENTHREAD_SPINEL_CONFIG_H_ +#include "openthread-core-config.h" + /** * @def OPENTHREAD_SPINEL_CONFIG_OPENTHREAD_MESSAGE_ENABLE * From adde085fcec105c76843851999ce011c4b352af5 Mon Sep 17 00:00:00 2001 From: Abtin Keshavarzian Date: Wed, 5 Apr 2023 18:00:30 -0700 Subject: [PATCH 04/10] [tmf] add `UriToString()` for logging (#8930) This commit adds a helper function `UriToString()` to convert a given `Uri` enumeration value to a human-readable string. This is used for logging. --- src/core/meshcop/announce_begin_client.cpp | 2 +- src/core/meshcop/commissioner.cpp | 28 +++++------ src/core/meshcop/energy_scan_client.cpp | 6 +-- src/core/meshcop/joiner.cpp | 8 ++-- src/core/meshcop/joiner_router.cpp | 9 ++-- src/core/meshcop/meshcop_leader.cpp | 10 ++-- src/core/meshcop/panid_query_client.cpp | 6 +-- src/core/thread/address_resolver.cpp | 31 +++++++------ src/core/thread/announce_begin_server.cpp | 2 +- src/core/thread/dua_manager.cpp | 15 +++--- src/core/thread/energy_scan_server.cpp | 4 +- src/core/thread/network_data_leader_ftd.cpp | 8 ++-- src/core/thread/network_data_notifier.cpp | 2 +- src/core/thread/network_diagnostic.cpp | 8 ++-- src/core/thread/panid_query_server.cpp | 4 +- src/core/thread/uri_paths.cpp | 40 ++++++++++++++++ src/core/thread/uri_paths.hpp | 51 +++++++++++++++++++++ 17 files changed, 163 insertions(+), 71 deletions(-) diff --git a/src/core/meshcop/announce_begin_client.cpp b/src/core/meshcop/announce_begin_client.cpp index 47e7fe71824..f0cde456fda 100644 --- a/src/core/meshcop/announce_begin_client.cpp +++ b/src/core/meshcop/announce_begin_client.cpp @@ -85,7 +85,7 @@ Error AnnounceBeginClient::SendRequest(uint32_t aChannelMask, SuccessOrExit(error = Get().SendMessage(*message, messageInfo)); - LogInfo("sent announce begin query"); + LogInfo("Sent %s", UriToString()); exit: FreeMessageOnError(message, error); diff --git a/src/core/meshcop/commissioner.cpp b/src/core/meshcop/commissioner.cpp index 5bd024bf970..0bac4320f2a 100644 --- a/src/core/meshcop/commissioner.cpp +++ b/src/core/meshcop/commissioner.cpp @@ -695,7 +695,7 @@ Error Commissioner::SendMgmtCommissionerGetRequest(const uint8_t *aTlvs, uint8_t SuccessOrExit(error = Get().SendMessage(*message, messageInfo, Commissioner::HandleMgmtCommissionerGetResponse, this)); - LogInfo("sent MGMT_COMMISSIONER_GET.req to leader"); + LogInfo("Sent %s to leader", UriToString()); exit: FreeMessageOnError(message, error); @@ -718,7 +718,7 @@ void Commissioner::HandleMgmtCommissionerGetResponse(Coap::Message *aMe OT_UNUSED_VARIABLE(aMessageInfo); VerifyOrExit(aResult == kErrorNone && aMessage->GetCode() == Coap::kCodeChanged); - LogInfo("received MGMT_COMMISSIONER_GET response"); + LogInfo("Received %s response", UriToString()); exit: return; @@ -764,7 +764,7 @@ Error Commissioner::SendMgmtCommissionerSetRequest(const Dataset &aDataset, cons SuccessOrExit(error = Get().SendMessage(*message, messageInfo, Commissioner::HandleMgmtCommissionerSetResponse, this)); - LogInfo("sent MGMT_COMMISSIONER_SET.req to leader"); + LogInfo("Sent %s to leader", UriToString()); exit: FreeMessageOnError(message, error); @@ -787,7 +787,7 @@ void Commissioner::HandleMgmtCommissionerSetResponse(Coap::Message *aMe OT_UNUSED_VARIABLE(aMessageInfo); VerifyOrExit(aResult == kErrorNone && aMessage->GetCode() == Coap::kCodeChanged); - LogInfo("received MGMT_COMMISSIONER_SET response"); + LogInfo("Received %s response", UriToString()); exit: return; @@ -813,7 +813,7 @@ Error Commissioner::SendPetition(void) SuccessOrExit( error = Get().SendMessage(*message, messageInfo, Commissioner::HandleLeaderPetitionResponse, this)); - LogInfo("sent petition"); + LogInfo("Sent %s", UriToString()); exit: FreeMessageOnError(message, error); @@ -842,7 +842,7 @@ void Commissioner::HandleLeaderPetitionResponse(Coap::Message *aMessage VerifyOrExit(aResult == kErrorNone && aMessage->GetCode() == Coap::kCodeChanged, retransmit = (mState == kStatePetition)); - LogInfo("received Leader Petition response"); + LogInfo("Received %s response", UriToString()); SuccessOrExit(Tlv::Find(*aMessage, state)); VerifyOrExit(state == StateTlv::kAccept, IgnoreError(Stop(kDoNotSendKeepAlive))); @@ -900,7 +900,7 @@ void Commissioner::SendKeepAlive(uint16_t aSessionId) SuccessOrExit(error = Get().SendMessage(*message, messageInfo, Commissioner::HandleLeaderKeepAliveResponse, this)); - LogInfo("sent keep alive"); + LogInfo("Sent %s", UriToString()); exit: FreeMessageOnError(message, error); @@ -928,7 +928,7 @@ void Commissioner::HandleLeaderKeepAliveResponse(Coap::Message *aMessag VerifyOrExit(aResult == kErrorNone && aMessage->GetCode() == Coap::kCodeChanged, IgnoreError(Stop(kDoNotSendKeepAlive))); - LogInfo("received Leader keep-alive response"); + LogInfo("Received %s response", UriToString()); SuccessOrExit(Tlv::Find(*aMessage, state)); VerifyOrExit(state == StateTlv::kAccept, IgnoreError(Stop(kDoNotSendKeepAlive))); @@ -985,7 +985,7 @@ template <> void Commissioner::HandleTmf(Coap::Message &aMessage, c { if (mJoinerIid != joinerIid) { - LogNote("Ignore Relay Receive (%s, 0x%04x), session in progress with (%s, 0x%04x)", + LogNote("Ignore %s (%s, 0x%04x), session in progress with (%s, 0x%04x)", UriToString(), joinerIid.ToString().AsCString(), joinerRloc, mJoinerIid.ToString().AsCString(), mJoinerRloc); ExitNow(); @@ -995,7 +995,7 @@ template <> void Commissioner::HandleTmf(Coap::Message &aMessage, c mJoinerPort = joinerPort; mJoinerRloc = joinerRloc; - LogInfo("Received Relay Receive (%s, 0x%04x)", mJoinerIid.ToString().AsCString(), mJoinerRloc); + LogInfo("Received %s (%s, 0x%04x)", UriToString(), mJoinerIid.ToString().AsCString(), mJoinerRloc); aMessage.SetOffset(offset); SuccessOrExit(error = aMessage.SetLength(offset + length)); @@ -1026,11 +1026,11 @@ void Commissioner::HandleTmf(Coap::Message &aMessage, const VerifyOrExit(mState == kStateActive); VerifyOrExit(aMessage.IsConfirmablePostRequest()); - LogInfo("received dataset changed"); + LogInfo("Received %s", UriToString()); SuccessOrExit(Get().SendEmptyAck(aMessage, aMessageInfo)); - LogInfo("sent dataset changed acknowledgment"); + LogInfo("Sent %s ack", UriToString()); exit: return; @@ -1046,7 +1046,7 @@ void Commissioner::HandleTmf(Coap::Message &aMessage, const VerifyOrExit(mState == kStateActive); - LogInfo("received joiner finalize"); + LogInfo("Received %s", UriToString()); switch (Tlv::Find(aMessage, provisioningUrl)) { @@ -1116,7 +1116,7 @@ void Commissioner::SendJoinFinalizeResponse(const Coap::Message &aRequest, State RemoveJoiner(*mActiveJoiner, kRemoveJoinerDelay); } - LogInfo("sent joiner finalize response"); + LogInfo("Sent %s response", UriToString()); exit: FreeMessageOnError(message, error); diff --git a/src/core/meshcop/energy_scan_client.cpp b/src/core/meshcop/energy_scan_client.cpp index 69ca8ed1f20..905d8a66f28 100644 --- a/src/core/meshcop/energy_scan_client.cpp +++ b/src/core/meshcop/energy_scan_client.cpp @@ -91,7 +91,7 @@ Error EnergyScanClient::SendQuery(uint32_t aChannelMas messageInfo.SetSockAddrToRlocPeerAddrTo(aAddress); SuccessOrExit(error = Get().SendMessage(*message, messageInfo)); - LogInfo("sent query"); + LogInfo("Sent %s", UriToString()); mCallback.Set(aCallback, aContext); @@ -108,7 +108,7 @@ void EnergyScanClient::HandleTmf(Coap::Message &aMessage, cons VerifyOrExit(aMessage.IsConfirmablePostRequest()); - LogInfo("received report"); + LogInfo("Received %s", UriToString()); VerifyOrExit((mask = MeshCoP::ChannelMaskTlv::GetChannelMask(aMessage)) != 0); @@ -118,7 +118,7 @@ void EnergyScanClient::HandleTmf(Coap::Message &aMessage, cons SuccessOrExit(Get().SendEmptyAck(aMessage, aMessageInfo)); - LogInfo("sent report response"); + LogInfo("Sent %s ack", UriToString()); exit: return; diff --git a/src/core/meshcop/joiner.cpp b/src/core/meshcop/joiner.cpp index 21adc7dd160..9894b9385f2 100644 --- a/src/core/meshcop/joiner.cpp +++ b/src/core/meshcop/joiner.cpp @@ -486,7 +486,7 @@ void Joiner::SendJoinerFinalize(void) SuccessOrExit(Get().SendMessage(*mFinalizeMessage, Joiner::HandleJoinerFinalizeResponse, this)); mFinalizeMessage = nullptr; - LogInfo("Joiner sent finalize"); + LogInfo("Sent %s", UriToString()); exit: return; @@ -517,7 +517,7 @@ void Joiner::HandleJoinerFinalizeResponse(Coap::Message *aMessage, const Ip6::Me SetState(kStateEntrust); mTimer.Start(kReponseTimeout); - LogInfo("Joiner received finalize response %d", state); + LogInfo("Received %s %d", UriToString(), state); #if OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE LogCertMessage("[THCI] direction=recv | type=JOIN_FIN.rsp |", *aMessage); @@ -535,7 +535,7 @@ template <> void Joiner::HandleTmf(Coap::Message &aMessage, c VerifyOrExit(mState == kStateEntrust && aMessage.IsConfirmablePostRequest(), error = kErrorDrop); - LogInfo("Joiner received entrust"); + LogInfo("Received %s", UriToString()); LogCert("[THCI] direction=recv | type=JOIN_ENT.ntf"); datasetInfo.Clear(); @@ -574,7 +574,7 @@ void Joiner::SendJoinerEntrustResponse(const Coap::Message &aRequest, const Ip6: SetState(kStateJoined); - LogInfo("Joiner sent entrust response"); + LogInfo("Sent %s response", UriToString()); LogCert("[THCI] direction=send | type=JOIN_ENT.rsp"); exit: diff --git a/src/core/meshcop/joiner_router.cpp b/src/core/meshcop/joiner_router.cpp index 04741e0ffb6..09616023c39 100644 --- a/src/core/meshcop/joiner_router.cpp +++ b/src/core/meshcop/joiner_router.cpp @@ -155,7 +155,7 @@ void JoinerRouter::HandleUdpReceive(Message &aMessage, const Ip6::MessageInfo &a SuccessOrExit(error = Get().SendMessage(*message, messageInfo)); - LogInfo("Sent relay rx"); + LogInfo("Sent %s", UriToString()); exit: FreeMessageOnError(message, error); @@ -177,7 +177,7 @@ template <> void JoinerRouter::HandleTmf(Coap::Message &aMessage, c VerifyOrExit(aMessage.IsNonConfirmablePostRequest(), error = kErrorDrop); - LogInfo("Received relay transmit"); + LogInfo("Received %s", UriToString()); SuccessOrExit(error = Tlv::Find(aMessage, joinerPort)); SuccessOrExit(error = Tlv::Find(aMessage, joinerIid)); @@ -273,11 +273,10 @@ Error JoinerRouter::SendJoinerEntrust(const Ip6::MessageInfo &aMessageInfo) IgnoreError(Get().AbortTransaction(&JoinerRouter::HandleJoinerEntrustResponse, this)); - LogInfo("Sending JOIN_ENT.ntf"); SuccessOrExit(error = Get().SendMessage(*message, aMessageInfo, &JoinerRouter::HandleJoinerEntrustResponse, this)); - LogInfo("Sent joiner entrust length = %d", message->GetLength()); + LogInfo("Sent %s (len= %d)", UriToString(), message->GetLength()); LogCert("[THCI] direction=send | type=JOIN_ENT.ntf"); exit: @@ -382,7 +381,7 @@ void JoinerRouter::HandleJoinerEntrustResponse(Coap::Message *aMessage, VerifyOrExit(aMessage->GetCode() == Coap::kCodeChanged); - LogInfo("Receive joiner entrust response"); + LogInfo("Receive %s response", UriToString()); LogCert("[THCI] direction=recv | type=JOIN_ENT.rsp"); exit: diff --git a/src/core/meshcop/meshcop_leader.cpp b/src/core/meshcop/meshcop_leader.cpp index 8c2417983a8..5f97185d4fa 100644 --- a/src/core/meshcop/meshcop_leader.cpp +++ b/src/core/meshcop/meshcop_leader.cpp @@ -71,7 +71,7 @@ template <> void Leader::HandleTmf(Coap::Message &aMessage, CommissionerIdTlv commissionerId; StateTlv::State state = StateTlv::kReject; - LogInfo("received petition"); + LogInfo("Received %s", UriToString()); VerifyOrExit(Get().IsRoutingLocator(aMessageInfo.GetPeerAddr())); SuccessOrExit(Tlv::FindTlv(aMessage, commissionerId)); @@ -136,7 +136,7 @@ void Leader::SendPetitionResponse(const Coap::Message &aRequest, SuccessOrExit(error = Get().SendMessage(*message, aMessageInfo)); - LogInfo("sent petition response"); + LogInfo("Sent %s response", UriToString()); exit: FreeMessageOnError(message, error); @@ -150,7 +150,7 @@ template <> void Leader::HandleTmf(Coap::Message &aMessage, BorderAgentLocatorTlv *borderAgentLocator; StateTlv::State responseState; - LogInfo("received keep alive"); + LogInfo("Received %s", UriToString()); SuccessOrExit(Tlv::Find(aMessage, state)); @@ -202,7 +202,7 @@ void Leader::SendKeepAliveResponse(const Coap::Message &aRequest, SuccessOrExit(error = Get().SendMessage(*message, aMessageInfo)); - LogInfo("sent keep alive response"); + LogInfo("Sent %s response", UriToString()); exit: FreeMessageOnError(message, error); @@ -221,7 +221,7 @@ void Leader::SendDatasetChanged(const Ip6::Address &aAddress) messageInfo.SetSockAddrToRlocPeerAddrTo(aAddress); SuccessOrExit(error = Get().SendMessage(*message, messageInfo)); - LogInfo("sent dataset changed"); + LogInfo("Sent %s", UriToString()); exit: FreeMessageOnError(message, error); diff --git a/src/core/meshcop/panid_query_client.cpp b/src/core/meshcop/panid_query_client.cpp index 4f9d9371cc0..8c6a004ef44 100644 --- a/src/core/meshcop/panid_query_client.cpp +++ b/src/core/meshcop/panid_query_client.cpp @@ -85,7 +85,7 @@ Error PanIdQueryClient::SendQuery(uint16_t aPanId, messageInfo.SetSockAddrToRlocPeerAddrTo(aAddress); SuccessOrExit(error = Get().SendMessage(*message, messageInfo)); - LogInfo("sent panid query"); + LogInfo("Sent %s", UriToString()); mCallback.Set(aCallback, aContext); @@ -102,7 +102,7 @@ void PanIdQueryClient::HandleTmf(Coap::Message &aMessage, con VerifyOrExit(aMessage.IsConfirmablePostRequest()); - LogInfo("received panid conflict"); + LogInfo("Received %s", UriToString()); SuccessOrExit(Tlv::Find(aMessage, panId)); @@ -112,7 +112,7 @@ void PanIdQueryClient::HandleTmf(Coap::Message &aMessage, con SuccessOrExit(Get().SendEmptyAck(aMessage, aMessageInfo)); - LogInfo("sent panid query conflict response"); + LogInfo("Sent %s response", UriToString()); exit: return; diff --git a/src/core/thread/address_resolver.cpp b/src/core/thread/address_resolver.cpp index 760b42e936c..1878eee97dc 100644 --- a/src/core/thread/address_resolver.cpp +++ b/src/core/thread/address_resolver.cpp @@ -600,7 +600,7 @@ Error AddressResolver::SendAddressQuery(const Ip6::Address &aEid) SuccessOrExit(error = Get().SendMessage(*message, messageInfo)); - LogInfo("Sending address query for %s", aEid.ToString().AsCString()); + LogInfo("Sent %s for %s", UriToString(), aEid.ToString().AsCString()); exit: @@ -612,8 +612,8 @@ Error AddressResolver::SendAddressQuery(const Ip6::Address &aEid) { uint16_t selfRloc16 = Get().GetRloc16(); - LogInfo("Extending ADDR.qry to BB.qry for target=%s, rloc16=%04x(self)", aEid.ToString().AsCString(), - selfRloc16); + LogInfo("Extending %s to %s for target %s, rloc16=%04x(self)", UriToString(), + UriToString(), aEid.ToString().AsCString(), selfRloc16); IgnoreError(Get().SendBackboneQuery(aEid, selfRloc16)); } #endif @@ -649,7 +649,7 @@ void AddressResolver::HandleTmf(Coap::Message &aMessage, cons ExitNow(); } - LogInfo("Received address notification from 0x%04x for %s to 0x%04x", + LogInfo("Received %s from 0x%04x for %s to 0x%04x", UriToString(), aMessageInfo.GetPeerAddr().GetIid().GetLocator(), target.ToString().AsCString(), rloc16); entry = FindCacheEntry(target, list, prev); @@ -681,7 +681,7 @@ void AddressResolver::HandleTmf(Coap::Message &aMessage, cons if (Get().SendEmptyAck(aMessage, aMessageInfo) == kErrorNone) { - LogInfo("Sending address notification acknowledgment"); + LogInfo("Sent %s ack", UriToString()); } Get().HandleResolved(target, kErrorNone); @@ -718,14 +718,14 @@ void AddressResolver::SendAddressError(const Ip6::Address &aTarget, SuccessOrExit(error = Get().SendMessage(*message, messageInfo)); - LogInfo("Sending address error for target %s", aTarget.ToString().AsCString()); + LogInfo("Sent %s for target %s", UriToString(), aTarget.ToString().AsCString()); exit: if (error != kErrorNone) { FreeMessage(message); - LogInfo("Failed to send address error: %s", ErrorToString(error)); + LogInfo("Failed to send %s: %s", UriToString(), ErrorToString(error)); } } @@ -744,13 +744,13 @@ void AddressResolver::HandleTmf(Coap::Message &aMessage, const VerifyOrExit(aMessage.IsPostRequest(), error = kErrorDrop); - LogInfo("Received address error notification"); + LogInfo("Received %s", UriToString()); if (aMessage.IsConfirmable() && !aMessageInfo.GetSockAddr().IsMulticast()) { if (Get().SendEmptyAck(aMessage, aMessageInfo) == kErrorNone) { - LogInfo("Sent address error notification acknowledgment"); + LogInfo("Sent %s ack", UriToString()); } } @@ -807,7 +807,7 @@ void AddressResolver::HandleTmf(Coap::Message &aMessage, const if (error != kErrorNone) { - LogWarn("Error while processing address error notification: %s", ErrorToString(error)); + LogWarn("Error %s when processing %s", ErrorToString(error), UriToString()); } } @@ -823,8 +823,8 @@ void AddressResolver::HandleTmf(Coap::Message &aMessage, const SuccessOrExit(Tlv::Find(aMessage, target)); - LogInfo("Received address query from 0x%04x for target %s", aMessageInfo.GetPeerAddr().GetIid().GetLocator(), - target.ToString().AsCString()); + LogInfo("Received %s from 0x%04x for target %s", UriToString(), + aMessageInfo.GetPeerAddr().GetIid().GetLocator(), target.ToString().AsCString()); if (Get().HasUnicastAddress(target)) { @@ -853,7 +853,8 @@ void AddressResolver::HandleTmf(Coap::Message &aMessage, const { uint16_t srcRloc16 = aMessageInfo.GetPeerAddr().GetIid().GetLocator(); - LogInfo("Extending ADDR.qry to BB.qry for target=%s, rloc16=%04x", target.ToString().AsCString(), srcRloc16); + LogInfo("Extending %s to %s for target %s rloc16=%04x", UriToString(), + UriToString(), target.ToString().AsCString(), srcRloc16); IgnoreError(Get().SendBackboneQuery(target, srcRloc16)); } #endif @@ -887,7 +888,7 @@ void AddressResolver::SendAddressQueryResponse(const Ip6::Address &a SuccessOrExit(error = Get().SendMessage(*message, messageInfo)); - LogInfo("Sending address notification for target %s", aTarget.ToString().AsCString()); + LogInfo("Sent %s for target %s", UriToString(), aTarget.ToString().AsCString()); exit: FreeMessageOnError(message, error); @@ -955,7 +956,7 @@ void AddressResolver::HandleTimeTick(void) mQueryList.PopAfter(prev); mQueryRetryList.Push(*entry); - LogInfo("Timed out waiting for address notification for %s, retry: %d", + LogInfo("Timed out waiting for %s for %s, retry: %d", UriToString(), entry->GetTarget().ToString().AsCString(), entry->GetTimeout()); Get().HandleResolved(entry->GetTarget(), kErrorDrop); diff --git a/src/core/thread/announce_begin_server.cpp b/src/core/thread/announce_begin_server.cpp index 80b74473fb9..1c904e358a9 100644 --- a/src/core/thread/announce_begin_server.cpp +++ b/src/core/thread/announce_begin_server.cpp @@ -81,7 +81,7 @@ void AnnounceBeginServer::HandleTmf(Coap::Message &aMessage, if (aMessage.IsConfirmable() && !aMessageInfo.GetSockAddr().IsMulticast()) { SuccessOrExit(Get().SendEmptyAck(aMessage, aMessageInfo)); - LogInfo("Sent announce begin response"); + LogInfo("Sent %s response", UriToString()); } exit: diff --git a/src/core/thread/dua_manager.cpp b/src/core/thread/dua_manager.cpp index d8866663f5e..4a0d25f1f3e 100644 --- a/src/core/thread/dua_manager.cpp +++ b/src/core/thread/dua_manager.cpp @@ -541,7 +541,7 @@ void DuaManager::PerformNextRegistration(void) Get().SendFastPolls(); } - LogInfo("Sent DUA.req for DUA %s", dua.ToString().AsCString()); + LogInfo("Sent %s for DUA %s", UriToString(), dua.ToString().AsCString()); exit: if (error == kErrorNoBufs) @@ -592,7 +592,7 @@ void DuaManager::HandleDuaResponse(Coap::Message *aMessage, const Ip6::MessageIn mRegistrationTask.Post(); } - LogInfo("Received DUA.rsp: %s", ErrorToString(error)); + LogInfo("Received %s response: %s", UriToString(), ErrorToString(error)); } template <> @@ -606,14 +606,14 @@ void DuaManager::HandleTmf(Coap::Message &aMessage, c if (aMessage.IsConfirmable() && Get().SendEmptyAck(aMessage, aMessageInfo) == kErrorNone) { - LogInfo("Sent DUA.ntf acknowledgment"); + LogInfo("Sent %s ack", UriToString()); } error = ProcessDuaResponse(aMessage); exit: OT_UNUSED_VARIABLE(error); - LogInfo("Received DUA.ntf: %s", ErrorToString(error)); + LogInfo("Received %s: %s", UriToString(), ErrorToString(error)); } Error DuaManager::ProcessDuaResponse(Coap::Message &aMessage) @@ -743,7 +743,8 @@ void DuaManager::SendAddressNotification(Ip6::Address &aAddress, SuccessOrExit(error = Get().SendMessage(*message, messageInfo)); - LogInfo("Sent ADDR_NTF for child %04x DUA %s", aChild.GetRloc16(), aAddress.ToString().AsCString()); + LogInfo("Sent %s for child %04x DUA %s", UriToString(), aChild.GetRloc16(), + aAddress.ToString().AsCString()); exit: @@ -752,8 +753,8 @@ void DuaManager::SendAddressNotification(Ip6::Address &aAddress, FreeMessage(message); // TODO: (DUA) (P4) may enhance to guarantee the delivery of DUA.ntf - LogWarn("Sent ADDR_NTF for child %04x DUA %s Error %s", aChild.GetRloc16(), aAddress.ToString().AsCString(), - ErrorToString(error)); + LogWarn("Sent %s for child %04x DUA %s Error %s", UriToString(), aChild.GetRloc16(), + aAddress.ToString().AsCString(), ErrorToString(error)); } } diff --git a/src/core/thread/energy_scan_server.cpp b/src/core/thread/energy_scan_server.cpp index fe9a70cfba8..c207ef9f3ad 100644 --- a/src/core/thread/energy_scan_server.cpp +++ b/src/core/thread/energy_scan_server.cpp @@ -102,7 +102,7 @@ void EnergyScanServer::HandleTmf(Coap::Message &aMessage, const if (aMessage.IsConfirmable() && !aMessageInfo.GetSockAddr().IsMulticast()) { SuccessOrExit(Get().SendEmptyAck(aMessage, aMessageInfo)); - LogInfo("sent energy scan query response"); + LogInfo("Sent %s ack", UriToString()); } exit: @@ -197,7 +197,7 @@ void EnergyScanServer::SendReport(void) SuccessOrExit(error = Get().SendMessage(*mReportMessage, messageInfo)); - LogInfo("sent scan results"); + LogInfo("Sent %s", UriToString()); exit: FreeMessageOnError(mReportMessage, error); diff --git a/src/core/thread/network_data_leader_ftd.cpp b/src/core/thread/network_data_leader_ftd.cpp index 6a553437413..39a033ded60 100644 --- a/src/core/thread/network_data_leader_ftd.cpp +++ b/src/core/thread/network_data_leader_ftd.cpp @@ -134,7 +134,7 @@ template <> void Leader::HandleTmf(Coap::Message &aMessage, cons VerifyOrExit(Get().IsLeader() && !mWaitingForNetDataSync); - LogInfo("Received network data registration"); + LogInfo("Received %s", UriToString()); VerifyOrExit(aMessageInfo.GetPeerAddr().GetIid().IsRoutingLocator()); @@ -162,7 +162,7 @@ template <> void Leader::HandleTmf(Coap::Message &aMessage, cons SuccessOrExit(Get().SendEmptyAck(aMessage, aMessageInfo)); - LogInfo("Sent network data registration acknowledgment"); + LogInfo("Sent %s ack", UriToString()); exit: return; @@ -332,7 +332,7 @@ void Leader::SendCommissioningGetResponse(const Coap::Message &aRequest, SuccessOrExit(error = Get().SendMessage(*message, aMessageInfo)); - LogInfo("sent commissioning dataset get response"); + LogInfo("Sent %s response", UriToString()); exit: FreeMessageOnError(message, error); @@ -352,7 +352,7 @@ void Leader::SendCommissioningSetResponse(const Coap::Message &aRequest, SuccessOrExit(error = Get().SendMessage(*message, aMessageInfo)); - LogInfo("sent commissioning dataset set response"); + LogInfo("sent %s response", UriToString()); exit: FreeMessageOnError(message, error); diff --git a/src/core/thread/network_data_notifier.cpp b/src/core/thread/network_data_notifier.cpp index e9befb4a12f..65b76e06a56 100644 --- a/src/core/thread/network_data_notifier.cpp +++ b/src/core/thread/network_data_notifier.cpp @@ -211,7 +211,7 @@ Error Notifier::SendServerDataNotification(uint16_t aOldRloc16, const NetworkDat IgnoreError(messageInfo.SetSockAddrToRlocPeerAddrToLeaderAloc()); SuccessOrExit(error = Get().SendMessage(*message, messageInfo, HandleCoapResponse, this)); - LogInfo("Sent server data notification"); + LogInfo("Sent %s", UriToString()); exit: FreeMessageOnError(message, error); diff --git a/src/core/thread/network_diagnostic.cpp b/src/core/thread/network_diagnostic.cpp index c40c048e8db..293426feba8 100644 --- a/src/core/thread/network_diagnostic.cpp +++ b/src/core/thread/network_diagnostic.cpp @@ -782,16 +782,16 @@ const char *NetworkDiagnostic::UriToString(Uri aUri) switch (aUri) { case kUriDiagnosticGetQuery: - str = "DiagGetQuery"; + str = ot::UriToString(); break; case kUriDiagnosticGetRequest: - str = "DiagGetRequest"; + str = ot::UriToString(); break; case kUriDiagnosticReset: - str = "DiagReset"; + str = ot::UriToString(); break; case kUriDiagnosticGetAnswer: - str = "DiagGetAnswer"; + str = ot::UriToString(); break; default: break; diff --git a/src/core/thread/panid_query_server.cpp b/src/core/thread/panid_query_server.cpp index 2288c597827..01df10af8ae 100644 --- a/src/core/thread/panid_query_server.cpp +++ b/src/core/thread/panid_query_server.cpp @@ -76,7 +76,7 @@ void PanIdQueryServer::HandleTmf(Coap::Message &aMessage, const if (aMessage.IsConfirmable() && !aMessageInfo.GetSockAddr().IsMulticast()) { SuccessOrExit(Get().SendEmptyAck(aMessage, aMessageInfo)); - LogInfo("sent panid query response"); + LogInfo("Sent %s ack", UriToString()); } exit: @@ -123,7 +123,7 @@ void PanIdQueryServer::SendConflict(void) SuccessOrExit(error = Get().SendMessage(*message, messageInfo)); - LogInfo("sent panid conflict"); + LogInfo("Sent %s", UriToString()); exit: FreeMessageOnError(message, error); diff --git a/src/core/thread/uri_paths.cpp b/src/core/thread/uri_paths.cpp index 112de3611fe..ff26d2e76ab 100644 --- a/src/core/thread/uri_paths.cpp +++ b/src/core/thread/uri_paths.cpp @@ -161,4 +161,44 @@ Uri UriFromPath(const char *aPath) return uri; } +template <> const char *UriToString(void) { return "AddressError"; } +template <> const char *UriToString(void) { return "AddressNotify"; } +template <> const char *UriToString(void) { return "AddressQuery"; } +template <> const char *UriToString(void) { return "AddressRelease"; } +template <> const char *UriToString(void) { return "AddressSolicit"; } +template <> const char *UriToString(void) { return "ServerData"; } +template <> const char *UriToString(void) { return "AnycastLocate"; } +template <> const char *UriToString(void) { return "BackboneAnswer"; } +template <> const char *UriToString(void) { return "BackboneMlr"; } +template <> const char *UriToString(void) { return "BackboneQuery"; } +template <> const char *UriToString(void) { return "AnnounceBegin"; } +template <> const char *UriToString(void) { return "ActiveGet"; } +template <> const char *UriToString(void) { return "ActiveSet"; } +template <> const char *UriToString(void) { return "CommissionerKeepAlive"; } +template <> const char *UriToString(void) { return "CommissionerGet"; } +template <> const char *UriToString(void) { return "CommissionerPetition"; } +template <> const char *UriToString(void) { return "CommissionerSet"; } +template <> const char *UriToString(void) { return "DatasetChanged"; } +template <> const char *UriToString(void) { return "EnergyReport"; } +template <> const char *UriToString(void) { return "EnergyScan"; } +template <> const char *UriToString(void) { return "JoinerEntrust"; } +template <> const char *UriToString(void) { return "JoinerFinalize"; } +template <> const char *UriToString(void) { return "LeaderKeepAlive"; } +template <> const char *UriToString(void) { return "LeaderPetition"; } +template <> const char *UriToString(void) { return "PanIdConflict"; } +template <> const char *UriToString(void) { return "PendingGet"; } +template <> const char *UriToString(void) { return "PanIdQuery"; } +template <> const char *UriToString(void) { return "PendingSet"; } +template <> const char *UriToString(void) { return "RelayRx"; } +template <> const char *UriToString(void) { return "RelayTx"; } +template <> const char *UriToString(void) { return "ProxyRx"; } +template <> const char *UriToString(void) { return "ProxyTx"; } +template <> const char *UriToString(void) { return "DiagGetAnswer"; } +template <> const char *UriToString(void) { return "DiagGetRequest"; } +template <> const char *UriToString(void) { return "DiagGetQuery"; } +template <> const char *UriToString(void) { return "DiagReset"; } +template <> const char *UriToString(void) { return "DuaRegNotify"; } +template <> const char *UriToString(void) { return "DuaRegRequest"; } +template <> const char *UriToString(void) { return "Mlr"; } + } // namespace ot diff --git a/src/core/thread/uri_paths.hpp b/src/core/thread/uri_paths.hpp index e43928edfac..01ef7b08c38 100644 --- a/src/core/thread/uri_paths.hpp +++ b/src/core/thread/uri_paths.hpp @@ -108,6 +108,57 @@ const char *PathForUri(Uri aUri); */ Uri UriFromPath(const char *aPath); +/** + * This template function converts a given URI to a human-readable string. + * + * @tparam kUri The URI to convert to string. + * + * @returns The string representation of @p kUri. + * + */ +template const char *UriToString(void); + +// Declaring specializations of `UriToString` for every `Uri` +template <> const char *UriToString(void); +template <> const char *UriToString(void); +template <> const char *UriToString(void); +template <> const char *UriToString(void); +template <> const char *UriToString(void); +template <> const char *UriToString(void); +template <> const char *UriToString(void); +template <> const char *UriToString(void); +template <> const char *UriToString(void); +template <> const char *UriToString(void); +template <> const char *UriToString(void); +template <> const char *UriToString(void); +template <> const char *UriToString(void); +template <> const char *UriToString(void); +template <> const char *UriToString(void); +template <> const char *UriToString(void); +template <> const char *UriToString(void); +template <> const char *UriToString(void); +template <> const char *UriToString(void); +template <> const char *UriToString(void); +template <> const char *UriToString(void); +template <> const char *UriToString(void); +template <> const char *UriToString(void); +template <> const char *UriToString(void); +template <> const char *UriToString(void); +template <> const char *UriToString(void); +template <> const char *UriToString(void); +template <> const char *UriToString(void); +template <> const char *UriToString(void); +template <> const char *UriToString(void); +template <> const char *UriToString(void); +template <> const char *UriToString(void); +template <> const char *UriToString(void); +template <> const char *UriToString(void); +template <> const char *UriToString(void); +template <> const char *UriToString(void); +template <> const char *UriToString(void); +template <> const char *UriToString(void); +template <> const char *UriToString(void); + } // namespace ot #endif // URI_PATHS_HPP_ From b48544c3b8345d17ad28f498a051804601684a1c Mon Sep 17 00:00:00 2001 From: Zhanglong Xia Date: Thu, 6 Apr 2023 14:23:01 +0800 Subject: [PATCH 05/10] [posix] reset the signal actions after processing signals (#8934) When compiling the otbr-agent in Android, Android will link the function debuggerd_init() and call it before calling the function main(). The function debuggerd_init() sets signal handlers to catch signals. This commit resets the signal actions to default and re-raises the original signal to let the pre-configured functions to catch signals. --- src/posix/platform/backtrace.cpp | 25 ++++++++++++++++++------- 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/src/posix/platform/backtrace.cpp b/src/posix/platform/backtrace.cpp index 873ccb3ff91..1f697ca57b5 100644 --- a/src/posix/platform/backtrace.cpp +++ b/src/posix/platform/backtrace.cpp @@ -132,6 +132,17 @@ static void dumpStack(void) return; } #endif // OPENTHREAD_POSIX_CONFIG_ANDROID_ENABLE +static constexpr uint8_t kNumSignals = 6; +static constexpr int kSignals[kNumSignals] = {SIGABRT, SIGILL, SIGSEGV, SIGBUS, SIGTRAP, SIGFPE}; +static struct sigaction sSigActions[kNumSignals]; + +static void resetSignalActions(void) +{ + for (uint8_t i = 0; i < kNumSignals; i++) + { + sigaction(kSignals[i], &sSigActions[i], (struct sigaction *)nullptr); + } +} static void signalCritical(int sig, siginfo_t *info, void *ucontext) { @@ -144,7 +155,9 @@ static void signalCritical(int sig, siginfo_t *info, void *ucontext) dumpStack(); otLogCritPlat("------------------ END OF CRASH ------------------"); - exit(EXIT_FAILURE); + + resetSignalActions(); + raise(sig); } void platformBacktraceInit(void) @@ -154,12 +167,10 @@ void platformBacktraceInit(void) sigact.sa_sigaction = &signalCritical; sigact.sa_flags = SA_RESTART | SA_SIGINFO | SA_NOCLDWAIT; - sigaction(SIGABRT, &sigact, (struct sigaction *)nullptr); - sigaction(SIGILL, &sigact, (struct sigaction *)nullptr); - sigaction(SIGSEGV, &sigact, (struct sigaction *)nullptr); - sigaction(SIGBUS, &sigact, (struct sigaction *)nullptr); - sigaction(SIGTRAP, &sigact, (struct sigaction *)nullptr); - sigaction(SIGFPE, &sigact, (struct sigaction *)nullptr); + for (uint8_t i = 0; i < kNumSignals; i++) + { + sigaction(kSignals[i], &sigact, &sSigActions[i]); + } } #else // OPENTHREAD_POSIX_CONFIG_ANDROID_ENABLE || defined(__GLIBC__) void platformBacktraceInit(void) {} From 356b4a6a582dc1431c129678bf0667c7845499ad Mon Sep 17 00:00:00 2001 From: Abtin Keshavarzian Date: Thu, 6 Apr 2023 09:51:29 -0700 Subject: [PATCH 06/10] [build] address new warnings with clang-14 (#8924) This commit adds two small changes to address new warnings when building with clang-14. It also updates `mbedtls` CMakeLists to set `MBEDTLS_FATAL_WARNINGS` option as `OFF` (so that compiler warnings are not treated as errors). This avoid issues with new warning for `unused-but-set-variable` emitted by clang-14. --- examples/platforms/simulation/diag.c | 2 +- src/cli/cli.cpp | 2 +- third_party/mbedtls/CMakeLists.txt | 2 ++ 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/examples/platforms/simulation/diag.c b/examples/platforms/simulation/diag.c index 44dc06a7d1a..5b1065c26cf 100644 --- a/examples/platforms/simulation/diag.c +++ b/examples/platforms/simulation/diag.c @@ -60,7 +60,7 @@ static uint16_t sRawPowerSettingLength = 0; void otPlatDiagModeSet(bool aMode) { sDiagMode = aMode; } -bool otPlatDiagModeGet() { return sDiagMode; } +bool otPlatDiagModeGet(void) { return sDiagMode; } void otPlatDiagChannelSet(uint8_t aChannel) { OT_UNUSED_VARIABLE(aChannel); } diff --git a/src/cli/cli.cpp b/src/cli/cli.cpp index dc248a46a22..4b1780bceed 100644 --- a/src/cli/cli.cpp +++ b/src/cli/cli.cpp @@ -3574,7 +3574,7 @@ template <> otError Interpreter::Process(Arg aArgs[]) memset(&iterator, 0, sizeof(iterator)); - for (uint8_t i = 0;; i++) + while (true) { SuccessOrExit(otThreadGetNextCacheEntry(GetInstancePtr(), &entry, &iterator)); OutputEidCacheEntry(entry); diff --git a/third_party/mbedtls/CMakeLists.txt b/third_party/mbedtls/CMakeLists.txt index e58ef81d080..f32df163cba 100644 --- a/third_party/mbedtls/CMakeLists.txt +++ b/third_party/mbedtls/CMakeLists.txt @@ -45,6 +45,8 @@ find_program(SED_EXE sed) string(REPLACE "-Wconversion" "" CMAKE_C_FLAGS "${CMAKE_C_FLAGS}") string(REPLACE "-Wconversion" "" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}") +set(MBEDTLS_FATAL_WARNINGS OFF CACHE BOOL "Compiler warnings treated as errors" FORCE) + add_subdirectory(repo) if(UNIFDEFALL_EXE AND SED_EXE AND UNIFDEF_VERSION VERSION_GREATER_EQUAL 2.10) From 6865b83d7fd045b1ea7ee3654b5553a29a967f46 Mon Sep 17 00:00:00 2001 From: Abtin Keshavarzian Date: Thu, 6 Apr 2023 09:52:33 -0700 Subject: [PATCH 07/10] [coap] avoid using default parameter values for common methods (#8932) This commit adds overloads of commonly used methods in `Coap` and `MessagePool` replacing default parameter values. This helps reduce code size. --- src/core/coap/coap.cpp | 22 +++++++++++ src/core/coap/coap.hpp | 74 +++++++++++++++++++++++++++++-------- src/core/common/message.cpp | 7 ++++ src/core/common/message.hpp | 25 +++++++++++-- 4 files changed, 110 insertions(+), 18 deletions(-) diff --git a/src/core/coap/coap.cpp b/src/core/coap/coap.cpp index b76fb378cbb..a1c5c21091c 100644 --- a/src/core/coap/coap.cpp +++ b/src/core/coap/coap.cpp @@ -115,6 +115,13 @@ Message *CoapBase::NewMessage(const Message::Settings &aSettings) return message; } +Message *CoapBase::NewMessage(void) { return NewMessage(Message::Settings::GetDefault()); } + +Message *CoapBase::NewPriorityMessage(void) +{ + return NewMessage(Message::Settings(Message::kWithLinkSecurity, Message::kPriorityNet)); +} + Message *CoapBase::NewPriorityConfirmablePostMessage(Uri aUri) { return InitMessage(NewPriorityMessage(), kTypeConfirmable, aUri); @@ -351,6 +358,11 @@ Error CoapBase::SendMessage(Message &aMessage, return error; } +Error CoapBase::SendMessage(Message &aMessage, const Ip6::MessageInfo &aMessageInfo, const TxParameters &aTxParameters) +{ + return SendMessage(aMessage, aMessageInfo, aTxParameters, nullptr, nullptr); +} + Error CoapBase::SendMessage(Message &aMessage, const Ip6::MessageInfo &aMessageInfo, ResponseHandler aHandler, @@ -363,6 +375,11 @@ Error CoapBase::SendMessage(Message &aMessage, #endif } +Error CoapBase::SendMessage(Message &aMessage, const Ip6::MessageInfo &aMessageInfo) +{ + return SendMessage(aMessage, aMessageInfo, nullptr, nullptr); +} + Error CoapBase::SendReset(Message &aRequest, const Ip6::MessageInfo &aMessageInfo) { return SendEmptyMessage(kTypeReset, aRequest, aMessageInfo); @@ -378,6 +395,11 @@ Error CoapBase::SendEmptyAck(const Message &aRequest, const Ip6::MessageInfo &aM return (aRequest.IsConfirmable() ? SendHeaderResponse(aCode, aRequest, aMessageInfo) : kErrorInvalidArgs); } +Error CoapBase::SendEmptyAck(const Message &aRequest, const Ip6::MessageInfo &aMessageInfo) +{ + return SendEmptyAck(aRequest, aMessageInfo, kCodeChanged); +} + Error CoapBase::SendNotFound(const Message &aRequest, const Ip6::MessageInfo &aMessageInfo) { return SendHeaderResponse(kCodeNotFound, aRequest, aMessageInfo); diff --git a/src/core/coap/coap.hpp b/src/core/coap/coap.hpp index 5a35770b189..b5d05a669fb 100644 --- a/src/core/coap/coap.hpp +++ b/src/core/coap/coap.hpp @@ -449,7 +449,15 @@ class CoapBase : public InstanceLocator, private NonCopyable * @returns A pointer to the message or `nullptr` if failed to allocate message. * */ - Message *NewMessage(const Message::Settings &aSettings = Message::Settings::GetDefault()); + Message *NewMessage(const Message::Settings &aSettings); + + /** + * This method allocates a new message with a CoAP header with default settings. + * + * @returns A pointer to the message or `nullptr` if failed to allocate message. + * + */ + Message *NewMessage(void); /** * This method allocates a new message with a CoAP header that has Network Control priority level. @@ -457,10 +465,7 @@ class CoapBase : public InstanceLocator, private NonCopyable * @returns A pointer to the message or `nullptr` if failed to allocate message. * */ - Message *NewPriorityMessage(void) - { - return NewMessage(Message::Settings(Message::kWithLinkSecurity, Message::kPriorityNet)); - } + Message *NewPriorityMessage(void); /** * This method allocates and initializes a new CoAP Confirmable Post message with Network Control priority level. @@ -557,7 +562,7 @@ class CoapBase : public InstanceLocator, private NonCopyable * * If a response for a request is expected, respective function and context information should be provided. * If no response is expected, these arguments should be NULL pointers. - * If Message Id was not set in the header (equal to 0), this function will assign unique Message Id to the message. + * If Message ID was not set in the header (equal to 0), this method will assign unique Message ID to the message. * * @param[in] aMessage A reference to the message to send. * @param[in] aMessageInfo A reference to the message info associated with @p aMessage. @@ -585,7 +590,7 @@ class CoapBase : public InstanceLocator, private NonCopyable * * If a response for a request is expected, respective function and context information should be provided. * If no response is expected, these arguments should be `nullptr` pointers. - * If Message Id was not set in the header (equal to 0), this function will assign unique Message Id to the message. + * If Message ID was not set in the header (equal to 0), this method will assign unique Message ID to the message. * * @param[in] aMessage A reference to the message to send. * @param[in] aMessageInfo A reference to the message info associated with @p aMessage. @@ -600,16 +605,28 @@ class CoapBase : public InstanceLocator, private NonCopyable Error SendMessage(Message &aMessage, const Ip6::MessageInfo &aMessageInfo, const TxParameters &aTxParameters, - ResponseHandler aHandler = nullptr, - void *aContext = nullptr); + ResponseHandler aHandler, + void *aContext); #endif // OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE + /** + * This method sends a CoAP message with custom transmission parameters. + * + * If Message ID was not set in the header (equal to 0), this method will assign unique Message ID to the message. + * + * @param[in] aMessage A reference to the message to send. + * @param[in] aMessageInfo A reference to the message info associated with @p aMessage. + * @param[in] aTxParameters A reference to transmission parameters for this message. + * + * @retval kErrorNone Successfully sent CoAP message. + * @retval kErrorNoBufs Insufficient buffers available to send the CoAP message. + * + */ + Error SendMessage(Message &aMessage, const Ip6::MessageInfo &aMessageInfo, const TxParameters &aTxParameters); /** * This method sends a CoAP message with default transmission parameters. * - * If a response for a request is expected, respective function and context information should be provided. - * If no response is expected, these arguments should be `nullptr` pointers. - * If Message Id was not set in the header (equal to 0), this function will assign unique Message Id to the message. + * If Message ID was not set in the header (equal to 0), this method will assign unique Message ID to the message. * * @param[in] aMessage A reference to the message to send. * @param[in] aMessageInfo A reference to the message info associated with @p aMessage. @@ -622,8 +639,22 @@ class CoapBase : public InstanceLocator, private NonCopyable */ Error SendMessage(Message &aMessage, const Ip6::MessageInfo &aMessageInfo, - ResponseHandler aHandler = nullptr, - void *aContext = nullptr); + ResponseHandler aHandler, + void *aContext); + + /** + * This method sends a CoAP message with default transmission parameters. + * + * If Message ID was not set in the header (equal to 0), this method will assign unique Message ID to the message. + * + * @param[in] aMessage A reference to the message to send. + * @param[in] aMessageInfo A reference to the message info associated with @p aMessage. + * + * @retval kErrorNone Successfully sent CoAP message. + * @retval kErrorNoBufs Insufficient buffers available to send the CoAP response. + * + */ + Error SendMessage(Message &aMessage, const Ip6::MessageInfo &aMessageInfo); /** * This method sends a CoAP reset message. @@ -677,7 +708,20 @@ class CoapBase : public InstanceLocator, private NonCopyable * @retval kErrorInvalidArgs The @p aRequest header is not of confirmable type. * */ - Error SendEmptyAck(const Message &aRequest, const Ip6::MessageInfo &aMessageInfo, Code aCode = kCodeChanged); + Error SendEmptyAck(const Message &aRequest, const Ip6::MessageInfo &aMessageInfo, Code aCode); + + /** + * This method sends a CoAP ACK message on which a dummy CoAP response is piggybacked. + * + * @param[in] aRequest A reference to the CoAP Message that was used in CoAP request. + * @param[in] aMessageInfo The message info corresponding to the CoAP request. + * + * @retval kErrorNone Successfully enqueued the CoAP response message. + * @retval kErrorNoBufs Insufficient buffers available to send the CoAP response. + * @retval kErrorInvalidArgs The @p aRequest header is not of confirmable type. + * + */ + Error SendEmptyAck(const Message &aRequest, const Ip6::MessageInfo &aMessageInfo); /** * This method sends a header-only CoAP message to indicate no resource matched for the request. diff --git a/src/core/common/message.cpp b/src/core/common/message.cpp index 1cdd339e0b0..e7727e21f93 100644 --- a/src/core/common/message.cpp +++ b/src/core/common/message.cpp @@ -98,6 +98,13 @@ Message *MessagePool::Allocate(Message::Type aType, uint16_t aReserveHeader, con return message; } +Message *MessagePool::Allocate(Message::Type aType) { return Allocate(aType, 0, Message::Settings::GetDefault()); } + +Message *MessagePool::Allocate(Message::Type aType, uint16_t aReserveHeader) +{ + return Allocate(aType, aReserveHeader, Message::Settings::GetDefault()); +} + void MessagePool::Free(Message *aMessage) { OT_ASSERT(aMessage->Next() == nullptr && aMessage->Prev() == nullptr); diff --git a/src/core/common/message.hpp b/src/core/common/message.hpp index 757d8a85173..307383724e7 100644 --- a/src/core/common/message.hpp +++ b/src/core/common/message.hpp @@ -1706,9 +1706,28 @@ class MessagePool : public InstanceLocator, private NonCopyable * @returns A pointer to the message or `nullptr` if no message buffers are available. * */ - Message *Allocate(Message::Type aType, - uint16_t aReserveHeader = 0, - const Message::Settings &aSettings = Message::Settings::GetDefault()); + Message *Allocate(Message::Type aType, uint16_t aReserveHeader, const Message::Settings &aSettings); + + /** + * This method allocates a new message of a given type using default settings. + * + * @param[in] aType The message type. + * + * @returns A pointer to the message or `nullptr` if no message buffers are available. + * + */ + Message *Allocate(Message::Type aType); + + /** + * This method allocates a new message with a given type and reserved length using default settings. + * + * @param[in] aType The message type. + * @param[in] aReserveHeader The number of header bytes to reserve. + * + * @returns A pointer to the message or `nullptr` if no message buffers are available. + * + */ + Message *Allocate(Message::Type aType, uint16_t aReserveHeader); /** * This method is used to free a message and return all message buffers to the buffer pool. From cb40a452985de5fe8816c642ce8d24832cd5cab7 Mon Sep 17 00:00:00 2001 From: Abtin Keshavarzian Date: Fri, 7 Apr 2023 09:26:38 -0700 Subject: [PATCH 08/10] [joiner] use `Clamp()` function to calculate priority from RSS (#8937) --- src/core/meshcop/joiner.cpp | 16 ++-------------- 1 file changed, 2 insertions(+), 14 deletions(-) diff --git a/src/core/meshcop/joiner.cpp b/src/core/meshcop/joiner.cpp index 9894b9385f2..fc6ac92c650 100644 --- a/src/core/meshcop/joiner.cpp +++ b/src/core/meshcop/joiner.cpp @@ -238,20 +238,8 @@ uint8_t Joiner::CalculatePriority(int8_t aRssi, bool aSteeringDataAllowsAny) aRssi = -127; } - // Limit the RSSI to range (-128, 0), i.e. -128 < aRssi < 0. - - if (aRssi <= -128) - { - priority = -127; - } - else if (aRssi >= 0) - { - priority = -1; - } - else - { - priority = aRssi; - } + // Clamp the RSSI to range [-127, -1] + priority = Clamp(aRssi, -127, -1); // Assign higher priority to networks with an exact match of Joiner // ID in the Steering Data (128 < priority < 256) compared to ones From d9ea37a3bbc40fb305f17eea44b08b1db78084ce Mon Sep 17 00:00:00 2001 From: Abtin Keshavarzian Date: Fri, 7 Apr 2023 12:23:01 -0700 Subject: [PATCH 09/10] [tmf] add local IPv6 DSCP values to indicate TMF message priority (#7869) This commit adds new IPv6 header DSCP values which indicate a TMF message priority. This allows intermediate routers forwarding TMF message to be able to determine and use the TMF message priority that originator of the message selects (e.g., address query/notification use net-level priority whereas network diagnostics message use normal priority). This replaces the previous behavior where on intermediate routers all TMF messages were treated as net-level priority. The new DSCP values are allocated from local codepoint range `0bxxxx11` (per RFC 2474 - section 6). If the sender does not use TMF-specific DSCP values, we use `kPriorityNet` as default on intermediate router. This ensures that senders that are using older code (do not use the new) experience the same behavior as before. --- src/core/net/ip6.cpp | 14 ++++++++- src/core/net/ip6_types.hpp | 8 ++++- src/core/thread/mesh_forwarder.cpp | 6 +++- src/core/thread/tmf.cpp | 48 ++++++++++++++++++++++++++++++ src/core/thread/tmf.hpp | 20 +++++++++++++ 5 files changed, 93 insertions(+), 3 deletions(-) diff --git a/src/core/net/ip6.cpp b/src/core/net/ip6.cpp index 2ba601b06b6..b47bf9ef74b 100644 --- a/src/core/net/ip6.cpp +++ b/src/core/net/ip6.cpp @@ -428,10 +428,22 @@ Error Ip6::SendDatagram(Message &aMessage, MessageInfo &aMessageInfo, uint8_t aI { Error error = kErrorNone; Header header; + uint8_t dscp; uint16_t payloadLength = aMessage.GetLength(); + if ((aIpProto == kProtoUdp) && + Get().IsTmfMessage(aMessageInfo.GetSockAddr(), aMessageInfo.GetPeerAddr(), + aMessageInfo.GetPeerPort())) + { + dscp = Tmf::Agent::PriorityToDscp(aMessage.GetPriority()); + } + else + { + dscp = PriorityToDscp(aMessage.GetPriority()); + } + header.InitVersionTrafficClassFlow(); - header.SetDscp(PriorityToDscp(aMessage.GetPriority())); + header.SetDscp(dscp); header.SetEcn(aMessageInfo.GetEcn()); header.SetPayloadLength(payloadLength); header.SetNextHeader(aIpProto); diff --git a/src/core/net/ip6_types.hpp b/src/core/net/ip6_types.hpp index 06af1266008..8e1f9c34137 100644 --- a/src/core/net/ip6_types.hpp +++ b/src/core/net/ip6_types.hpp @@ -89,7 +89,13 @@ enum IpDscpCs : uint8_t kDscpCs5 = 40, ///< Class selector codepoint 40 kDscpCs6 = 48, ///< Class selector codepoint 48 kDscpCs7 = 56, ///< Class selector codepoint 56 - kDscpCsMask = 0x38, ///< Class selector mask + kDscpCsMask = 0x38, ///< Class selector mask (0b111000) + + // DSCP values to use within Thread mesh (from local codepoint space 0bxxxx11 [RFC 2474 - section 6]). + + kDscpTmfNetPriority = 0x07, ///< TMF network priority (0b000111). + kDscpTmfNormalPriority = 0x0f, ///< TMF normal priority (0b001111). + kDscpTmfLowPriority = 0x17, ///< TMF low priority (0b010111). }; /** diff --git a/src/core/thread/mesh_forwarder.cpp b/src/core/thread/mesh_forwarder.cpp index 9af353bc504..4be804c0779 100644 --- a/src/core/thread/mesh_forwarder.cpp +++ b/src/core/thread/mesh_forwarder.cpp @@ -1626,10 +1626,14 @@ Error MeshForwarder::GetFramePriority(const FrameData &aFrameData, { uint16_t destPort = headers.GetUdpHeader().GetDestinationPort(); - if ((destPort == Mle::kUdpPort) || (destPort == Tmf::kUdpPort)) + if (destPort == Mle::kUdpPort) { aPriority = Message::kPriorityNet; } + else if (Get().IsTmfMessage(headers.GetSourceAddress(), headers.GetDestinationAddress(), destPort)) + { + aPriority = Tmf::Agent::DscpToPriority(headers.GetIp6Header().GetDscp()); + } } exit: diff --git a/src/core/thread/tmf.cpp b/src/core/thread/tmf.cpp index 3f35c574d6d..fecd978537b 100644 --- a/src/core/thread/tmf.cpp +++ b/src/core/thread/tmf.cpp @@ -34,6 +34,7 @@ #include "thread/tmf.hpp" #include "common/locator_getters.hpp" +#include "net/ip6_types.hpp" namespace ot { namespace Tmf { @@ -222,6 +223,53 @@ bool Agent::IsTmfMessage(const Ip6::Address &aSourceAddress, const Ip6::Address return isTmf; } +uint8_t Agent::PriorityToDscp(Message::Priority aPriority) +{ + uint8_t dscp = Ip6::kDscpTmfNormalPriority; + + switch (aPriority) + { + case Message::kPriorityNet: + dscp = Ip6::kDscpTmfNetPriority; + break; + + case Message::kPriorityHigh: + case Message::kPriorityNormal: + break; + + case Message::kPriorityLow: + dscp = Ip6::kDscpTmfLowPriority; + break; + } + + return dscp; +} + +Message::Priority Agent::DscpToPriority(uint8_t aDscp) +{ + Message::Priority priority = Message::kPriorityNet; + + // If the sender does not use TMF specific DSCP value, we use + // `kPriorityNet`. This ensures that senders that do not use the + // new value (older firmware) experience the same behavior as + // before where all TMF message were treated as `kPriorityNet`. + + switch (aDscp) + { + case Ip6::kDscpTmfNetPriority: + default: + break; + case Ip6::kDscpTmfNormalPriority: + priority = Message::kPriorityNormal; + break; + case Ip6::kDscpTmfLowPriority: + priority = Message::kPriorityLow; + break; + } + + return priority; +} + #if OPENTHREAD_CONFIG_DTLS_ENABLE SecureAgent::SecureAgent(Instance &aInstance) diff --git a/src/core/thread/tmf.hpp b/src/core/thread/tmf.hpp index ecb389479e3..02a1aa34a51 100644 --- a/src/core/thread/tmf.hpp +++ b/src/core/thread/tmf.hpp @@ -182,6 +182,26 @@ class Agent : public Coap::Coap */ bool IsTmfMessage(const Ip6::Address &aSourceAddress, const Ip6::Address &aDestAddress, uint16_t aDestPort) const; + /** + * This static method converts a TMF message priority to IPv6 header DSCP value. + * + * @param[in] aPriority The message priority to convert. + * + * @returns The DSCP value corresponding to @p aPriority. + * + */ + static uint8_t PriorityToDscp(Message::Priority aPriority); + + /** + * This static method converts a IPv6 header DSCP value to message priority for TMF message. + * + * @param[in] aDscp The IPv6 header DSCP value in a TMF message. + * + * @returns The message priority corresponding to the @p aDscp. + * + */ + static Message::Priority DscpToPriority(uint8_t aDscp); + private: template void HandleTmf(Message &aMessage, const Ip6::MessageInfo &aMessageInfo); From 0f94f26a207d1ead2fe55f978d99d4c0b04d05e6 Mon Sep 17 00:00:00 2001 From: Abtin Keshavarzian Date: Fri, 7 Apr 2023 15:58:07 -0700 Subject: [PATCH 10/10] [net-diag] introduce `Server` and `Client` classes (#8936) This commit introduces `Server` and `Client` classes breaking the `NetworkDiagnostic` module into two components. The `Server` responds to queries and requests (handling Net Diag TMF commands), while `Client` issues queries/requests and processes responses. The `Server` is available under both FTD and MTD (which follows the requirement of Thread spec). The client is enabled using newly added `OPENTHREAD_CONFIG_TMF_NETDIAG_CLIENT_ENABLE` config option which is also available as `OT_NETDIAG_CLIENT` CMake option or the autoconf build switch `NETDIAG_CLIENT`. The client functionality is by default enabled on Border Routers (tied to `CONFIG_BORDER_ROUTING_ENABLE` config). The previous `TMF_NETWORK_DIAG_MTD_ENABLE` config is now removed along with its CMake `OT_MTD_NETDIAG` and autoconf switch. This commit adds checks to trigger build error if the previous config and/or its related CMake option are used. --- etc/cmake/options.cmake | 16 +- etc/gn/openthread.gni | 4 +- examples/Makefile-simulation | 2 +- examples/README.md | 8 +- examples/common-switches.mk | 8 + include/openthread/instance.h | 2 +- include/openthread/netdiag.h | 2 + script/check-arm-build | 2 +- script/check-scan-build | 2 +- script/check-simulation-build-autotools | 2 +- script/check-size | 1 - script/cmake-build | 2 +- script/make-pretty | 2 +- src/cli/cli.cpp | 6 +- src/cli/cli.hpp | 2 +- src/core/BUILD.gn | 9 +- src/core/api/netdiag_api.cpp | 12 +- src/core/common/instance.cpp | 5 +- src/core/common/instance.hpp | 11 +- .../config/openthread-core-config-check.h | 6 + src/core/config/tmf.h | 11 +- src/core/thread/network_diagnostic.cpp | 295 +++++++++--------- src/core/thread/network_diagnostic.hpp | 76 +++-- src/core/thread/tmf.cpp | 11 +- src/posix/Makefile-posix | 2 +- tests/fuzz/oss-fuzz-build | 2 +- 26 files changed, 276 insertions(+), 225 deletions(-) diff --git a/etc/cmake/options.cmake b/etc/cmake/options.cmake index 7bbe4ebc858..8122957205e 100644 --- a/etc/cmake/options.cmake +++ b/etc/cmake/options.cmake @@ -122,12 +122,12 @@ ot_option(OT_MESH_DIAG OPENTHREAD_CONFIG_MESH_DIAG_ENABLE "mesh diag") ot_option(OT_MESSAGE_USE_HEAP OPENTHREAD_CONFIG_MESSAGE_USE_HEAP_ENABLE "heap allocator for message buffers") ot_option(OT_MLE_LONG_ROUTES OPENTHREAD_CONFIG_MLE_LONG_ROUTES_ENABLE "MLE long routes extension (experimental)") ot_option(OT_MLR OPENTHREAD_CONFIG_MLR_ENABLE "Multicast Listener Registration (MLR)") -ot_option(OT_MTD_NETDIAG OPENTHREAD_CONFIG_TMF_NETWORK_DIAG_MTD_ENABLE "TMF network diagnostics on MTDs") ot_option(OT_MULTIPLE_INSTANCE OPENTHREAD_CONFIG_MULTIPLE_INSTANCE_ENABLE "multiple instances") ot_option(OT_NAT64_BORDER_ROUTING OPENTHREAD_CONFIG_NAT64_BORDER_ROUTING_ENABLE "border routing NAT64") ot_option(OT_NAT64_TRANSLATOR OPENTHREAD_CONFIG_NAT64_TRANSLATOR_ENABLE "NAT64 translator support") ot_option(OT_NEIGHBOR_DISCOVERY_AGENT OPENTHREAD_CONFIG_NEIGHBOR_DISCOVERY_AGENT_ENABLE "neighbor discovery agent") ot_option(OT_NETDATA_PUBLISHER OPENTHREAD_CONFIG_NETDATA_PUBLISHER_ENABLE "Network Data publisher") +ot_option(OT_NETDIAG_CLIENT OPENTHREAD_CONFIG_TMF_NETDIAG_CLIENT_ENABLE "Network Diagnostic client") ot_option(OT_OTNS OPENTHREAD_CONFIG_OTNS_ENABLE "OTNS") ot_option(OT_PING_SENDER OPENTHREAD_CONFIG_PING_SENDER_ENABLE "ping sender" ${OT_APP_CLI}) ot_option(OT_PLATFORM_NETIF OPENTHREAD_CONFIG_PLATFORM_NETIF_ENABLE "platform netif") @@ -210,3 +210,17 @@ endif() if(OT_POSIX_SETTINGS_PATH) target_compile_definitions(ot-config INTERFACE "OPENTHREAD_CONFIG_POSIX_SETTINGS_PATH=${OT_POSIX_SETTINGS_PATH}") endif() + +#----------------------------------------------------------------------------------------------------------------------- +# Check removed/replaced options + +macro(ot_removed_option name error) + # This macro checks for a remove option and emits an error + # if the option is set. + get_property(is_set CACHE ${name} PROPERTY VALUE SET) + if (is_set) + message(FATAL_ERROR "Removed option ${name} is set - ${error}") + endif() +endmacro() + +ot_removed_option(OT_MTD_NETDIAG "- Use OT_NETDIAG_CLIENT instead - note that server function is always supported") diff --git a/etc/gn/openthread.gni b/etc/gn/openthread.gni index fe494b08ea6..9bf75fb0f9e 100644 --- a/etc/gn/openthread.gni +++ b/etc/gn/openthread.gni @@ -174,8 +174,8 @@ if (openthread_enable_core_config_args) { # Enable MLE long routes extension (experimental, breaks Thread conformance] openthread_config_mle_long_routes_enable = false - # Enable TMF network diagnostics on MTDs - openthread_config_tmf_network_diag_mtd_enable = false + # Enable TMF network diagnostics client + openthread_config_tmf_netdiag_client_enable = false # Enable multiple instances openthread_config_multiple_instance_enable = false diff --git a/examples/Makefile-simulation b/examples/Makefile-simulation index f7577f72398..72894fb4703 100644 --- a/examples/Makefile-simulation +++ b/examples/Makefile-simulation @@ -59,9 +59,9 @@ JAM_DETECTION ?= 1 JOINER ?= 1 LINK_RAW ?= 1 MAC_FILTER ?= 1 -MTD_NETDIAG ?= 1 NEIGHBOR_DISCOVERY_AGENT ?= 1 NETDATA_PUBLISHER ?= 1 +NETDIAG_CLIENT ?= 1 PING_SENDER ?= 1 REFERENCE_DEVICE ?= 1 SERVICE ?= 1 diff --git a/examples/README.md b/examples/README.md index 45d4815f31d..0c2a59de701 100644 --- a/examples/README.md +++ b/examples/README.md @@ -51,11 +51,11 @@ This page lists the available common switches with description. Unless stated ot | MAC_FILTER | OT_MAC_FILTER | Enables support for the MAC filter. | | MLE_LONG_ROUTES | OT_MLE_LONG_ROUTES | Enables the MLE long routes extension. **Note: Enabling this feature breaks conformance to the Thread Specification.** | | MLR | OT_MLR | Enables Multicast Listener Registration feature for Thread 1.2. | -| MTD_NETDIAG | OT_MTD_NETDIAG | Enables the TMF network diagnostics on MTDs. | | MULTIPLE_INSTANCE | OT_MULTIPLE_INSTANCE | Enables multiple OpenThread instances. | | NAT64_BORDER_ROUTING | OT_NAT64_BORDER_ROUTING | Enables NAT64 border routing support for Border Router. | | NAT64_TRANSLATOR | OT_NAT64_TRANSLATOR | Enables NAT64 translator for Border Router. | | NETDATA_PUBLISHER | OT_NETDATA_PUBLISHER | Enables support for Thread Network Data publisher. | +| NETDIAG_CLIENT | OT_NETDIAG_CLIENT | Enables Network Diagnostics client functionality. | | PING_SENDER | OT_PING_SENDER | Enables support for ping sender. | | OTNS | OT_OTNS | Enables support for [OpenThread Network Simulator](https://github.com/openthread/ot-ns). Enable this switch if you are building OpenThread for OpenThread Network Simulator. | | PLATFORM_UDP | OT_PLATFORM_UDP | Enables platform UDP support. | @@ -73,3 +73,9 @@ This page lists the available common switches with description. Unless stated ot | TREL | OT_TREL | Enables TREL radio link for Thread over Infrastructure feature. | | UDP_FORWARD | OT_UDP_FORWARD | Enables support for UDP forward. Enable this switch on the Border Router device (running on the NCP design) with External Commissioning support to service Thread Commissioner packets on the NCP side. | | UPTIME | OT_UPTIME | Enables support for tracking OpenThread instance's uptime. | + +Removed or replaced switches: + +| Makefile switch | CMake switch | Description | +| --- | --- | --- | +| MTD_NETDIAG | OT_MTD_NETDIAG | Use NEDIAG_CLIENT to enable client functionality. Server functionality is always supported. | diff --git a/examples/common-switches.mk b/examples/common-switches.mk index 0ae6831332c..36ff95726db 100644 --- a/examples/common-switches.mk +++ b/examples/common-switches.mk @@ -76,6 +76,7 @@ NAT64_BORDER_ROUTING ?= 0 NAT64_TRANSLATOR ?= 0 NEIGHBOR_DISCOVERY_AGENT ?= 0 NETDATA_PUBLISHER ?= 0 +NETDIAG_CLIENT ?= 0 OTNS ?= 0 PING_SENDER ?= 1 PLATFORM_UDP ?= 0 @@ -286,6 +287,9 @@ ifeq ($(MLR),1) COMMONCFLAGS += -DOPENTHREAD_CONFIG_MLR_ENABLE=1 endif +# This config is removed but we still check and add the +# `OPENTHREAD_CONFIG_TMF_NETWORK_DIAG_MTD_ENABLE` so to +# get an error during build if `MTD_NETDIAG` is used. ifeq ($(MTD_NETDIAG),1) COMMONCFLAGS += -DOPENTHREAD_CONFIG_TMF_NETWORK_DIAG_MTD_ENABLE=1 endif @@ -302,6 +306,10 @@ ifeq ($(NETDATA_PUBLISHER),1) COMMONCFLAGS += -DOPENTHREAD_CONFIG_NETDATA_PUBLISHER_ENABLE=1 endif +ifeq ($(NETDIAG_CLIENT),1) +COMMONCFLAGS += -DOPENTHREAD_CONFIG_TMF_NETDIAG_CLIENT_ENABLE=1 +endif + ifeq ($(PING_SENDER),1) COMMONCFLAGS += -DOPENTHREAD_CONFIG_PING_SENDER_ENABLE=1 endif diff --git a/include/openthread/instance.h b/include/openthread/instance.h index 367d8e9751c..8f523cbe111 100644 --- a/include/openthread/instance.h +++ b/include/openthread/instance.h @@ -53,7 +53,7 @@ extern "C" { * @note This number versions both OpenThread platform and user APIs. * */ -#define OPENTHREAD_API_VERSION (307) +#define OPENTHREAD_API_VERSION (308) /** * @addtogroup api-instance diff --git a/include/openthread/netdiag.h b/include/openthread/netdiag.h index c4071e2073e..ce8ab36ac88 100644 --- a/include/openthread/netdiag.h +++ b/include/openthread/netdiag.h @@ -47,6 +47,8 @@ extern "C" { * * @{ * + * + * Network Dianostics APIs require OPENTHREAD_CONFIG_TMF_NETDIAG_CLIENT_ENABLE. */ /** diff --git a/script/check-arm-build b/script/check-arm-build index a2dff828dd2..398dff76682 100755 --- a/script/check-arm-build +++ b/script/check-arm-build @@ -65,8 +65,8 @@ build_nrf52840() "-DOT_MAC_FILTER=ON" "-DOT_MESSAGE_USE_HEAP=ON" "-DOT_MLR=ON" - "-DOT_MTD_NETDIAG=ON" "-DOT_NETDATA_PUBLISHER=ON" + "-DOT_NETDIAG_CLIENT=ON" "-DOT_PING_SENDER=ON" "-DOT_SERVICE=ON" "-DOT_SLAAC=ON" diff --git a/script/check-scan-build b/script/check-scan-build index b04b111b401..edd86d7cafe 100755 --- a/script/check-scan-build +++ b/script/check-scan-build @@ -62,10 +62,10 @@ OT_BUILD_OPTIONS=( "-DOT_LOG_LEVEL_DYNAMIC=ON" "-DOT_MAC_FILTER=ON" "-DOT_MESH_DIAG=ON" - "-DOT_MTD_NETDIAG=ON" "-DOT_NAT64_BORDER_ROUTING=ON" "-DOT_NAT64_TRANSLATOR=ON" "-DOT_NEIGHBOR_DISCOVERY_AGENT=ON" + "-DOT_NETDIAG_CLIENT=ON" "-DOT_PING_SENDER=ON" "-DOT_PLATFORM=external" "-DOT_RCP_RESTORATION_MAX_COUNT=2" diff --git a/script/check-simulation-build-autotools b/script/check-simulation-build-autotools index 118a293a43a..1e3ef250f4d 100755 --- a/script/check-simulation-build-autotools +++ b/script/check-simulation-build-autotools @@ -86,7 +86,7 @@ build_all_features() "-DOPENTHREAD_CONFIG_SNTP_CLIENT_ENABLE=1" "-DOPENTHREAD_CONFIG_SRP_CLIENT_ENABLE=1" "-DOPENTHREAD_CONFIG_TMF_NETDATA_SERVICE_ENABLE=1" - "-DOPENTHREAD_CONFIG_TMF_NETWORK_DIAG_MTD_ENABLE=1" + "-DOPENTHREAD_CONFIG_TMF_NETDIAG_CLIENT_ENABLE=1" "-DOPENTHREAD_CONFIG_UDP_FORWARD_ENABLE=1" "-DOPENTHREAD_CONFIG_MAC_BEACON_PAYLOAD_PARSING_ENABLE=1" "-DOPENTHREAD_CONFIG_MAC_OUTGOING_BEACON_PAYLOAD_ENABLE=1" diff --git a/script/check-size b/script/check-size index 482dddf1a04..9076626984e 100755 --- a/script/check-size +++ b/script/check-size @@ -142,7 +142,6 @@ size_nrf52840_version() "-DOT_LINK_RAW=ON" "-DOT_MAC_FILTER=ON" "-DOT_MESSAGE_USE_HEAP=ON" - "-DOT_MTD_NETDIAG=ON" "-DOT_NETDATA_PUBLISHER=ON" "-DOT_PING_SENDER=ON" "-DOT_SERVICE=ON" diff --git a/script/cmake-build b/script/cmake-build index 364721623d1..9d16d2474fd 100755 --- a/script/cmake-build +++ b/script/cmake-build @@ -92,9 +92,9 @@ OT_POSIX_SIM_COMMON_OPTIONS=( "-DOT_JAM_DETECTION=ON" "-DOT_JOINER=ON" "-DOT_MAC_FILTER=ON" - "-DOT_MTD_NETDIAG=ON" "-DOT_NEIGHBOR_DISCOVERY_AGENT=ON" "-DOT_NETDATA_PUBLISHER=ON" + "-DOT_NETDIAG_CLIENT=ON" "-DOT_PING_SENDER=ON" "-DOT_REFERENCE_DEVICE=ON" "-DOT_SERVICE=ON" diff --git a/script/make-pretty b/script/make-pretty index 443ac53216b..28abdf01461 100755 --- a/script/make-pretty +++ b/script/make-pretty @@ -121,10 +121,10 @@ OT_CLANG_TIDY_BUILD_OPTS=( '-DOT_LINK_METRICS_SUBJECT=ON' '-DOT_MAC_FILTER=ON' '-DOT_MESH_DIAG=ON' - '-DOT_MTD_NETDIAG=ON' '-DOT_NAT64_BORDER_ROUTING=ON' '-DOT_NAT64_TRANSLATOR=ON' '-DOT_NETDATA_PUBLISHER=ON' + '-DOT_NETDIAG_CLIENT=ON' '-DOT_PING_SENDER=ON' '-DOT_REFERENCE_DEVICE=ON' '-DOT_SERVICE=ON' diff --git a/src/cli/cli.cpp b/src/cli/cli.cpp index 4b1780bceed..b45c2c3c71a 100644 --- a/src/cli/cli.cpp +++ b/src/cli/cli.cpp @@ -7950,7 +7950,7 @@ template <> otError Interpreter::Process(Arg aArgs[]) } #endif -#if OPENTHREAD_FTD || OPENTHREAD_CONFIG_TMF_NETWORK_DIAG_MTD_ENABLE +#if OPENTHREAD_CONFIG_TMF_NETDIAG_CLIENT_ENABLE template <> otError Interpreter::Process(Arg aArgs[]) { otError error = OT_ERROR_NONE; @@ -8175,7 +8175,7 @@ void Interpreter::OutputChildTableEntry(uint8_t aIndentSize, const otNetworkDiag OutputLine(aIndentSize, "Mode:"); OutputMode(aIndentSize + kIndentSize, aChildEntry.mMode); } -#endif // OPENTHREAD_FTD || OPENTHREAD_CONFIG_TMF_NETWORK_DIAG_MTD_ENABLE +#endif // OPENTHREAD_CONFIG_TMF_NETDIAG_CLIENT_ENABLE void Interpreter::HandleDetachGracefullyResult(void *aContext) { @@ -8394,7 +8394,7 @@ otError Interpreter::ProcessCommand(Arg aArgs[]) #endif CmdEntry("netdata"), CmdEntry("netstat"), -#if OPENTHREAD_FTD || OPENTHREAD_CONFIG_TMF_NETWORK_DIAG_MTD_ENABLE +#if OPENTHREAD_CONFIG_TMF_NETDIAG_CLIENT_ENABLE CmdEntry("networkdiagnostic"), #endif #if OPENTHREAD_FTD diff --git a/src/cli/cli.hpp b/src/cli/cli.hpp index 79faa376db8..d9a59ce53ac 100644 --- a/src/cli/cli.hpp +++ b/src/cli/cli.hpp @@ -439,7 +439,7 @@ class Interpreter : public OutputImplementer, public Output static void HandleEnergyScanResult(otEnergyScanResult *aResult, void *aContext); static void HandleLinkPcapReceive(const otRadioFrame *aFrame, bool aIsTx, void *aContext); -#if OPENTHREAD_FTD || (OPENTHREAD_MTD && OPENTHREAD_CONFIG_TMF_NETWORK_DIAG_MTD_ENABLE) +#if OPENTHREAD_CONFIG_TMF_NETDIAG_CLIENT_ENABLE void HandleDiagnosticGetResponse(otError aError, const otMessage *aMessage, const Ip6::MessageInfo *aMessageInfo); static void HandleDiagnosticGetResponse(otError aError, otMessage *aMessage, diff --git a/src/core/BUILD.gn b/src/core/BUILD.gn index f968aee6a45..5212041b47f 100644 --- a/src/core/BUILD.gn +++ b/src/core/BUILD.gn @@ -196,18 +196,19 @@ if (openthread_enable_core_config_args) { defines += [ "OPENTHREAD_CONFIG_MLE_LONG_ROUTES_ENABLE=1" ] } - if (openthread_config_tmf_network_diag_mtd_enable) { - defines += [ "OPENTHREAD_CONFIG_TMF_NETWORK_DIAG_MTD_ENABLE=1" ] - } - if (openthread_config_multiple_instance_enable) { defines += [ "OPENTHREAD_CONFIG_MULTIPLE_INSTANCE_ENABLE=1" ] } + if (openthread_config_tmf_netdiag_client_enable) { + defines += [ "OPENTHREAD_CONFIG_TMF_NETDIAG_CLIENT_ENABLE=1" ] + } + if (openthread_config_platform_netif_enable) { defines += [ "OPENTHREAD_CONFIG_PLATFORM_NETIF_ENABLE=1" ] } + if (openthread_config_platform_udp_enable) { defines += [ "OPENTHREAD_CONFIG_PLATFORM_UDP_ENABLE=1" ] } diff --git a/src/core/api/netdiag_api.cpp b/src/core/api/netdiag_api.cpp index b6f2d6072cf..68cf91c6268 100644 --- a/src/core/api/netdiag_api.cpp +++ b/src/core/api/netdiag_api.cpp @@ -33,7 +33,7 @@ #include "openthread-core-config.h" -#if OPENTHREAD_FTD || OPENTHREAD_CONFIG_TMF_NETWORK_DIAG_MTD_ENABLE +#if OPENTHREAD_CONFIG_TMF_NETDIAG_CLIENT_ENABLE #include @@ -49,7 +49,7 @@ otError otThreadGetNextDiagnosticTlv(const otMessage *aMessage, AssertPointerIsNotNull(aIterator); AssertPointerIsNotNull(aNetworkDiagTlv); - return NetworkDiagnostic::NetworkDiagnostic::GetNextDiagTlv(AsCoapMessage(aMessage), *aIterator, *aNetworkDiagTlv); + return NetworkDiagnostic::Client::GetNextDiagTlv(AsCoapMessage(aMessage), *aIterator, *aNetworkDiagTlv); } otError otThreadSendDiagnosticGet(otInstance *aInstance, @@ -59,7 +59,7 @@ otError otThreadSendDiagnosticGet(otInstance *aInstance, otReceiveDiagnosticGetCallback aCallback, void *aCallbackContext) { - return AsCoreType(aInstance).Get().SendDiagnosticGet( + return AsCoreType(aInstance).Get().SendDiagnosticGet( AsCoreType(aDestination), aTlvTypes, aCount, aCallback, aCallbackContext); } @@ -68,8 +68,8 @@ otError otThreadSendDiagnosticReset(otInstance *aInstance, const uint8_t aTlvTypes[], uint8_t aCount) { - return AsCoreType(aInstance).Get().SendDiagnosticReset( - AsCoreType(aDestination), aTlvTypes, aCount); + return AsCoreType(aInstance).Get().SendDiagnosticReset(AsCoreType(aDestination), + aTlvTypes, aCount); } -#endif // OPENTHREAD_FTD || OPENTHREAD_CONFIG_TMF_NETWORK_DIAG_MTD_ENABLE +#endif // OPENTHREAD_CONFIG_TMF_NETDIAG_CLIENT_ENABLE diff --git a/src/core/common/instance.cpp b/src/core/common/instance.cpp index 384843b68b6..294fc4a7110 100644 --- a/src/core/common/instance.cpp +++ b/src/core/common/instance.cpp @@ -136,8 +136,9 @@ Instance::Instance(void) , mNetworkDataPublisher(*this) #endif , mNetworkDataServiceManager(*this) -#if OPENTHREAD_FTD || OPENTHREAD_CONFIG_TMF_NETWORK_DIAG_MTD_ENABLE - , mNetworkDiagnostic(*this) + , mNetworkDiagnosticServer(*this) +#if OPENTHREAD_CONFIG_TMF_NETDIAG_CLIENT_ENABLE + , mNetworkDiagnosticClient(*this) #endif #if OPENTHREAD_CONFIG_BORDER_AGENT_ENABLE , mBorderAgent(*this) diff --git a/src/core/common/instance.hpp b/src/core/common/instance.hpp index 14d21ca6b89..d7c171dcb1f 100644 --- a/src/core/common/instance.hpp +++ b/src/core/common/instance.hpp @@ -495,8 +495,9 @@ class Instance : public otInstance, private NonCopyable NetworkData::Service::Manager mNetworkDataServiceManager; -#if OPENTHREAD_FTD || OPENTHREAD_CONFIG_TMF_NETWORK_DIAG_MTD_ENABLE - NetworkDiagnostic::NetworkDiagnostic mNetworkDiagnostic; + NetworkDiagnostic::Server mNetworkDiagnosticServer; +#if OPENTHREAD_CONFIG_TMF_NETDIAG_CLIENT_ENABLE + NetworkDiagnostic::Client mNetworkDiagnosticClient; #endif #if OPENTHREAD_CONFIG_BORDER_AGENT_ENABLE @@ -824,8 +825,10 @@ template <> inline Dns::ServiceDiscovery::Server &Instance::Get(void) { return m template <> inline Dns::Dso &Instance::Get(void) { return mDnsDso; } #endif -#if OPENTHREAD_FTD || OPENTHREAD_CONFIG_TMF_NETWORK_DIAG_MTD_ENABLE -template <> inline NetworkDiagnostic::NetworkDiagnostic &Instance::Get(void) { return mNetworkDiagnostic; } +template <> inline NetworkDiagnostic::Server &Instance::Get(void) { return mNetworkDiagnosticServer; } + +#if OPENTHREAD_CONFIG_TMF_NETDIAG_CLIENT_ENABLE +template <> inline NetworkDiagnostic::Client &Instance::Get(void) { return mNetworkDiagnosticClient; } #endif #if OPENTHREAD_CONFIG_DHCP6_CLIENT_ENABLE diff --git a/src/core/config/openthread-core-config-check.h b/src/core/config/openthread-core-config-check.h index 99121477067..685fb9123e0 100644 --- a/src/core/config/openthread-core-config-check.h +++ b/src/core/config/openthread-core-config-check.h @@ -648,4 +648,10 @@ #error "OPENTHREAD_CONFIG_CHILD_SUPERVISION_MSG_NO_ACK_REQUEST is removed". #endif +#ifdef OPENTHREAD_CONFIG_TMF_NETWORK_DIAG_MTD_ENABLE +#error "OPENTHREAD_CONFIG_TMF_NETWORK_DIAG_MTD_ENABLE is removed. "\ + "Use OPENTHREAD_CONFIG_TMF_NETDIAG_CLIENT_ENABLE to enable client functionality."\ + "Netdiag server functionality is always supported." +#endif + #endif // OPENTHREAD_CORE_CONFIG_CHECK_H_ diff --git a/src/core/config/tmf.h b/src/core/config/tmf.h index 0bd1f365129..5632b8f6457 100644 --- a/src/core/config/tmf.h +++ b/src/core/config/tmf.h @@ -173,13 +173,16 @@ #endif /** - * @def OPENTHREAD_CONFIG_TMF_NETWORK_DIAG_MTD_ENABLE + * @def OPENTHREAD_CONFIG_TMF_NETDIAG_CLIENT_ENABLE * - * Define to 1 to enable TMF network diagnostics on MTDs. + * Define to 1 to enable TMF network diagnostics client. + * + * The network diagnostic client add API to send diagnostic requests and queries to other node and process the response. + * It is enabled by default on Border Routers. * */ -#ifndef OPENTHREAD_CONFIG_TMF_NETWORK_DIAG_MTD_ENABLE -#define OPENTHREAD_CONFIG_TMF_NETWORK_DIAG_MTD_ENABLE 0 +#ifndef OPENTHREAD_CONFIG_TMF_NETDIAG_CLIENT_ENABLE +#define OPENTHREAD_CONFIG_TMF_NETDIAG_CLIENT_ENABLE OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE #endif /** diff --git a/src/core/thread/network_diagnostic.cpp b/src/core/thread/network_diagnostic.cpp index 293426feba8..2511047e615 100644 --- a/src/core/thread/network_diagnostic.cpp +++ b/src/core/thread/network_diagnostic.cpp @@ -33,8 +33,6 @@ #include "network_diagnostic.hpp" -#if OPENTHREAD_FTD || OPENTHREAD_CONFIG_TMF_NETWORK_DIAG_MTD_ENABLE - #include "coap/coap_message.hpp" #include "common/array.hpp" #include "common/as_core_type.hpp" @@ -58,82 +56,15 @@ RegisterLogModule("NetDiag"); namespace NetworkDiagnostic { -NetworkDiagnostic::NetworkDiagnostic(Instance &aInstance) +//--------------------------------------------------------------------------------------------------------------------- +// Server + +Server::Server(Instance &aInstance) : InstanceLocator(aInstance) { } -Error NetworkDiagnostic::SendDiagnosticGet(const Ip6::Address &aDestination, - const uint8_t aTlvTypes[], - uint8_t aCount, - GetCallback aCallback, - void *aContext) -{ - Error error; - - if (aDestination.IsMulticast()) - { - error = SendCommand(kUriDiagnosticGetQuery, aDestination, aTlvTypes, aCount); - } - else - { - error = SendCommand(kUriDiagnosticGetRequest, aDestination, aTlvTypes, aCount, &HandleGetResponse, this); - } - - SuccessOrExit(error); - - mGetCallback.Set(aCallback, aContext); - -exit: - return error; -} - -Error NetworkDiagnostic::SendCommand(Uri aUri, - const Ip6::Address &aDestination, - const uint8_t aTlvTypes[], - uint8_t aCount, - Coap::ResponseHandler aHandler, - void *aContext) -{ - Error error; - Coap::Message *message = nullptr; - Tmf::MessageInfo messageInfo(GetInstance()); - - switch (aUri) - { - case kUriDiagnosticGetQuery: - message = Get().NewNonConfirmablePostMessage(aUri); - break; - - case kUriDiagnosticGetRequest: - case kUriDiagnosticReset: - message = Get().NewConfirmablePostMessage(aUri); - break; - - default: - OT_ASSERT(false); - } - - VerifyOrExit(message != nullptr, error = kErrorNoBufs); - - if (aCount > 0) - { - SuccessOrExit(error = Tlv::Append(*message, aTlvTypes, aCount)); - } - - PrepareMessageInfoForDest(aDestination, messageInfo); - - SuccessOrExit(error = Get().SendMessage(*message, messageInfo, aHandler, aContext)); - - Log(kMessageSend, aUri, aDestination); - -exit: - FreeMessageOnError(message, error); - return error; -} - -void NetworkDiagnostic::PrepareMessageInfoForDest(const Ip6::Address &aDestination, - Tmf::MessageInfo &aMessageInfo) const +void Server::PrepareMessageInfoForDest(const Ip6::Address &aDestination, Tmf::MessageInfo &aMessageInfo) const { if (aDestination.IsMulticast()) { @@ -152,41 +83,7 @@ void NetworkDiagnostic::PrepareMessageInfoForDest(const Ip6::Address &aDestinati aMessageInfo.SetPeerAddr(aDestination); } -void NetworkDiagnostic::HandleGetResponse(void *aContext, - otMessage *aMessage, - const otMessageInfo *aMessageInfo, - Error aResult) -{ - static_cast(aContext)->HandleGetResponse(AsCoapMessagePtr(aMessage), - AsCoreTypePtr(aMessageInfo), aResult); -} - -void NetworkDiagnostic::HandleGetResponse(Coap::Message *aMessage, const Ip6::MessageInfo *aMessageInfo, Error aResult) -{ - SuccessOrExit(aResult); - VerifyOrExit(aMessage->GetCode() == Coap::kCodeChanged, aResult = kErrorFailed); - -exit: - mGetCallback.InvokeIfSet(aResult, aMessage, aMessageInfo); -} - -template <> -void NetworkDiagnostic::HandleTmf(Coap::Message &aMessage, - const Ip6::MessageInfo &aMessageInfo) -{ - VerifyOrExit(aMessage.IsConfirmablePostRequest()); - - Log(kMessageReceive, kUriDiagnosticGetAnswer, aMessageInfo.GetPeerAddr()); - - mGetCallback.InvokeIfSet(kErrorNone, &aMessage, &aMessageInfo); - - IgnoreError(Get().SendEmptyAck(aMessage, aMessageInfo)); - -exit: - return; -} - -Error NetworkDiagnostic::AppendIp6AddressList(Message &aMessage) +Error Server::AppendIp6AddressList(Message &aMessage) { Error error = kErrorNone; uint16_t count = 0; @@ -224,7 +121,7 @@ Error NetworkDiagnostic::AppendIp6AddressList(Message &aMessage) } #if OPENTHREAD_FTD -Error NetworkDiagnostic::AppendChildTable(Message &aMessage) +Error Server::AppendChildTable(Message &aMessage) { Error error = kErrorNone; uint16_t count; @@ -276,7 +173,7 @@ Error NetworkDiagnostic::AppendChildTable(Message &aMessage) } #endif // OPENTHREAD_FTD -Error NetworkDiagnostic::AppendMacCounters(Message &aMessage) +Error Server::AppendMacCounters(Message &aMessage) { MacCountersTlv tlv; const otMacCounters &counters = Get().GetCounters(); @@ -298,7 +195,7 @@ Error NetworkDiagnostic::AppendMacCounters(Message &aMessage) return tlv.AppendTo(aMessage); } -Error NetworkDiagnostic::AppendRequestedTlvs(const Message &aRequest, Message &aResponse) +Error Server::AppendRequestedTlvs(const Message &aRequest, Message &aResponse) { Error error; uint16_t offset; @@ -320,7 +217,7 @@ Error NetworkDiagnostic::AppendRequestedTlvs(const Message &aRequest, Message &a return error; } -Error NetworkDiagnostic::AppendDiagTlv(uint8_t aTlvType, Message &aMessage) +Error Server::AppendDiagTlv(uint8_t aTlvType, Message &aMessage) { Error error = kErrorNone; @@ -437,14 +334,16 @@ Error NetworkDiagnostic::AppendDiagTlv(uint8_t aTlvType, Message &aMessage) } template <> -void NetworkDiagnostic::HandleTmf(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo) +void Server::HandleTmf(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo) { Error error = kErrorNone; Coap::Message *response = nullptr; Tmf::MessageInfo responseInfo(GetInstance()); VerifyOrExit(aMessage.IsPostRequest(), error = kErrorDrop); - Log(kMessageReceive, kUriDiagnosticGetQuery, aMessageInfo.GetPeerAddr()); + + LogInfo("Received %s from %s", UriToString(), + aMessageInfo.GetPeerAddr().ToString().AsCString()); // DIAG_GET.qry may be sent as a confirmable request. if (aMessage.IsConfirmable()) @@ -465,14 +364,15 @@ void NetworkDiagnostic::HandleTmf(Coap::Message &aMessag } template <> -void NetworkDiagnostic::HandleTmf(Coap::Message &aMessage, - const Ip6::MessageInfo &aMessageInfo) +void Server::HandleTmf(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo) { Error error = kErrorNone; Coap::Message *response = nullptr; VerifyOrExit(aMessage.IsConfirmablePostRequest(), error = kErrorDrop); - Log(kMessageReceive, kUriDiagnosticGetRequest, aMessageInfo.GetPeerAddr()); + + LogInfo("Received %s from %s", UriToString(), + aMessageInfo.GetPeerAddr().ToString().AsCString()); response = Get().NewResponseMessage(aMessage); VerifyOrExit(response != nullptr, error = kErrorNoBufs); @@ -484,22 +384,16 @@ void NetworkDiagnostic::HandleTmf(Coap::Message FreeMessageOnError(response, error); } -Error NetworkDiagnostic::SendDiagnosticReset(const Ip6::Address &aDestination, - const uint8_t aTlvTypes[], - uint8_t aCount) -{ - return SendCommand(kUriDiagnosticReset, aDestination, aTlvTypes, aCount); -} - -template <> -void NetworkDiagnostic::HandleTmf(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo) +template <> void Server::HandleTmf(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo) { uint16_t offset = 0; uint8_t type; Tlv tlv; VerifyOrExit(aMessage.IsConfirmablePostRequest()); - Log(kMessageReceive, kUriDiagnosticReset, aMessageInfo.GetPeerAddr()); + + LogInfo("Received %s from %s", UriToString(), + aMessageInfo.GetPeerAddr().ToString().AsCString()); SuccessOrExit(aMessage.Read(aMessage.GetOffset(), tlv)); @@ -528,6 +422,121 @@ void NetworkDiagnostic::HandleTmf(Coap::Message &aMessage, return; } +#if OPENTHREAD_CONFIG_TMF_NETDIAG_CLIENT_ENABLE + +//--------------------------------------------------------------------------------------------------------------------- +// Client + +Client::Client(Instance &aInstance) + : InstanceLocator(aInstance) +{ +} + +Error Client::SendDiagnosticGet(const Ip6::Address &aDestination, + const uint8_t aTlvTypes[], + uint8_t aCount, + GetCallback aCallback, + void *aContext) +{ + Error error; + + if (aDestination.IsMulticast()) + { + error = SendCommand(kUriDiagnosticGetQuery, aDestination, aTlvTypes, aCount); + } + else + { + error = SendCommand(kUriDiagnosticGetRequest, aDestination, aTlvTypes, aCount, &HandleGetResponse, this); + } + + SuccessOrExit(error); + + mGetCallback.Set(aCallback, aContext); + +exit: + return error; +} + +Error Client::SendCommand(Uri aUri, + const Ip6::Address &aDestination, + const uint8_t aTlvTypes[], + uint8_t aCount, + Coap::ResponseHandler aHandler, + void *aContext) +{ + Error error; + Coap::Message *message = nullptr; + Tmf::MessageInfo messageInfo(GetInstance()); + + switch (aUri) + { + case kUriDiagnosticGetQuery: + message = Get().NewNonConfirmablePostMessage(aUri); + break; + + case kUriDiagnosticGetRequest: + case kUriDiagnosticReset: + message = Get().NewConfirmablePostMessage(aUri); + break; + + default: + OT_ASSERT(false); + } + + VerifyOrExit(message != nullptr, error = kErrorNoBufs); + + if (aCount > 0) + { + SuccessOrExit(error = Tlv::Append(*message, aTlvTypes, aCount)); + } + + Get().PrepareMessageInfoForDest(aDestination, messageInfo); + + SuccessOrExit(error = Get().SendMessage(*message, messageInfo, aHandler, aContext)); + + LogInfo("Sent %s to %s", UriToString(aUri), aDestination.ToString().AsCString()); + +exit: + FreeMessageOnError(message, error); + return error; +} + +void Client::HandleGetResponse(void *aContext, otMessage *aMessage, const otMessageInfo *aMessageInfo, Error aResult) +{ + static_cast(aContext)->HandleGetResponse(AsCoapMessagePtr(aMessage), AsCoreTypePtr(aMessageInfo), + aResult); +} + +void Client::HandleGetResponse(Coap::Message *aMessage, const Ip6::MessageInfo *aMessageInfo, Error aResult) +{ + SuccessOrExit(aResult); + VerifyOrExit(aMessage->GetCode() == Coap::kCodeChanged, aResult = kErrorFailed); + +exit: + mGetCallback.InvokeIfSet(aResult, aMessage, aMessageInfo); +} + +template <> +void Client::HandleTmf(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo) +{ + VerifyOrExit(aMessage.IsConfirmablePostRequest()); + + LogInfo("Received %s from %s", ot::UriToString(), + aMessageInfo.GetPeerAddr().ToString().AsCString()); + + mGetCallback.InvokeIfSet(kErrorNone, &aMessage, &aMessageInfo); + + IgnoreError(Get().SendEmptyAck(aMessage, aMessageInfo)); + +exit: + return; +} + +Error Client::SendDiagnosticReset(const Ip6::Address &aDestination, const uint8_t aTlvTypes[], uint8_t aCount) +{ + return SendCommand(kUriDiagnosticReset, aDestination, aTlvTypes, aCount); +} + static void ParseRoute(const RouteTlv &aRouteTlv, otNetworkDiagRoute &aNetworkDiagRoute) { uint8_t routeCount = 0; @@ -561,7 +570,7 @@ static inline void ParseMacCounters(const MacCountersTlv &aMacCountersTlv, otNet aMacCounters.mIfOutDiscards = aMacCountersTlv.GetIfOutDiscards(); } -Error NetworkDiagnostic::GetNextDiagTlv(const Coap::Message &aMessage, Iterator &aIterator, TlvInfo &aTlvInfo) +Error Client::GetNextDiagTlv(const Coap::Message &aMessage, Iterator &aIterator, TlvInfo &aTlvInfo) { Error error = kErrorNotFound; uint16_t offset = (aIterator == 0) ? aMessage.GetOffset() : aIterator; @@ -775,7 +784,7 @@ Error NetworkDiagnostic::GetNextDiagTlv(const Coap::Message &aMessage, Iterator #if OT_SHOULD_LOG_AT(OT_LOG_LEVEL_INFO) -const char *NetworkDiagnostic::UriToString(Uri aUri) +const char *Client::UriToString(Uri aUri) { const char *str = ""; @@ -790,9 +799,6 @@ const char *NetworkDiagnostic::UriToString(Uri aUri) case kUriDiagnosticReset: str = ot::UriToString(); break; - case kUriDiagnosticGetAnswer: - str = ot::UriToString(); - break; default: break; } @@ -800,29 +806,10 @@ const char *NetworkDiagnostic::UriToString(Uri aUri) return str; } -void NetworkDiagnostic::Log(Action aAction, Uri aUri, const Ip6::Address &aIp6Address) const -{ - static const char *const kActionStrings[] = { - "Sent", // (0) kMessageSend - "Received", // (1) kMessageReceive - }; - - static const char *const kActionPrepositionStrings[] = { - "to", // (0) kMessageSend - "from", // (1) kMessageReceive - }; - - static_assert(kMessageSend == 0, "kMessageSend value is incorrect"); - static_assert(kMessageReceive == 1, "kMessageReceive value is incorrect"); - - LogInfo("%s %s %s %s", kActionStrings[aAction], UriToString(aUri), kActionPrepositionStrings[aAction], - aIp6Address.ToString().AsCString()); -} - #endif // #if OT_SHOULD_LOG_AT(OT_LOG_LEVEL_INFO) +#endif // OPENTHREAD_CONFIG_TMF_NETDIAG_CLIENT_ENABLE + } // namespace NetworkDiagnostic } // namespace ot - -#endif // OPENTHREAD_FTD || OPENTHREAD_CONFIG_TMF_NETWORK_DIAG_MTD_ENABLE diff --git a/src/core/thread/network_diagnostic.hpp b/src/core/thread/network_diagnostic.hpp index 40259443d35..2521418e4a8 100644 --- a/src/core/thread/network_diagnostic.hpp +++ b/src/core/thread/network_diagnostic.hpp @@ -36,8 +36,6 @@ #include "openthread-core-config.h" -#if OPENTHREAD_FTD || OPENTHREAD_CONFIG_TMF_NETWORK_DIAG_MTD_ENABLE - #include #include "common/callback.hpp" @@ -61,11 +59,50 @@ namespace NetworkDiagnostic { * @{ */ +class Client; + +/** + * This class implements the Network Diagnostic server responding to requests. + * + */ +class Server : public InstanceLocator, private NonCopyable +{ + friend class Tmf::Agent; + friend class Client; + +public: + /** + * This constructor initializes the Server. + * + * @param[in] aInstance The OpenThread instance. + * + */ + explicit Server(Instance &aInstance); + +private: + static constexpr uint16_t kMaxChildEntries = 398; + + Error AppendDiagTlv(uint8_t aTlvType, Message &aMessage); + Error AppendIp6AddressList(Message &aMessage); + Error AppendMacCounters(Message &aMessage); + Error AppendChildTable(Message &aMessage); + Error AppendRequestedTlvs(const Message &aRequest, Message &aResponse); + void PrepareMessageInfoForDest(const Ip6::Address &aDestination, Tmf::MessageInfo &aMessageInfo) const; + + template void HandleTmf(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo); +}; + +DeclareTmfHandler(Server, kUriDiagnosticGetRequest); +DeclareTmfHandler(Server, kUriDiagnosticGetQuery); +DeclareTmfHandler(Server, kUriDiagnosticGetAnswer); + +#if OPENTHREAD_CONFIG_TMF_NETDIAG_CLIENT_ENABLE + /** - * This class implements the Network Diagnostic processing. + * This class implements the Network Diagnostic client sending requests and queries. * */ -class NetworkDiagnostic : public InstanceLocator, private NonCopyable +class Client : public InstanceLocator, private NonCopyable { friend class Tmf::Agent; @@ -78,10 +115,12 @@ class NetworkDiagnostic : public InstanceLocator, private NonCopyable static constexpr Iterator kIteratorInit = OT_NETWORK_DIAGNOSTIC_ITERATOR_INIT; ///< Initializer for Iterator. /** - * This constructor initializes the object. + * This constructor initializes the Client. + * + * @param[in] aInstance The OpenThread instance. * */ - explicit NetworkDiagnostic(Instance &aInstance); + explicit Client(Instance &aInstance); /** * This method sends Diagnostic Get request. If the @p aDestination is of multicast type, the DIAG_GET.qry @@ -127,12 +166,6 @@ class NetworkDiagnostic : public InstanceLocator, private NonCopyable private: static constexpr uint16_t kMaxChildEntries = 398; - enum Action : uint8_t - { - kMessageSend, - kMessageReceive, - }; - Error SendCommand(Uri aUri, const Ip6::Address &aDestination, const uint8_t aTlvTypes[], @@ -140,13 +173,6 @@ class NetworkDiagnostic : public InstanceLocator, private NonCopyable Coap::ResponseHandler aHandler = nullptr, void *aContext = nullptr); - Error AppendDiagTlv(uint8_t aTlvType, Message &aMessage); - Error AppendIp6AddressList(Message &aMessage); - Error AppendMacCounters(Message &aMessage); - Error AppendChildTable(Message &aMessage); - Error AppendRequestedTlvs(const Message &aRequest, Message &aResponse); - void PrepareMessageInfoForDest(const Ip6::Address &aDestination, Tmf::MessageInfo &aMessageInfo) const; - static void HandleGetResponse(void *aContext, otMessage *aMessage, const otMessageInfo *aMessageInfo, @@ -157,18 +183,14 @@ class NetworkDiagnostic : public InstanceLocator, private NonCopyable #if OT_SHOULD_LOG_AT(OT_LOG_LEVEL_INFO) static const char *UriToString(Uri aUri); - void Log(Action aAction, Uri aUri, const Ip6::Address &aIp6Address) const; -#else - void Log(Action, Uri, const Ip6::Address &) const {} #endif Callback mGetCallback; }; -DeclareTmfHandler(NetworkDiagnostic, kUriDiagnosticGetRequest); -DeclareTmfHandler(NetworkDiagnostic, kUriDiagnosticGetQuery); -DeclareTmfHandler(NetworkDiagnostic, kUriDiagnosticGetAnswer); -DeclareTmfHandler(NetworkDiagnostic, kUriDiagnosticReset); +DeclareTmfHandler(Client, kUriDiagnosticReset); + +#endif // OPENTHREAD_CONFIG_TMF_NETDIAG_CLIENT_ENABLE /** * @} @@ -177,6 +199,4 @@ DeclareTmfHandler(NetworkDiagnostic, kUriDiagnosticReset); } // namespace ot -#endif // OPENTHREAD_FTD || OPENTHREAD_CONFIG_TMF_NETWORK_DIAG_MTD_ENABLE - #endif // NETWORK_DIAGNOSTIC_HPP_ diff --git a/src/core/thread/tmf.cpp b/src/core/thread/tmf.cpp index fecd978537b..583f6e29d7e 100644 --- a/src/core/thread/tmf.cpp +++ b/src/core/thread/tmf.cpp @@ -165,11 +165,12 @@ bool Agent::HandleResource(const char *aUriPath, Message &aMessage, const Ip6::M Case(kUriAnycastLocate, AnycastLocator); #endif -#if OPENTHREAD_FTD || OPENTHREAD_CONFIG_TMF_NETWORK_DIAG_MTD_ENABLE - Case(kUriDiagnosticGetRequest, NetworkDiagnostic::NetworkDiagnostic); - Case(kUriDiagnosticGetQuery, NetworkDiagnostic::NetworkDiagnostic); - Case(kUriDiagnosticGetAnswer, NetworkDiagnostic::NetworkDiagnostic); - Case(kUriDiagnosticReset, NetworkDiagnostic::NetworkDiagnostic); + Case(kUriDiagnosticGetRequest, NetworkDiagnostic::Server); + Case(kUriDiagnosticGetQuery, NetworkDiagnostic::Server); + Case(kUriDiagnosticReset, NetworkDiagnostic::Server); + +#if OPENTHREAD_CONFIG_TMF_NETDIAG_CLIENT_ENABLE + Case(kUriDiagnosticGetAnswer, NetworkDiagnostic::Client); #endif #if OPENTHREAD_FTD && OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE diff --git a/src/posix/Makefile-posix b/src/posix/Makefile-posix index 5ddc67b4ed5..7fe9722de56 100644 --- a/src/posix/Makefile-posix +++ b/src/posix/Makefile-posix @@ -61,9 +61,9 @@ LINK_RAW ?= 0 LOG_OUTPUT ?= PLATFORM_DEFINED MAC_FILTER ?= 1 MAX_POWER_TABLE ?= 1 -MTD_NETDIAG ?= 1 NEIGHBOR_DISCOVERY_AGENT ?= 1 NETDATA_PUBLISHER ?= 1 +NETDIAG_CLIENT ?= 1 PING_SENDER ?= 1 READLINE ?= readline REFERENCE_DEVICE ?= 1 diff --git a/tests/fuzz/oss-fuzz-build b/tests/fuzz/oss-fuzz-build index 60782f6c3f9..c8bb40789b7 100755 --- a/tests/fuzz/oss-fuzz-build +++ b/tests/fuzz/oss-fuzz-build @@ -62,8 +62,8 @@ set -euxo pipefail -DOT_LINK_RAW=ON \ -DOT_LOG_OUTPUT=APP \ -DOT_MAC_FILTER=ON \ - -DOT_MTD_NETDIAG=ON \ -DOT_NETDATA_PUBLISHER=ON \ + -DOT_NETDIAG_CLIENT=ON \ -DOT_PING_SENDER=ON \ -DOT_SERVICE=ON \ -DOT_SLAAC=ON \