From 7d008f6c7659e48b9b91a805f44f990f1aab33bc Mon Sep 17 00:00:00 2001 From: Abtin Keshavarzian Date: Wed, 14 Apr 2021 00:45:18 -0700 Subject: [PATCH] [dns-client] add support for NAT64 during address resolution (#6414) This commit adds support for NAT64 address translation (from IPv4 to IPv6) during address resolution in `Dns::Client`. This feature is enabled using `OPENTHREAD_CONFIG_DNS_CLIENT_NAT64_ENABLE` config option. When enabled, the caller can specify in the address query configuration whether to allow or disallow NAT64 address translation for a specific query. The default behavior can also be set using the default DNS client config. When the NAT64 is allowed, if the response from server does not provide an IPv6 address for the host name, we first check if the server has provided IPv4 address information in the response, and if not, start a new query for the IPv4 address. Once the IPv4 address(es) of the host name are resolved, they are translated to IPv6 address(es) and reported to the user in the query callback function. --- include/openthread/dns_client.h | 15 ++ include/openthread/instance.h | 2 +- src/core/config/dns_client.h | 22 +++ src/core/net/dns_client.cpp | 249 +++++++++++++++++++++++++++----- src/core/net/dns_client.hpp | 71 ++++++++- src/core/net/ip6_address.cpp | 2 +- src/core/net/ip6_address.hpp | 2 +- tests/unit/test_ip_address.cpp | 4 +- 8 files changed, 316 insertions(+), 51 deletions(-) diff --git a/include/openthread/dns_client.h b/include/openthread/dns_client.h index 11c3ec517..b24317d0d 100644 --- a/include/openthread/dns_client.h +++ b/include/openthread/dns_client.h @@ -66,6 +66,20 @@ typedef enum OT_DNS_FLAG_NO_RECURSION = 2, ///< Indicates DNS name server can not resolve the query recursively. } otDnsRecursionFlag; +/** + * This enumeration type represents the NAT64 mode in an `otDnsQueryConfig`. + * + * The NAT64 mode indicates whether to allow or disallow NAT64 address translation during DNS client address resolution. + * This mode is only used when `OPENTHREAD_CONFIG_DNS_CLIENT_NAT64_ENABLE` is enabled. + * + */ +typedef enum +{ + OT_DNS_NAT64_UNSPECIFIED = 0, ///< NAT64 mode is not specified. Use default NAT64 mode. + OT_DNS_NAT64_ALLOW = 1, ///< Allow NAT64 address translation during DNS client address resolution. + OT_DNS_NAT64_DISALLOW = 2, ///< Do not allow NAT64 address translation during DNS client address resolution. +} otDnsNat64Mode; + /** * This structure represents a DNS query configuration. * @@ -79,6 +93,7 @@ typedef struct otDnsQueryConfig 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. } otDnsQueryConfig; /** diff --git a/include/openthread/instance.h b/include/openthread/instance.h index 3f2595018..35438f424 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 (98) +#define OPENTHREAD_API_VERSION (99) /** * @addtogroup api-instance diff --git a/src/core/config/dns_client.h b/src/core/config/dns_client.h index e7c4d0868..817d7ab25 100644 --- a/src/core/config/dns_client.h +++ b/src/core/config/dns_client.h @@ -45,6 +45,28 @@ #define OPENTHREAD_CONFIG_DNS_CLIENT_ENABLE 0 #endif +/** + * @def OPENTHREAD_CONFIG_DNS_CLIENT_NAT64_ENABLE + * + * Define to 1 to enable support for NAT64 address translation (from IPv4 to IPv6) during address resolution by DNS + * client. + * + */ +#ifndef OPENTHREAD_CONFIG_DNS_CLIENT_NAT64_ENABLE +#define OPENTHREAD_CONFIG_DNS_CLIENT_NAT64_ENABLE 1 +#endif + +/** + * @def OPENTHREAD_CONFIG_DNS_CLIENT_DEFAULT_NAT64_ALLOWED + * + * Specifies the default NAT64 mode, i.e., whether to allow or disallow NAT64 address translation during DNS client + * address resolution. This mode is only available when `OPENTHREAD_CONFIG_DNS_CLIENT_NAT64_ENABLE` is enabled. + * + */ +#ifndef OPENTHREAD_CONFIG_DNS_CLIENT_DEFAULT_NAT64_ALLOWED +#define OPENTHREAD_CONFIG_DNS_CLIENT_DEFAULT_NAT64_ALLOWED 1 +#endif + /** * @def OPENTHREAD_CONFIG_DNS_CLIENT_SERVICE_DISCOVERY_ENABLE * diff --git a/src/core/net/dns_client.cpp b/src/core/net/dns_client.cpp index 6e5e8d11b..28e3d0e46 100644 --- a/src/core/net/dns_client.cpp +++ b/src/core/net/dns_client.cpp @@ -60,6 +60,9 @@ Client::QueryConfig::QueryConfig(InitMode aMode) SetResponseTimeout(kDefaultResponseTimeout); SetMaxTxAttempts(kDefaultMaxTxAttempts); SetRecursionFlag(kDefaultRecursionDesired ? kFlagRecursionDesired : kFlagNoRecursion); +#if OPENTHREAD_CONFIG_DNS_CLIENT_NAT64_ENABLE + SetNat64Mode(kDefaultNat64Allowed ? kNat64Allow : kNat64Disallow); +#endif } void Client::QueryConfig::SetFrom(const QueryConfig &aConfig, const QueryConfig &aDefaultConfig) @@ -94,6 +97,13 @@ void Client::QueryConfig::SetFrom(const QueryConfig &aConfig, const QueryConfig { SetRecursionFlag(aDefaultConfig.GetRecursionFlag()); } + +#if OPENTHREAD_CONFIG_DNS_CLIENT_NAT64_ENABLE + if (GetNat64Mode() == kNat64Unspecified) + { + SetNat64Mode(aDefaultConfig.GetNat64Mode()); + } +#endif } //--------------------------------------------------------------------------------------------------------------------- @@ -122,37 +132,57 @@ Error Client::Response::GetName(char *aNameBuffer, uint16_t aNameBufferSize) con return Name::ReadName(*mQuery, offset, aNameBuffer, aNameBufferSize); } +Error Client::Response::CheckForHostNameAlias(Section aSection, Name &aHostName) const +{ + // If the response includes a CNAME record mapping the query host + // name to a canonical name, we update `aHostName` to the new alias + // name. Otherwise `aHostName` remains as before. + + Error error; + uint16_t offset; + uint16_t numRecords; + CnameRecord cnameRecord; + + VerifyOrExit(mMessage != nullptr, error = kErrorNotFound); + + SelectSection(aSection, offset, numRecords); + error = ResourceRecord::FindRecord(*mMessage, offset, numRecords, /* aIndex */ 0, aHostName, cnameRecord); + + switch (error) + { + case kErrorNone: + // A CNAME record was found. `offset` now points to after the + // last read byte within the `mMessage` into the `cnameRecord` + // (which is the start of the new canonical name). + aHostName.SetFromMessage(*mMessage, offset); + error = Name::ParseName(*mMessage, offset); + break; + + case kErrorNotFound: + error = kErrorNone; + break; + + default: + break; + } + +exit: + return error; +} + Error Client::Response::FindHostAddress(Section aSection, const Name & aHostName, uint16_t aIndex, Ip6::Address &aAddress, uint32_t & aTtl) const { - Error error; - uint16_t offset; - uint16_t numRecords; - Name name = aHostName; - CnameRecord cnameRecord; - AaaaRecord aaaaRecord; + Error error; + uint16_t offset; + uint16_t numRecords; + Name name = aHostName; + AaaaRecord aaaaRecord; - VerifyOrExit(mMessage != nullptr, error = kErrorNotFound); - - // If the response includes a CNAME record mapping the query host - // name to a canonical name, we then search for AAAA records - // matching the canonical name. - - SelectSection(aSection, offset, numRecords); - error = ResourceRecord::FindRecord(*mMessage, offset, numRecords, /* aIndex */ 0, aHostName, cnameRecord); - - if (error == kErrorNone) - { - name.SetFromMessage(*mMessage, offset); - SuccessOrExit(error = Name::ParseName(*mMessage, offset)); - } - else - { - VerifyOrExit(error == kErrorNotFound); - } + SuccessOrExit(error = CheckForHostNameAlias(aSection, name)); SelectSection(aSection, offset, numRecords); SuccessOrExit(error = ResourceRecord::FindRecord(*mMessage, offset, numRecords, aIndex, name, aaaaRecord)); @@ -163,6 +193,26 @@ exit: return error; } +#if OPENTHREAD_CONFIG_DNS_CLIENT_NAT64_ENABLE + +Error Client::Response::FindARecord(Section aSection, const Name &aHostName, uint16_t aIndex, ARecord &aARecord) const +{ + Error error; + uint16_t offset; + uint16_t numRecords; + Name name = aHostName; + + SuccessOrExit(error = CheckForHostNameAlias(aSection, name)); + + SelectSection(aSection, offset, numRecords); + error = ResourceRecord::FindRecord(*mMessage, offset, numRecords, aIndex, name, aARecord); + +exit: + return error; +} + +#endif // OPENTHREAD_CONFIG_DNS_CLIENT_NAT64_ENABLE + #if OPENTHREAD_CONFIG_DNS_CLIENT_SERVICE_DISCOVERY_ENABLE Error Client::Response::FindServiceInfo(Section aSection, const Name &aName, ServiceInfo &aServiceInfo) const @@ -257,9 +307,60 @@ exit: Error Client::AddressResponse::GetAddress(uint16_t aIndex, Ip6::Address &aAddress, uint32_t &aTtl) const { - return FindHostAddress(kAnswerSection, Name(*mQuery, kNameOffsetInQuery), aIndex, aAddress, aTtl); + Error error = kErrorNone; + Name name(*mQuery, kNameOffsetInQuery); + +#if OPENTHREAD_CONFIG_DNS_CLIENT_NAT64_ENABLE + + // If the response is for an IPv4 address query or if it is an + // IPv6 address query response with no IPv6 address but with + // an IPv4 in its additional section, we read the IPv4 address + // and translate it to an IPv6 address. + + QueryInfo info; + + info.ReadFrom(*mQuery); + + if ((info.mQueryType == kIp4AddressQuery) || mIp6QueryResponseRequiresNat64) + { + Section section; + ARecord aRecord; + Ip6::Prefix nat64Prefix; + + SuccessOrExit(error = GetNat64Prefix(nat64Prefix)); + + section = (info.mQueryType == kIp4AddressQuery) ? kAnswerSection : kAdditionalDataSection; + SuccessOrExit(error = FindARecord(section, name, aIndex, aRecord)); + + aAddress.SynthesizeFromIp4Address(nat64Prefix, aRecord.GetAddress()); + aTtl = aRecord.GetTtl(); + + ExitNow(); + } + +#endif // OPENTHREAD_CONFIG_DNS_CLIENT_NAT64_ENABLE + + ExitNow(error = FindHostAddress(kAnswerSection, name, aIndex, aAddress, aTtl)); + +exit: + return error; } +#if OPENTHREAD_CONFIG_DNS_CLIENT_NAT64_ENABLE + +Error Client::AddressResponse::GetNat64Prefix(Ip6::Prefix &aPrefix) const +{ + // Use well-known prefix "64:ff9b::/96" (temporary solution). + static const uint8_t kWellknownPrefix[] = {0x00, 0x64, 0xff, 0x9b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + + aPrefix.Clear(); + aPrefix.Set(kWellknownPrefix, 96); + + return kErrorNone; +} + +#endif // OPENTHREAD_CONFIG_DNS_CLIENT_NAT64_ENABLE + #if OPENTHREAD_CONFIG_DNS_CLIENT_SERVICE_DISCOVERY_ENABLE //--------------------------------------------------------------------------------------------------------------------- @@ -410,25 +511,34 @@ Error Client::ServiceResponse::GetHostAddress(const char * aHostName, //--------------------------------------------------------------------------------------------------------------------- // Client -const uint16_t Client::kAddressQueryRecordTypes[] = {ResourceRecord::kTypeAaaa}; +const uint16_t Client::kIp6AddressQueryRecordTypes[] = {ResourceRecord::kTypeAaaa}; +#if OPENTHREAD_CONFIG_DNS_CLIENT_NAT64_ENABLE +const uint16_t Client::kIp4AddressQueryRecordTypes[] = {ResourceRecord::kTypeA}; +#endif #if OPENTHREAD_CONFIG_DNS_CLIENT_SERVICE_DISCOVERY_ENABLE const uint16_t Client::kBrowseQueryRecordTypes[] = {ResourceRecord::kTypePtr}; const uint16_t Client::kServiceQueryRecordTypes[] = {ResourceRecord::kTypeSrv, ResourceRecord::kTypeTxt}; #endif const uint8_t Client::kQuestionCount[] = { - /* (0) kAddressQuery -> */ OT_ARRAY_LENGTH(kAddressQueryRecordTypes), // AAAA records + /* kIp6AddressQuery -> */ OT_ARRAY_LENGTH(kIp6AddressQueryRecordTypes), // AAAA records +#if OPENTHREAD_CONFIG_DNS_CLIENT_NAT64_ENABLE + /* kIp4AddressQuery -> */ OT_ARRAY_LENGTH(kIp4AddressQueryRecordTypes), // A records +#endif #if OPENTHREAD_CONFIG_DNS_CLIENT_SERVICE_DISCOVERY_ENABLE - /* (1) kBrowseQuery -> */ OT_ARRAY_LENGTH(kBrowseQueryRecordTypes), // PTR records - /* (2) kServiceQuery -> */ OT_ARRAY_LENGTH(kServiceQueryRecordTypes), // SRV and TXT records + /* kBrowseQuery -> */ OT_ARRAY_LENGTH(kBrowseQueryRecordTypes), // PTR records + /* kServiceQuery -> */ OT_ARRAY_LENGTH(kServiceQueryRecordTypes), // SRV and TXT records #endif }; const uint16_t *Client::kQuestionRecordTypes[] = { - /* (0) kAddressQuery -> */ kAddressQueryRecordTypes, + /* kIp6AddressQuery -> */ kIp6AddressQueryRecordTypes, +#if OPENTHREAD_CONFIG_DNS_CLIENT_NAT64_ENABLE + /* kIp4AddressQuery -> */ kIp4AddressQueryRecordTypes, +#endif #if OPENTHREAD_CONFIG_DNS_CLIENT_SERVICE_DISCOVERY_ENABLE - /* (1) kBrowseQuery -> */ kBrowseQueryRecordTypes, - /* (2) kServiceQuery -> */ kServiceQueryRecordTypes, + /* kBrowseQuery -> */ kBrowseQueryRecordTypes, + /* kServiceQuery -> */ kServiceQueryRecordTypes, #endif }; @@ -438,8 +548,14 @@ Client::Client(Instance &aInstance) , mTimer(aInstance, Client::HandleTimer) , mDefaultConfig(QueryConfig::kInitFromDefaults) { - static_assert(kAddressQuery == 0, "kAddressQuery value is not correct"); + static_assert(kIp6AddressQuery == 0, "kIp6AddressQuery value is not correct"); +#if OPENTHREAD_CONFIG_DNS_CLIENT_NAT64_ENABLE + 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"); +#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"); #endif @@ -488,7 +604,7 @@ Error Client::ResolveAddress(const char * aHostName, QueryInfo info; info.Clear(); - info.mQueryType = kAddressQuery; + info.mQueryType = kIp6AddressQuery; info.mCallback.mAddressCallback = aCallback; return StartQuery(info, aConfig, nullptr, aHostName, aContext); @@ -694,7 +810,8 @@ void Client::FinalizeQuery(Query &aQuery, Error aError) Response response; QueryInfo info; - response.mQuery = &aQuery; + response.mInstance = &Get(); + response.mQuery = &aQuery; info.ReadFrom(aQuery); FinalizeQuery(response, info.mQueryType, aError); @@ -709,7 +826,10 @@ void Client::FinalizeQuery(Response &aResponse, QueryType aType, Error aError) switch (aType) { - case kAddressQuery: + case kIp6AddressQuery: +#if OPENTHREAD_CONFIG_DNS_CLIENT_NAT64_ENABLE + case kIp4AddressQuery: +#endif if (callback.mAddressCallback != nullptr) { callback.mAddressCallback(aError, &aResponse, context); @@ -777,7 +897,8 @@ void Client::ProcessResponse(const Message &aMessage) QueryType type; Error responseError; - response.mMessage = &aMessage; + response.mInstance = &Get(); + response.mMessage = &aMessage; // We intentionally parse the response in a separate method // `ParseResponse()` to free all the stack allocated variables @@ -798,6 +919,7 @@ Error Client::ParseResponse(Response &aResponse, QueryType &aType, Error &aRespo uint16_t offset = message.GetOffset(); Header header; QueryInfo info; + Name queryName; SuccessOrExit(error = message.Read(offset, header)); offset += sizeof(Header); @@ -812,14 +934,15 @@ Error Client::ParseResponse(Response &aResponse, QueryType &aType, Error &aRespo info.ReadFrom(*aResponse.mQuery); aType = info.mQueryType; + queryName.SetFromMessage(*aResponse.mQuery, kNameOffsetInQuery); + // Check the Question Section VerifyOrExit(header.GetQuestionCount() == kQuestionCount[aType], error = kErrorParse); for (uint8_t num = 0; num < kQuestionCount[aType]; num++) { - // The name is encoded after `Info` struct in `query`. - SuccessOrExit(error = Name::CompareName(message, offset, *aResponse.mQuery, kNameOffsetInQuery)); + SuccessOrExit(error = Name::CompareName(message, offset, queryName)); offset += sizeof(Question); } @@ -838,6 +961,54 @@ Error Client::ParseResponse(Response &aResponse, QueryType &aType, Error &aRespo aResponseError = Header::ResponseCodeToError(header.GetResponseCode()); +#if OPENTHREAD_CONFIG_DNS_CLIENT_NAT64_ENABLE + + if (aType == kIp6AddressQuery) + { + Ip6::Address ip6ddress; + uint32_t ttl; + ARecord aRecord; + + // 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. + + VerifyOrExit(aResponse.FindHostAddress(Response::kAnswerSection, queryName, /* aIndex */ 0, ip6ddress, ttl) != + kErrorNone); + VerifyOrExit(info.mConfig.GetNat64Mode() == QueryConfig::kNat64Allow); + + // First, we check if the response already contains an A record + // (IPv4 address) for the query name. + + if (aResponse.FindARecord(Response::kAdditionalDataSection, queryName, /* aIndex */ 0, aRecord) == kErrorNone) + { + aResponse.mIp6QueryResponseRequiresNat64 = true; + aResponseError = kErrorNone; + 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`. + + info.mQueryType = kIp4AddressQuery; + info.mMessageId = 0; + info.mTransmissionCount = 0; + + SendQuery(*aResponse.mQuery, info, /* aUpdateTimer */ true); + + error = kErrorPending; + } + +#endif // OPENTHREAD_CONFIG_DNS_CLIENT_NAT64_ENABLE + exit: if (error != kErrorNone) { diff --git a/src/core/net/dns_client.hpp b/src/core/net/dns_client.hpp index e44a8f9af..b0a9b7c29 100644 --- a/src/core/net/dns_client.hpp +++ b/src/core/net/dns_client.hpp @@ -108,6 +108,19 @@ public: kFlagNoRecursion = OT_DNS_FLAG_NO_RECURSION, ///< Server can not resolve the query recursively. }; +#if OPENTHREAD_CONFIG_DNS_CLIENT_NAT64_ENABLE + /** + * This enumeration type represents the NAT64 mode. + * + */ + enum Nat64Mode + { + kNat64Unspecified = OT_DNS_NAT64_UNSPECIFIED, ///< NAT64 mode is not specified. Use default NAT64 mode. + kNat64Allow = OT_DNS_NAT64_ALLOW, ///< Allow NAT64 address translation + kNat64Disallow = OT_DNS_NAT64_DISALLOW, ///< Disallow NAT64 address translation. + }; +#endif + /** * This is the default constructor for `QueryConfig` object. * @@ -149,6 +162,16 @@ public: */ RecursionFlag GetRecursionFlag(void) const { return static_cast(mRecursionFlag); } +#if OPENTHREAD_CONFIG_DNS_CLIENT_NAT64_ENABLE + /** + * This method gets the NAT64 mode. + * + * @returns The NAT64 mode. + * + */ + Nat64Mode GetNat64Mode(void) const { return static_cast(mNat64Mode); } +#endif + private: enum : uint32_t { @@ -170,6 +193,13 @@ public: kDefaultRecursionDesired = OPENTHREAD_CONFIG_DNS_CLIENT_DEFAULT_RECURSION_DESIRED_FLAG, }; +#if OPENTHREAD_CONFIG_DNS_CLIENT_NAT64_ENABLE + enum : bool + { + kDefaultNat64Allowed = OPENTHREAD_CONFIG_DNS_CLIENT_DEFAULT_NAT64_ALLOWED, + }; +#endif + enum InitMode : uint8_t { kInitFromDefaults, @@ -184,6 +214,9 @@ public: void SetResponseTimeout(uint32_t aResponseTimeout) { mResponseTimeout = aResponseTimeout; } void SetMaxTxAttempts(uint8_t aMaxTxAttempts) { mMaxTxAttempts = aMaxTxAttempts; } void SetRecursionFlag(RecursionFlag aFlag) { mRecursionFlag = static_cast(aFlag); } +#if OPENTHREAD_CONFIG_DNS_CLIENT_NAT64_ENABLE + void SetNat64Mode(Nat64Mode aMode) { mNat64Mode = static_cast(aMode); } +#endif void SetFrom(const QueryConfig &aConfig, const QueryConfig &aDefaultConfig); }; @@ -200,14 +233,12 @@ public: * This class represents a DNS query response. * */ - class Response : public Clearable, - public otDnsAddressResponse + class Response : public otDnsAddressResponse, #if OPENTHREAD_CONFIG_DNS_CLIENT_SERVICE_DISCOVERY_ENABLE - , public otDnsBrowseResponse, - public otDnsServiceResponse + public otDnsServiceResponse, #endif - + public Clearable { friend class Client; @@ -222,22 +253,34 @@ public: Error GetName(char *aNameBuffer, uint16_t aNameBufferSize) const; void SelectSection(Section aSection, uint16_t &aOffset, uint16_t &aNumRecord) const; + Error CheckForHostNameAlias(Section aSection, Name &aHostName) const; Error FindHostAddress(Section aSection, const Name & aHostName, uint16_t aIndex, Ip6::Address &aAddress, uint32_t & aTtl) const; +#if OPENTHREAD_CONFIG_DNS_CLIENT_NAT64_ENABLE + Error FindARecord(Section aSection, const Name &aHostName, uint16_t aIndex, ARecord &aARecord) const; +#endif #if OPENTHREAD_CONFIG_DNS_CLIENT_SERVICE_DISCOVERY_ENABLE Error FindServiceInfo(Section aSection, const Name &aName, ServiceInfo &aServiceInfo) const; #endif + Instance * mInstance; // The OpenThread instance. Query * mQuery; // The associated query. const Message *mMessage; // The response message. 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`. uint16_t mAdditionalRecordCount; // Number of records in additional data section. +#if OPENTHREAD_CONFIG_DNS_CLIENT_NAT64_ENABLE + // This flag is only used in an IPv6 address query response. + // It indicates that the response does not contain any IPv6 + // addresses but server provided at least one IPv4 address + // in the additional data section for NAT64 address synthesis. + bool mIp6QueryResponseRequiresNat64; +#endif }; /** @@ -292,6 +335,11 @@ public: * */ Error GetAddress(uint16_t aIndex, Ip6::Address &aAddress, uint32_t &aTtl) const; + + private: +#if OPENTHREAD_CONFIG_DNS_CLIENT_NAT64_ENABLE + Error GetNat64Prefix(Ip6::Prefix &aPrefix) const; +#endif }; #if OPENTHREAD_CONFIG_DNS_CLIENT_SERVICE_DISCOVERY_ENABLE @@ -612,7 +660,10 @@ public: private: enum QueryType : uint8_t { - kAddressQuery, // Address resolution. + kIp6AddressQuery, // IPv6 Address resolution. +#if OPENTHREAD_CONFIG_DNS_CLIENT_NAT64_ENABLE + kIp4AddressQuery, // IPv4 Address resolution +#endif #if OPENTHREAD_CONFIG_DNS_CLIENT_SERVICE_DISCOVERY_ENABLE kBrowseQuery, // Browse (service instance enumeration). kServiceQuery, // Service instance resolution. @@ -668,11 +719,17 @@ private: Error ParseResponse(Response &aResponse, QueryType &aType, Error &aResponseError); static void HandleTimer(Timer &aTimer); void HandleTimer(void); +#if OPENTHREAD_CONFIG_DNS_CLIENT_NAT64_ENABLE + Error CheckAddressResponse(Response &aResponse, Error aResponseError) const; +#endif static const uint8_t kQuestionCount[]; static const uint16_t *kQuestionRecordTypes[]; - static const uint16_t kAddressQueryRecordTypes[]; + static const uint16_t kIp6AddressQueryRecordTypes[]; +#if OPENTHREAD_CONFIG_DNS_CLIENT_NAT64_ENABLE + static const uint16_t kIp4AddressQueryRecordTypes[]; +#endif #if OPENTHREAD_CONFIG_DNS_CLIENT_SERVICE_DISCOVERY_ENABLE static const uint16_t kBrowseQueryRecordTypes[]; static const uint16_t kServiceQueryRecordTypes[]; diff --git a/src/core/net/ip6_address.cpp b/src/core/net/ip6_address.cpp index f7063eca4..cc2bb1e7c 100644 --- a/src/core/net/ip6_address.cpp +++ b/src/core/net/ip6_address.cpp @@ -453,7 +453,7 @@ bool Address::MatchesFilter(TypeFilter aFilter) const return matches; } -void Address::SetFromTranslatedIp4Address(const Prefix &aPrefix, const Ip4::Address &aIp4Address) +void Address::SynthesizeFromIp4Address(const Prefix &aPrefix, const Ip4::Address &aIp4Address) { // The prefix length must be 32, 40, 48, 56, 64, 96. IPv4 bytes are added // after the prefix, skipping over the bits 64 to 71 (byte at `kSkipIndex`) diff --git a/src/core/net/ip6_address.hpp b/src/core/net/ip6_address.hpp index e0efdba2d..95f776136 100644 --- a/src/core/net/ip6_address.hpp +++ b/src/core/net/ip6_address.hpp @@ -892,7 +892,7 @@ public: * @param[in] aIp4Address The IPv4 address to translate to IPv6. * */ - void SetFromTranslatedIp4Address(const Prefix &aPrefix, const Ip4::Address &aIp4Address); + void SynthesizeFromIp4Address(const Prefix &aPrefix, const Ip4::Address &aIp4Address); /** * This method converts an IPv6 address string to binary. diff --git a/tests/unit/test_ip_address.cpp b/tests/unit/test_ip_address.cpp index b680a0feb..c7da5159a 100644 --- a/tests/unit/test_ip_address.cpp +++ b/tests/unit/test_ip_address.cpp @@ -399,13 +399,13 @@ void TestIp4Ip6Translation(void) SuccessOrQuit(expectedAddress.FromString(testCase.mIp6Address), "Ip6::FromString() failed"); - address.SetFromTranslatedIp4Address(prefix, ip4Address); + address.SynthesizeFromIp4Address(prefix, ip4Address); printf("Prefix: %-26s IPv4Addr: %-12s Ipv6Address: %-36s Expected: %s (%s)\n", prefix.ToString().AsCString(), ip4Address.ToString().AsCString(), address.ToString().AsCString(), testCase.mIp6Address, expectedAddress.ToString().AsCString()); - VerifyOrQuit(address == expectedAddress, "Ip6::SetFromTranslatedIp4Address() failed"); + VerifyOrQuit(address == expectedAddress, "Ip6::SynthesizeFromIp4Address() failed"); } }