blob: 16fc5d1b2e213e45d74383f93798a985a1e4bf3e [file] [log] [blame]
[email protected]d0d49dd82012-01-26 00:03:591// Copyright (c) 2012 The Chromium Authors. All rights reserved.
[email protected]7556ea22011-12-08 19:29:152// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "net/dns/dns_session.h"
6
avi65168052015-12-01 19:27:077#include <stdint.h>
danakj22f90e72016-04-16 01:55:408
avi65168052015-12-01 19:27:079#include <limits>
dchengc7eeda422015-12-26 03:56:4810#include <utility>
avi65168052015-12-01 19:27:0711
[email protected]7556ea22011-12-08 19:29:1512#include "base/bind.h"
[email protected]a6c84f42013-06-07 20:39:3813#include "base/lazy_instance.h"
Avi Drissman13fc8932015-12-20 04:40:4614#include "base/macros.h"
danakj22f90e72016-04-16 01:55:4015#include "base/memory/ptr_util.h"
ttuttlecf1158bf2016-03-18 16:37:4416#include "base/metrics/field_trial.h"
asvitkinec3c93722015-06-17 14:48:3717#include "base/metrics/histogram_macros.h"
[email protected]ae1b30b2013-05-23 23:06:0318#include "base/metrics/sample_vector.h"
[email protected]120d38d2012-12-14 01:42:3219#include "base/rand_util.h"
20#include "base/stl_util.h"
[email protected]66e96c42013-06-28 15:20:3121#include "base/time/time.h"
juliatuttled7fa5852016-07-29 13:16:5722#include "base/values.h"
[email protected]7556ea22011-12-08 19:29:1523#include "net/base/ip_endpoint.h"
[email protected]120d38d2012-12-14 01:42:3224#include "net/base/net_errors.h"
[email protected]7556ea22011-12-08 19:29:1525#include "net/dns/dns_config_service.h"
[email protected]120d38d2012-12-14 01:42:3226#include "net/dns/dns_socket_pool.h"
ttuttlecf1158bf2016-03-18 16:37:4427#include "net/dns/dns_util.h"
mikecirone8b85c432016-09-08 19:11:0028#include "net/log/net_log_event_type.h"
[email protected]bdb65982012-12-20 20:44:5929#include "net/socket/stream_socket.h"
30#include "net/udp/datagram_client_socket.h"
[email protected]7556ea22011-12-08 19:29:1531
32namespace net {
33
[email protected]ae1b30b2013-05-23 23:06:0334namespace {
ttuttlecf1158bf2016-03-18 16:37:4435
[email protected]ae1b30b2013-05-23 23:06:0336// Set min timeout, in case we are talking to a local DNS proxy.
37const unsigned kMinTimeoutMs = 10;
38
ttuttlecf1158bf2016-03-18 16:37:4439// Default maximum timeout between queries, even with exponential backoff.
40// (Can be overridden by field trial.)
41const unsigned kDefaultMaxTimeoutMs = 5000;
42
43// Maximum RTT that will fit in the RTT histograms.
44const int32_t kRTTMaxMs = 30000;
[email protected]ae1b30b2013-05-23 23:06:0345// Number of buckets in the histogram of observed RTTs.
ttuttlecf1158bf2016-03-18 16:37:4446const size_t kRTTBucketCount = 350;
[email protected]ae1b30b2013-05-23 23:06:0347// Target percentile in the RTT histogram used for retransmission timeout.
48const unsigned kRTOPercentile = 99;
ttuttlecf1158bf2016-03-18 16:37:4449
[email protected]ae1b30b2013-05-23 23:06:0350} // namespace
51
[email protected]a6c84f42013-06-07 20:39:3852// Runtime statistics of DNS server.
53struct DnsSession::ServerStats {
54 ServerStats(base::TimeDelta rtt_estimate_param, RttBuckets* buckets)
55 : last_failure_count(0), rtt_estimate(rtt_estimate_param) {
56 rtt_histogram.reset(new base::SampleVector(buckets));
[email protected]a144bd22013-07-29 21:53:1057 // Seed histogram with 2 samples at |rtt_estimate| timeout.
pkasting6b68a162014-12-01 22:10:2958 rtt_histogram->Accumulate(
59 static_cast<base::HistogramBase::Sample>(rtt_estimate.InMilliseconds()),
60 2);
[email protected]a6c84f42013-06-07 20:39:3861 }
62
63 // Count of consecutive failures after last success.
64 int last_failure_count;
65
66 // Last time when server returned failure or timeout.
67 base::Time last_failure;
68 // Last time when server returned success.
69 base::Time last_success;
70
71 // Estimated RTT using moving average.
72 base::TimeDelta rtt_estimate;
73 // Estimated error in the above.
74 base::TimeDelta rtt_deviation;
75
76 // A histogram of observed RTT .
danakj22f90e72016-04-16 01:55:4077 std::unique_ptr<base::SampleVector> rtt_histogram;
[email protected]a6c84f42013-06-07 20:39:3878
79 DISALLOW_COPY_AND_ASSIGN(ServerStats);
80};
81
82// static
83base::LazyInstance<DnsSession::RttBuckets>::Leaky DnsSession::rtt_buckets_ =
84 LAZY_INSTANCE_INITIALIZER;
85
86DnsSession::RttBuckets::RttBuckets() : base::BucketRanges(kRTTBucketCount + 1) {
ttuttlecf1158bf2016-03-18 16:37:4487 base::Histogram::InitializeBucketRanges(1, kRTTMaxMs, this);
[email protected]a6c84f42013-06-07 20:39:3888}
89
danakj22f90e72016-04-16 01:55:4090DnsSession::SocketLease::SocketLease(
91 scoped_refptr<DnsSession> session,
92 unsigned server_index,
93 std::unique_ptr<DatagramClientSocket> socket)
dchengc7eeda422015-12-26 03:56:4894 : session_(session),
95 server_index_(server_index),
96 socket_(std::move(socket)) {}
[email protected]120d38d2012-12-14 01:42:3297
98DnsSession::SocketLease::~SocketLease() {
dchengc7eeda422015-12-26 03:56:4899 session_->FreeSocket(server_index_, std::move(socket_));
[email protected]120d38d2012-12-14 01:42:32100}
101
[email protected]7556ea22011-12-08 19:29:15102DnsSession::DnsSession(const DnsConfig& config,
danakj22f90e72016-04-16 01:55:40103 std::unique_ptr<DnsSocketPool> socket_pool,
[email protected]7556ea22011-12-08 19:29:15104 const RandIntCallback& rand_int_callback,
105 NetLog* net_log)
106 : config_(config),
dchengc7eeda422015-12-26 03:56:48107 socket_pool_(std::move(socket_pool)),
avi65168052015-12-01 19:27:07108 rand_callback_(base::Bind(rand_int_callback,
109 0,
110 std::numeric_limits<uint16_t>::max())),
[email protected]7556ea22011-12-08 19:29:15111 net_log_(net_log),
[email protected]a6c84f42013-06-07 20:39:38112 server_index_(0) {
[email protected]120d38d2012-12-14 01:42:32113 socket_pool_->Initialize(&config_.nameservers, net_log);
drbasicf0d1b262016-08-23 06:10:42114 UMA_HISTOGRAM_CUSTOM_COUNTS("AsyncDNS.ServerCount",
115 config_.nameservers.size(), 1, 10, 11);
ttuttlecf1158bf2016-03-18 16:37:44116 UpdateTimeouts(NetworkChangeNotifier::GetConnectionType());
117 InitializeServerStats();
118 NetworkChangeNotifier::AddConnectionTypeObserver(this);
[email protected]120d38d2012-12-14 01:42:32119}
120
[email protected]a6c84f42013-06-07 20:39:38121DnsSession::~DnsSession() {
122 RecordServerStats();
ttuttlecf1158bf2016-03-18 16:37:44123 NetworkChangeNotifier::RemoveConnectionTypeObserver(this);
124}
125
126void DnsSession::UpdateTimeouts(NetworkChangeNotifier::ConnectionType type) {
127 initial_timeout_ = GetTimeDeltaForConnectionTypeFromFieldTrialOrDefault(
128 "AsyncDnsInitialTimeoutMsByConnectionType", config_.timeout, type);
129 max_timeout_ = GetTimeDeltaForConnectionTypeFromFieldTrialOrDefault(
130 "AsyncDnsMaxTimeoutMsByConnectionType",
131 base::TimeDelta::FromMilliseconds(kDefaultMaxTimeoutMs), type);
132}
133
134void DnsSession::InitializeServerStats() {
135 server_stats_.clear();
136 for (size_t i = 0; i < config_.nameservers.size(); ++i) {
ricea2deef682016-09-09 08:04:07137 server_stats_.push_back(base::MakeUnique<ServerStats>(
138 initial_timeout_, rtt_buckets_.Pointer()));
ttuttlecf1158bf2016-03-18 16:37:44139 }
140}
141
142void DnsSession::OnConnectionTypeChanged(
143 NetworkChangeNotifier::ConnectionType type) {
144 UpdateTimeouts(type);
145 const char* kTrialName = "AsyncDnsFlushServerStatsOnConnectionTypeChange";
146 if (base::FieldTrialList::FindFullName(kTrialName) == "enable") {
147 RecordServerStats();
148 InitializeServerStats();
149 }
[email protected]a6c84f42013-06-07 20:39:38150}
[email protected]7556ea22011-12-08 19:29:15151
avi65168052015-12-01 19:27:07152uint16_t DnsSession::NextQueryId() const {
153 return static_cast<uint16_t>(rand_callback_.Run());
pkasting6b68a162014-12-01 22:10:29154}
[email protected]7556ea22011-12-08 19:29:15155
[email protected]a6c84f42013-06-07 20:39:38156unsigned DnsSession::NextFirstServerIndex() {
157 unsigned index = NextGoodServerIndex(server_index_);
[email protected]7556ea22011-12-08 19:29:15158 if (config_.rotate)
159 server_index_ = (server_index_ + 1) % config_.nameservers.size();
[email protected]d0d49dd82012-01-26 00:03:59160 return index;
[email protected]7556ea22011-12-08 19:29:15161}
162
[email protected]a6c84f42013-06-07 20:39:38163unsigned DnsSession::NextGoodServerIndex(unsigned server_index) {
164 unsigned index = server_index;
165 base::Time oldest_server_failure(base::Time::Now());
166 unsigned oldest_server_failure_index = 0;
167
168 UMA_HISTOGRAM_BOOLEAN("AsyncDNS.ServerIsGood",
169 server_stats_[server_index]->last_failure.is_null());
170
171 do {
172 base::Time cur_server_failure = server_stats_[index]->last_failure;
173 // If number of failures on this server doesn't exceed number of allowed
174 // attempts, return its index.
175 if (server_stats_[server_index]->last_failure_count < config_.attempts) {
176 return index;
177 }
178 // Track oldest failed server.
179 if (cur_server_failure < oldest_server_failure) {
180 oldest_server_failure = cur_server_failure;
181 oldest_server_failure_index = index;
182 }
183 index = (index + 1) % config_.nameservers.size();
184 } while (index != server_index);
185
186 // If we are here it means that there are no successful servers, so we have
187 // to use one that has failed oldest.
188 return oldest_server_failure_index;
189}
190
191void DnsSession::RecordServerFailure(unsigned server_index) {
drbasicf0d1b262016-08-23 06:10:42192 UMA_HISTOGRAM_CUSTOM_COUNTS("AsyncDNS.ServerFailureIndex", server_index, 1,
193 10, 11);
[email protected]a6c84f42013-06-07 20:39:38194 ++(server_stats_[server_index]->last_failure_count);
195 server_stats_[server_index]->last_failure = base::Time::Now();
196}
197
198void DnsSession::RecordServerSuccess(unsigned server_index) {
199 if (server_stats_[server_index]->last_success.is_null()) {
200 UMA_HISTOGRAM_COUNTS_100("AsyncDNS.ServerFailuresAfterNetworkChange",
201 server_stats_[server_index]->last_failure_count);
202 } else {
203 UMA_HISTOGRAM_COUNTS_100("AsyncDNS.ServerFailuresBeforeSuccess",
204 server_stats_[server_index]->last_failure_count);
205 }
206 server_stats_[server_index]->last_failure_count = 0;
207 server_stats_[server_index]->last_failure = base::Time();
208 server_stats_[server_index]->last_success = base::Time::Now();
209}
210
[email protected]ae1b30b2013-05-23 23:06:03211void DnsSession::RecordRTT(unsigned server_index, base::TimeDelta rtt) {
[email protected]a6c84f42013-06-07 20:39:38212 DCHECK_LT(server_index, server_stats_.size());
[email protected]ae1b30b2013-05-23 23:06:03213
214 // For measurement, assume it is the first attempt (no backoff).
215 base::TimeDelta timeout_jacobson = NextTimeoutFromJacobson(server_index, 0);
216 base::TimeDelta timeout_histogram = NextTimeoutFromHistogram(server_index, 0);
217 UMA_HISTOGRAM_TIMES("AsyncDNS.TimeoutErrorJacobson", rtt - timeout_jacobson);
218 UMA_HISTOGRAM_TIMES("AsyncDNS.TimeoutErrorHistogram",
219 rtt - timeout_histogram);
220 UMA_HISTOGRAM_TIMES("AsyncDNS.TimeoutErrorJacobsonUnder",
221 timeout_jacobson - rtt);
222 UMA_HISTOGRAM_TIMES("AsyncDNS.TimeoutErrorHistogramUnder",
223 timeout_histogram - rtt);
224
225 // Jacobson/Karels algorithm for TCP.
226 // Using parameters: alpha = 1/8, delta = 1/4, beta = 4
[email protected]a6c84f42013-06-07 20:39:38227 base::TimeDelta& estimate = server_stats_[server_index]->rtt_estimate;
228 base::TimeDelta& deviation = server_stats_[server_index]->rtt_deviation;
[email protected]ae1b30b2013-05-23 23:06:03229 base::TimeDelta current_error = rtt - estimate;
230 estimate += current_error / 8; // * alpha
231 base::TimeDelta abs_error = base::TimeDelta::FromInternalValue(
232 std::abs(current_error.ToInternalValue()));
233 deviation += (abs_error - deviation) / 4; // * delta
234
235 // Histogram-based method.
pkasting6b68a162014-12-01 22:10:29236 server_stats_[server_index]->rtt_histogram->Accumulate(
237 static_cast<base::HistogramBase::Sample>(rtt.InMilliseconds()), 1);
[email protected]ae1b30b2013-05-23 23:06:03238}
239
240void DnsSession::RecordLostPacket(unsigned server_index, int attempt) {
241 base::TimeDelta timeout_jacobson =
242 NextTimeoutFromJacobson(server_index, attempt);
243 base::TimeDelta timeout_histogram =
244 NextTimeoutFromHistogram(server_index, attempt);
245 UMA_HISTOGRAM_TIMES("AsyncDNS.TimeoutSpentJacobson", timeout_jacobson);
246 UMA_HISTOGRAM_TIMES("AsyncDNS.TimeoutSpentHistogram", timeout_histogram);
247}
248
[email protected]a6c84f42013-06-07 20:39:38249void DnsSession::RecordServerStats() {
250 for (size_t index = 0; index < server_stats_.size(); ++index) {
251 if (server_stats_[index]->last_failure_count) {
252 if (server_stats_[index]->last_success.is_null()) {
253 UMA_HISTOGRAM_COUNTS("AsyncDNS.ServerFailuresWithoutSuccess",
254 server_stats_[index]->last_failure_count);
255 } else {
256 UMA_HISTOGRAM_COUNTS("AsyncDNS.ServerFailuresAfterSuccess",
257 server_stats_[index]->last_failure_count);
258 }
259 }
260 }
261}
262
263
[email protected]ae1b30b2013-05-23 23:06:03264base::TimeDelta DnsSession::NextTimeout(unsigned server_index, int attempt) {
ttuttlecf1158bf2016-03-18 16:37:44265 // Respect initial timeout (from config or field trial) if it exceeds max.
266 if (initial_timeout_ > max_timeout_)
267 return initial_timeout_;
[email protected]a144bd22013-07-29 21:53:10268 return NextTimeoutFromHistogram(server_index, attempt);
[email protected]7556ea22011-12-08 19:29:15269}
270
[email protected]120d38d2012-12-14 01:42:32271// Allocate a socket, already connected to the server address.
danakj22f90e72016-04-16 01:55:40272std::unique_ptr<DnsSession::SocketLease> DnsSession::AllocateSocket(
273 unsigned server_index,
274 const NetLog::Source& source) {
275 std::unique_ptr<DatagramClientSocket> socket;
[email protected]120d38d2012-12-14 01:42:32276
277 socket = socket_pool_->AllocateSocket(server_index);
[email protected]dd946bb2013-06-12 22:53:01278 if (!socket.get())
danakj22f90e72016-04-16 01:55:40279 return std::unique_ptr<SocketLease>();
[email protected]120d38d2012-12-14 01:42:32280
mikecirone8b85c432016-09-08 19:11:00281 socket->NetLog().BeginEvent(NetLogEventType::SOCKET_IN_USE,
[email protected]ae1b30b2013-05-23 23:06:03282 source.ToEventParametersCallback());
[email protected]120d38d2012-12-14 01:42:32283
dchengc7eeda422015-12-26 03:56:48284 SocketLease* lease = new SocketLease(this, server_index, std::move(socket));
danakj22f90e72016-04-16 01:55:40285 return std::unique_ptr<SocketLease>(lease);
[email protected]120d38d2012-12-14 01:42:32286}
287
danakj22f90e72016-04-16 01:55:40288std::unique_ptr<StreamSocket> DnsSession::CreateTCPSocket(
289 unsigned server_index,
290 const NetLog::Source& source) {
[email protected]bdb65982012-12-20 20:44:59291 return socket_pool_->CreateTCPSocket(server_index, source);
292}
293
juliatuttled7fa5852016-07-29 13:16:57294void DnsSession::ApplyPersistentData(const base::Value& data) {}
295
296std::unique_ptr<const base::Value> DnsSession::GetPersistentData() const {
297 return std::unique_ptr<const base::Value>();
298}
299
[email protected]120d38d2012-12-14 01:42:32300// Release a socket.
[email protected]ae1b30b2013-05-23 23:06:03301void DnsSession::FreeSocket(unsigned server_index,
danakj22f90e72016-04-16 01:55:40302 std::unique_ptr<DatagramClientSocket> socket) {
[email protected]120d38d2012-12-14 01:42:32303 DCHECK(socket.get());
304
mikecirone8b85c432016-09-08 19:11:00305 socket->NetLog().EndEvent(NetLogEventType::SOCKET_IN_USE);
[email protected]120d38d2012-12-14 01:42:32306
dchengc7eeda422015-12-26 03:56:48307 socket_pool_->FreeSocket(server_index, std::move(socket));
[email protected]120d38d2012-12-14 01:42:32308}
[email protected]7556ea22011-12-08 19:29:15309
[email protected]ae1b30b2013-05-23 23:06:03310base::TimeDelta DnsSession::NextTimeoutFromJacobson(unsigned server_index,
311 int attempt) {
[email protected]a6c84f42013-06-07 20:39:38312 DCHECK_LT(server_index, server_stats_.size());
[email protected]ae1b30b2013-05-23 23:06:03313
[email protected]a6c84f42013-06-07 20:39:38314 base::TimeDelta timeout = server_stats_[server_index]->rtt_estimate +
315 4 * server_stats_[server_index]->rtt_deviation;
[email protected]ae1b30b2013-05-23 23:06:03316
317 timeout = std::max(timeout, base::TimeDelta::FromMilliseconds(kMinTimeoutMs));
318
319 // The timeout doubles every full round.
320 unsigned num_backoffs = attempt / config_.nameservers.size();
321
ttuttlecf1158bf2016-03-18 16:37:44322 return std::min(timeout * (1 << num_backoffs), max_timeout_);
[email protected]ae1b30b2013-05-23 23:06:03323}
324
325base::TimeDelta DnsSession::NextTimeoutFromHistogram(unsigned server_index,
326 int attempt) {
[email protected]a6c84f42013-06-07 20:39:38327 DCHECK_LT(server_index, server_stats_.size());
[email protected]ae1b30b2013-05-23 23:06:03328
mostynb91e0da982015-01-20 19:17:27329 static_assert(std::numeric_limits<base::HistogramBase::Count>::is_signed,
330 "histogram base count assumed to be signed");
[email protected]ae1b30b2013-05-23 23:06:03331
332 // Use fixed percentile of observed samples.
[email protected]a6c84f42013-06-07 20:39:38333 const base::SampleVector& samples =
334 *server_stats_[server_index]->rtt_histogram;
335
[email protected]ae1b30b2013-05-23 23:06:03336 base::HistogramBase::Count total = samples.TotalCount();
337 base::HistogramBase::Count remaining_count = kRTOPercentile * total / 100;
338 size_t index = 0;
[email protected]a6c84f42013-06-07 20:39:38339 while (remaining_count > 0 && index < rtt_buckets_.Get().size()) {
[email protected]ae1b30b2013-05-23 23:06:03340 remaining_count -= samples.GetCountAtIndex(index);
341 ++index;
342 }
343
344 base::TimeDelta timeout =
[email protected]a6c84f42013-06-07 20:39:38345 base::TimeDelta::FromMilliseconds(rtt_buckets_.Get().range(index));
[email protected]ae1b30b2013-05-23 23:06:03346
347 timeout = std::max(timeout, base::TimeDelta::FromMilliseconds(kMinTimeoutMs));
348
349 // The timeout still doubles every full round.
350 unsigned num_backoffs = attempt / config_.nameservers.size();
351
ttuttlecf1158bf2016-03-18 16:37:44352 return std::min(timeout * (1 << num_backoffs), max_timeout_);
[email protected]ae1b30b2013-05-23 23:06:03353}
354
[email protected]7556ea22011-12-08 19:29:15355} // namespace net