[dns-client] add support address resolution query for IPv4 directly (#7441)

This commit adds a new mechanism in `Dns::Client` along with a new
public OT API `otDnsClientResolveIp4Address()` and its related CLI
sub-command to allow a user to request an address resolution DNS
query specifically for A records or IPv4 addresses for a given host
name.

This API requires `OPENTHREAD_CONFIG_DNS_CLIENT_NAT64_ENABLE` to be
enabled. Upon receiving a successful query response the addresses are
reported to user (in callback) as NAT64 IPv6 translated version of
the IPv4 addresses from the received query response.

The new API is added in addition to the existing behavior where address
resolution query from `otDnsClientResolveAddress()` first queries for
IPv6 addresses and only if no IPv6 address is found and NAT64 is
enabled and allowed by the query config, `Dns::Client` implementation
tries to perform an IPv4 query. The new API directly starts with IPv4
query and can be useful in situations where Thread devices are
connected through BRs to a network without global IPv6 connectivity.
This commit is contained in:
Abtin Keshavarzian
2022-03-08 17:18:44 -08:00
committed by GitHub
parent 2612fbc927
commit 05d8e81988
9 changed files with 171 additions and 43 deletions
+36 -3
View File
@@ -199,6 +199,8 @@ typedef void (*otDnsAddressCallback)(otError aError, const otDnsAddressResponse
*
* @retval OT_ERROR_NONE Query sent successfully. @p aCallback will be invoked to report the status.
* @retval OT_ERROR_NO_BUFS Insufficient buffer to prepare and send query.
* @retval OT_ERROR_INVALID_ARGS The host name is not valid format.
* @retval OT_ERROR_INVALID_STATE Cannot send query since Thread interface is not up.
*
*/
otError otDnsClientResolveAddress(otInstance * aInstance,
@@ -207,6 +209,36 @@ otError otDnsClientResolveAddress(otInstance * aInstance,
void * aContext,
const otDnsQueryConfig *aConfig);
/**
* This function sends an address resolution DNS query for A (IPv4) record(s) for a given host name.
*
* This function requires and is available when `OPENTHREAD_CONFIG_DNS_CLIENT_NAT64_ENABLE` is enabled.
*
* When a successful response is received, the addresses are returned from @p aCallback as NAT64 IPv6 translated
* versions of the IPv4 addresses from the query response.
*
* The @p aConfig can be NULL. In this case the default config (from `otDnsClientGetDefaultConfig()`) will be used as
* the config for this query. In a non-NULL @p aConfig, some of the fields can be left unspecified (value zero). The
* unspecified fields are then replaced by the values from the default config.
*
* @param[in] aInstance A pointer to an OpenThread instance.
* @param[in] aHostName The host name for which to query the address (MUST NOT be NULL).
* @param[in] aCallback A function pointer that shall be called on response reception or time-out.
* @param[in] aContext A pointer to arbitrary context information.
* @param[in] aConfig A pointer to the config to use for this query.
*
* @retval OT_ERROR_NONE Query sent successfully. @p aCallback will be invoked to report the status.
* @retval OT_ERROR_NO_BUFS Insufficient buffer to prepare and send query.
* @retval OT_ERROR_INVALID_ARGS The host name is not valid format or NAT64 is not enabled in config.
* @retval OT_ERROR_INVALID_STATE Cannot send query since Thread interface is not up.
*
*/
otError otDnsClientResolveIp4Address(otInstance * aInstance,
const char * aHostName,
otDnsAddressCallback aCallback,
void * aContext,
const otDnsQueryConfig *aConfig);
/**
* This function gets the full host name associated with an address resolution DNS response.
*
@@ -239,9 +271,10 @@ otError otDnsAddressResponseGetHostName(const otDnsAddressResponse *aResponse,
* @param[out] aTtl A pointer to an `uint32_t` to output TTL for the address. It can be NULL if caller does not
* want to get the TTL.
*
* @retval OT_ERROR_NONE The address was read successfully.
* @retval OT_ERROR_NOT_FOUND No address record in @p aResponse at @p aIndex.
* @retval OT_ERROR_PARSE Could not parse the records in the @p aResponse.
* @retval OT_ERROR_NONE The address was read successfully.
* @retval OT_ERROR_NOT_FOUND No address record in @p aResponse at @p aIndex.
* @retval OT_ERROR_PARSE Could not parse the records in the @p aResponse.
* @retval OT_ERROR_INVALID_STATE No NAT64 prefix (applicable only when NAT64 is allowed).
*
*/
otError otDnsAddressResponseGetAddress(const otDnsAddressResponse *aResponse,
+1 -1
View File
@@ -53,7 +53,7 @@ extern "C" {
* @note This number versions both OpenThread platform and user APIs.
*
*/
#define OPENTHREAD_API_VERSION (195)
#define OPENTHREAD_API_VERSION (196)
/**
* @addtogroup api-instance
+10
View File
@@ -1544,6 +1544,16 @@ template <> otError Interpreter::Process<Cmd("dns")>(Arg aArgs[])
&Interpreter::HandleDnsAddressResponse, this, config));
error = OT_ERROR_PENDING;
}
#if OPENTHREAD_CONFIG_DNS_CLIENT_NAT64_ENABLE
else if (aArgs[0] == "resolve4")
{
VerifyOrExit(!aArgs[1].IsEmpty(), error = OT_ERROR_INVALID_ARGS);
SuccessOrExit(error = GetDnsConfig(aArgs + 2, config));
SuccessOrExit(error = otDnsClientResolveIp4Address(GetInstancePtr(), aArgs[1].GetCString(),
&Interpreter::HandleDnsAddressResponse, this, config));
error = OT_ERROR_PENDING;
}
#endif
#if OPENTHREAD_CONFIG_DNS_CLIENT_SERVICE_DISCOVERY_ENABLE
else if (aArgs[0] == "browse")
{
+12
View File
@@ -91,6 +91,18 @@ otError otDnsClientResolveAddress(otInstance * aInstance,
AsCoreTypePtr(aConfig));
}
#if OPENTHREAD_CONFIG_DNS_CLIENT_NAT64_ENABLE
otError otDnsClientResolveIp4Address(otInstance * aInstance,
const char * aHostName,
otDnsAddressCallback aCallback,
void * aContext,
const otDnsQueryConfig *aConfig)
{
return AsCoreType(aInstance).Get<Dns::Client>().ResolveIp4Address(aHostName, aCallback, aContext,
AsCoreTypePtr(aConfig));
}
#endif
otError otDnsAddressResponseGetHostName(const otDnsAddressResponse *aResponse,
char * aNameBuffer,
uint16_t aNameBufferSize)
+33 -36
View File
@@ -328,16 +328,17 @@ Error Client::AddressResponse::GetAddress(uint16_t aIndex, Ip6::Address &aAddres
if ((info.mQueryType == kIp4AddressQuery) || mIp6QueryResponseRequiresNat64)
{
Section section;
ARecord aRecord;
Ip6::Prefix nat64Prefix;
Section section;
ARecord aRecord;
NetworkData::ExternalRouteConfig nat64Prefix;
SuccessOrExit(error = GetNat64Prefix(nat64Prefix));
VerifyOrExit(mInstance->Get<NetworkData::Leader>().GetPreferredNat64Prefix(nat64Prefix) == kErrorNone,
error = kErrorInvalidState);
section = (info.mQueryType == kIp4AddressQuery) ? kAnswerSection : kAdditionalDataSection;
SuccessOrExit(error = FindARecord(section, name, aIndex, aRecord));
aAddress.SynthesizeFromIp4Address(nat64Prefix, aRecord.GetAddress());
aAddress.SynthesizeFromIp4Address(nat64Prefix.GetPrefix(), aRecord.GetAddress());
aTtl = aRecord.GetTtl();
ExitNow();
@@ -351,37 +352,6 @@ exit:
return error;
}
#if OPENTHREAD_CONFIG_DNS_CLIENT_NAT64_ENABLE
Error Client::AddressResponse::GetNat64Prefix(Ip6::Prefix &aPrefix) const
{
Error error = kErrorNotFound;
NetworkData::Iterator iterator = NetworkData::kIteratorInit;
signed int preference = NetworkData::kRoutePreferenceLow;
NetworkData::ExternalRouteConfig config;
aPrefix.Clear();
while (mInstance->Get<NetworkData::Leader>().GetNextExternalRoute(iterator, config) == kErrorNone)
{
if (!config.mNat64 || !config.GetPrefix().IsValidNat64())
{
continue;
}
if ((aPrefix.GetLength() == 0) || (config.mPreference > preference))
{
aPrefix = config.GetPrefix();
preference = config.mPreference;
error = kErrorNone;
}
}
return error;
}
#endif // OPENTHREAD_CONFIG_DNS_CLIENT_NAT64_ENABLE
#if OPENTHREAD_CONFIG_DNS_CLIENT_SERVICE_DISCOVERY_ENABLE
//---------------------------------------------------------------------------------------------------------------------
@@ -651,6 +621,22 @@ Error Client::ResolveAddress(const char * aHostName,
return StartQuery(info, aConfig, nullptr, aHostName, aContext);
}
#if OPENTHREAD_CONFIG_DNS_CLIENT_NAT64_ENABLE
Error Client::ResolveIp4Address(const char * aHostName,
AddressCallback aCallback,
void * aContext,
const QueryConfig *aConfig)
{
QueryInfo info;
info.Clear();
info.mQueryType = kIp4AddressQuery;
info.mCallback.mAddressCallback = aCallback;
return StartQuery(info, aConfig, nullptr, aHostName, aContext);
}
#endif
#if OPENTHREAD_CONFIG_DNS_CLIENT_SERVICE_DISCOVERY_ENABLE
Error Client::Browse(const char *aServiceName, BrowseCallback aCallback, void *aContext, const QueryConfig *aConfig)
@@ -718,6 +704,17 @@ Error Client::StartQuery(QueryInfo & aInfo,
aInfo.mCallbackContext = aContext;
#if OPENTHREAD_CONFIG_DNS_CLIENT_NAT64_ENABLE
if (aInfo.mQueryType == kIp4AddressQuery)
{
NetworkData::ExternalRouteConfig nat64Prefix;
VerifyOrExit(aInfo.mConfig.GetNat64Mode() == QueryConfig::kNat64Allow, error = kErrorInvalidArgs);
VerifyOrExit(Get<NetworkData::Leader>().GetPreferredNat64Prefix(nat64Prefix) == kErrorNone,
error = kErrorInvalidState);
}
#endif
SuccessOrExit(error = AllocateQuery(aInfo, aLabel, aName, query));
mQueries.Enqueue(*query);
+32 -3
View File
@@ -331,9 +331,10 @@ public:
* @param[out] aAddress A reference to an IPv6 address to output the address.
* @param[out] aTtl A reference to a `uint32_t` to output TTL for the address.
*
* @retval kErrorNone The address was read successfully.
* @retval kErrorNotFound No address record at @p aIndex.
* @retval kErrorParse Could not parse the records.
* @retval kErrorNone The address was read successfully.
* @retval kErrorNotFound No address record at @p aIndex.
* @retval kErrorParse Could not parse the records.
* @retval kErrorInvalidState No NAT64 prefix (applicable only when NAT64 is allowed).
*
*/
Error GetAddress(uint16_t aIndex, Ip6::Address &aAddress, uint32_t &aTtl) const;
@@ -609,6 +610,34 @@ public:
void * aContext,
const QueryConfig *aConfig = nullptr);
#if OPENTHREAD_CONFIG_DNS_CLIENT_NAT64_ENABLE
/**
* This method sends an address resolution DNS query for A (IPv4) record for a given host name.
*
* When a successful response is received, the addresses are returned from @p aCallback as NAT64 IPv6 translated
* versions of the IPv4 addresses from the query response.
*
* The @p aConfig can be nullptr. In this case the default config (from `GetDefaultConfig()`) will be used as
* the config for this query. In a non-nullptr @p aConfig, some of the fields can be left unspecified (value zero).
* The unspecified fields are then replaced by the values from the default config.
*
* @param[in] aHostName The host name for which to query the address (MUST NOT be `nullptr`).
* @param[in] aCallback A callback function pointer to report the result of query.
* @param[in] aContext A pointer to arbitrary context information passed to @p aCallback.
* @param[in] aConfig The config to use for this query.
*
* @retval kErrorNone Successfully sent DNS query.
* @retval kErrorNoBufs Failed to allocate retransmission data.
* @retval kErrorInvalidArgs The host name is not valid format or NAT64 is not enabled in config.
* @retval kErrorInvalidState Cannot send query since Thread interface is not up, or there is no NAT64 prefix.
*
*/
Error ResolveIp4Address(const char * aHostName,
AddressCallback aCallback,
void * aContext,
const QueryConfig *aConfig = nullptr);
#endif
#if OPENTHREAD_CONFIG_DNS_CLIENT_SERVICE_DISCOVERY_ENABLE
/**
+23
View File
@@ -89,6 +89,29 @@ exit:
return error;
}
Error LeaderBase::GetPreferredNat64Prefix(ExternalRouteConfig &aConfig) const
{
Error error = kErrorNotFound;
Iterator iterator = kIteratorInit;
ExternalRouteConfig config;
while (GetNextExternalRoute(iterator, config) == kErrorNone)
{
if (!config.mNat64 || !config.GetPrefix().IsValidNat64())
{
continue;
}
if ((error == kErrorNotFound) || (config.mPreference > aConfig.mPreference))
{
aConfig = config;
error = kErrorNone;
}
}
return error;
}
const PrefixTlv *LeaderBase::FindNextMatchingPrefix(const Ip6::Address &aAddress, const PrefixTlv *aPrevTlv) const
{
const PrefixTlv *prefixTlv;
+14
View File
@@ -267,6 +267,20 @@ public:
bool aServerStable,
uint8_t & aServiceId) const;
/**
* This methods gets the preferred NAT64 prefix from network data.
*
* The returned prefix is the highest preference external route entry in Network Data with NAT64 flag set. If there
* are multiple such entries the first one is returned.
*
* @param[out] aConfig A reference to an `ExternalRouteConfig` to return the prefix.
*
* @retval kErrorNone Found the NAT64 prefix and updated @p aConfig.
* @retval kErrorNotFound Could not find any NAT64 entry.
*
*/
Error GetPreferredNat64Prefix(ExternalRouteConfig &aConfig) const;
protected:
uint8_t mStableVersion;
uint8_t mVersion;
+10
View File
@@ -40,8 +40,18 @@ expect_line "Done"
wait_for "state" "leader"
expect_line "Done"
send "route add 2001:dead:beef:cafe::/64 s n med\n"
expect_line "Done"
send "netdata register\n"
expect_line "Done"
send "dns resolve ipv6.google.com ::1 53\n"
expect "DNS response for ipv6.google.com"
expect_line "Done"
send "dns resolve4 ipv6.google.com ::1 53\n"
expect "DNS response for ipv6.google.com"
expect_line "Done"
dispose_all