aboutsummaryrefslogtreecommitdiff
path: root/PrivateDnsConfiguration.cpp
diff options
context:
space:
mode:
authorMike Yu <yumike@google.com>2021-05-11 14:49:30 +0800
committerMike Yu <yumike@google.com>2021-06-05 23:30:45 +0800
commit1aede8135e8a227e127f826f38073eba7447c382 (patch)
tree7359b314bb0e92d2e78b49598ffe96afd38b98a7 /PrivateDnsConfiguration.cpp
parent8d636aebd52fca96b732a44792a1918cd9ac0143 (diff)
downloadDnsResolver-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.cpp83
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;