diff options
author | Mike Yu <yumike@google.com> | 2021-05-11 14:49:30 +0800 |
---|---|---|
committer | Mike Yu <yumike@google.com> | 2021-06-05 23:30:45 +0800 |
commit | 1aede8135e8a227e127f826f38073eba7447c382 (patch) | |
tree | 7359b314bb0e92d2e78b49598ffe96afd38b98a7 /PrivateDnsConfiguration.cpp | |
parent | 8d636aebd52fca96b732a44792a1918cd9ac0143 (diff) | |
download | DnsResolver-1aede8135e8a227e127f826f38073eba7447c382.tar.gz |
Support evaluating private DNS by latency
The evaluation is limited to opportunistic mode and is implemented
as a flag-off feature. It is introduced to avoid from using high
latency private DNS servers.
The latency of a server is considered high if it's higher than a
latency threshold which is calculated based on the average latency of
cleartext DNS server:
latency threshold = std::clamp(3 * mean_do53_latency_ms,
min_private_dns_latency_threshold_ms,
max_private_dns_latency_threshold_ms)
, where min_private_dns_latency_threshold_ms is 500 ms by default and
max_private_dns_latency_threshold_ms is 2000 ms by default.
If there's no Do53 average latency for reference, the latency threshold
is min_private_dns_latency_threshold_ms.
The evaluation of a private DNS server works in two phases.
Phase 1: In this phase, Private DNS Validation is being performed,
and the server is not considered validated. The server latency is
evaluated by sending a probe. If the latency is lower than a the
latency threshold, the server state is transitioned to Validation::success.
The evaluation goes to phase 2.
Phase 2: In this phase, the server is considered validated and
DnsResolver can send DNS queries to the server. The server latency
is evaluated by the query response time, and the same latency threshold
is used. If there are several, 10 by default, query response time
failed to meet the time threshold in a row, the server state is
transitioned to Validation::in_process. The evaluation goes to phase 1.
Bug: 188153519
Test: run atest with all the flags off/on
avoid_bad_private_dns: 0 / 1
sort_nameservers: 0 / 1
dot_xport_unusable_threshold: -1 / 20
dot_query_timeout_ms: -1 / 10000
min_private_dns_latency_threshold_ms: -1 / 500
keep_listening_udp: 0 / 1
parallel_lookup_sleep_time: 2 / 2
dot_revalidation_threshold: -1 / 10
max_private_dns_latency_threshold_ms: -1 / 2000
dot_async_handshake: 0 / 1
dot_maxtries: 3 / 1
dot_connect_timeout_ms: 127000 / 10000
parallel_lookup_release: UNSET / UNSET
Change-Id: Ib681b1ea1417eadac9c013f19549a9fa7c408696
Diffstat (limited to 'PrivateDnsConfiguration.cpp')
-rw-r--r-- | PrivateDnsConfiguration.cpp | 83 |
1 files changed, 71 insertions, 12 deletions
diff --git a/PrivateDnsConfiguration.cpp b/PrivateDnsConfiguration.cpp index b09c7ce3..6939227e 100644 --- a/PrivateDnsConfiguration.cpp +++ b/PrivateDnsConfiguration.cpp @@ -21,18 +21,23 @@ #include <android-base/format.h> #include <android-base/logging.h> #include <android-base/stringprintf.h> +#include <netdutils/Stopwatch.h> #include <netdutils/ThreadUtil.h> #include <sys/socket.h> #include "DnsTlsTransport.h" +#include "Experiments.h" #include "ResolverEventReporter.h" #include "netd_resolv/resolv.h" +#include "resolv_cache.h" +#include "resolv_private.h" #include "util.h" using aidl::android::net::resolv::aidl::IDnsResolverUnsolicitedEventListener; using aidl::android::net::resolv::aidl::PrivateDnsValidationEventParcel; using android::base::StringPrintf; using android::netdutils::setThreadName; +using android::netdutils::Stopwatch; using std::chrono::milliseconds; namespace android { @@ -195,6 +200,23 @@ void PrivateDnsConfiguration::startValidation(const ServerIdentity& identity, un std::thread validate_thread([this, identity, server, netId, isRevalidation] { setThreadName(StringPrintf("TlsVerify_%u", netId).c_str()); + const bool avoidBadPrivateDns = + Experiments::getInstance()->getFlag("avoid_bad_private_dns", 0); + std::optional<int64_t> latencyThreshold; + if (avoidBadPrivateDns) { + const int maxLatency = Experiments::getInstance()->getFlag( + "max_private_dns_latency_threshold_ms", kMaxPrivateDnsLatencyThresholdMs); + const int minLatency = Experiments::getInstance()->getFlag( + "min_private_dns_latency_threshold_ms", kMinPrivateDnsLatencyThresholdMs); + const auto do53Latency = resolv_stats_get_average_response_time(netId, PROTO_UDP); + const int target = + do53Latency.has_value() ? (3 * do53Latency.value().count() / 1000) : 0; + + // The time is limited to the range [minLatency, maxLatency]. + latencyThreshold = std::clamp(target, minLatency, maxLatency); + } + const bool isOpportunisticMode = server.name.empty(); + // cat /proc/sys/net/ipv4/tcp_syn_retries yields "6". // // Start with a 1 minute delay and backoff to once per hour. @@ -215,12 +237,24 @@ void PrivateDnsConfiguration::startValidation(const ServerIdentity& identity, un // It can take milliseconds to minutes, up to the SYN retry limit. LOG(WARNING) << "Validating DnsTlsServer " << server.toIpString() << " with mark 0x" << std::hex << server.validationMark(); - const bool success = DnsTlsTransport::validate(server, server.validationMark()); - LOG(WARNING) << "validateDnsTlsServer returned " << success << " for " - << server.toIpString(); - const bool needs_reeval = - this->recordPrivateDnsValidation(identity, netId, success, isRevalidation); + Stopwatch stopwatch; + const bool gotAnswer = DnsTlsTransport::validate(server, server.validationMark()); + const int32_t timeTaken = saturate_cast<int32_t>(stopwatch.timeTakenUs() / 1000); + LOG(WARNING) << fmt::format("validateDnsTlsServer returned {} for {}, took {}ms", + gotAnswer, server.toIpString(), timeTaken); + + const int64_t targetTime = latencyThreshold.value_or(INT64_MAX); + bool latencyTooHigh = false; + if (isOpportunisticMode && timeTaken > targetTime) { + latencyTooHigh = true; + LOG(WARNING) << "validateDnsTlsServer took too long: threshold is " << targetTime + << "ms"; + } + + // TODO: combine these boolean variables into a bitwise variable. + const bool needs_reeval = this->recordPrivateDnsValidation( + identity, netId, gotAnswer, isRevalidation, latencyTooHigh); if (!needs_reeval) { break; @@ -233,6 +267,8 @@ void PrivateDnsConfiguration::startValidation(const ServerIdentity& identity, un break; } } + + this->updateServerLatencyThreshold(identity, latencyThreshold, netId); }); validate_thread.detach(); } @@ -268,8 +304,8 @@ void PrivateDnsConfiguration::sendPrivateDnsValidationEvent(const ServerIdentity } bool PrivateDnsConfiguration::recordPrivateDnsValidation(const ServerIdentity& identity, - unsigned netId, bool success, - bool isRevalidation) { + unsigned netId, bool gotAnswer, + bool isRevalidation, bool latencyTooHigh) { constexpr bool NEEDS_REEVALUATION = true; constexpr bool DONT_REEVALUATE = false; @@ -290,14 +326,17 @@ bool PrivateDnsConfiguration::recordPrivateDnsValidation(const ServerIdentity& i } bool reevaluationStatus = NEEDS_REEVALUATION; - if (success) { - reevaluationStatus = DONT_REEVALUATE; + if (gotAnswer) { + if (!latencyTooHigh) { + reevaluationStatus = DONT_REEVALUATE; + } } else if (mode->second == PrivateDnsMode::OFF) { reevaluationStatus = DONT_REEVALUATE; } else if (mode->second == PrivateDnsMode::OPPORTUNISTIC && !isRevalidation) { reevaluationStatus = DONT_REEVALUATE; } + bool success = gotAnswer; auto& tracker = netPair->second; auto serverPair = tracker.find(identity); if (serverPair == tracker.end()) { @@ -312,10 +351,12 @@ bool PrivateDnsConfiguration::recordPrivateDnsValidation(const ServerIdentity& i reevaluationStatus = DONT_REEVALUATE; } + const bool succeededQuickly = success && !latencyTooHigh; + // Send private dns validation result to listeners. - sendPrivateDnsValidationEvent(identity, netId, success); + sendPrivateDnsValidationEvent(identity, netId, succeededQuickly); - if (success) { + if (succeededQuickly) { updateServerState(identity, Validation::success, netId); } else { // Validation failure is expected if a user is on a captive portal. @@ -325,7 +366,7 @@ bool PrivateDnsConfiguration::recordPrivateDnsValidation(const ServerIdentity& i : Validation::fail; updateServerState(identity, result, netId); } - LOG(WARNING) << "Validation " << (success ? "success" : "failed"); + LOG(WARNING) << "Validation " << (succeededQuickly ? "success" : "failed"); return reevaluationStatus; } @@ -385,6 +426,24 @@ base::Result<IPrivateDnsServer*> PrivateDnsConfiguration::getPrivateDnsLocked( return iter->second.get(); } +void PrivateDnsConfiguration::updateServerLatencyThreshold(const ServerIdentity& identity, + std::optional<int64_t> latencyThreshold, + uint32_t netId) { + std::lock_guard guard(mPrivateDnsLock); + + const auto result = getPrivateDnsLocked(identity, netId); + if (!result.ok()) return; + + if (result.value()->isDot()) { + DnsTlsServer& server = *static_cast<DnsTlsServer*>(result.value()); + server.setLatencyThreshold(latencyThreshold); + LOG(INFO) << "Set latencyThreshold " + << (latencyThreshold ? std::to_string(latencyThreshold.value()) + "ms" + : "nullopt") + << " to " << server.toIpString(); + } +} + void PrivateDnsConfiguration::setObserver(PrivateDnsValidationObserver* observer) { std::lock_guard guard(mPrivateDnsLock); mObserver = observer; |