blob: 82b947e12f72b703a3c5eeecbf4da41c62d74fc3 [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"
[email protected]7556ea22011-12-08 19:29:1522#include "net/base/ip_endpoint.h"
[email protected]120d38d2012-12-14 01:42:3223#include "net/base/net_errors.h"
[email protected]7556ea22011-12-08 19:29:1524#include "net/dns/dns_config_service.h"
[email protected]120d38d2012-12-14 01:42:3225#include "net/dns/dns_socket_pool.h"
ttuttlecf1158bf2016-03-18 16:37:4426#include "net/dns/dns_util.h"
[email protected]bdb65982012-12-20 20:44:5927#include "net/socket/stream_socket.h"
28#include "net/udp/datagram_client_socket.h"
[email protected]7556ea22011-12-08 19:29:1529
30namespace net {
31
[email protected]ae1b30b2013-05-23 23:06:0332namespace {
ttuttlecf1158bf2016-03-18 16:37:4433
[email protected]ae1b30b2013-05-23 23:06:0334// Set min timeout, in case we are talking to a local DNS proxy.
35const unsigned kMinTimeoutMs = 10;
36
ttuttlecf1158bf2016-03-18 16:37:4437// Default maximum timeout between queries, even with exponential backoff.
38// (Can be overridden by field trial.)
39const unsigned kDefaultMaxTimeoutMs = 5000;
40
41// Maximum RTT that will fit in the RTT histograms.
42const int32_t kRTTMaxMs = 30000;
[email protected]ae1b30b2013-05-23 23:06:0343// Number of buckets in the histogram of observed RTTs.
ttuttlecf1158bf2016-03-18 16:37:4444const size_t kRTTBucketCount = 350;
[email protected]ae1b30b2013-05-23 23:06:0345// Target percentile in the RTT histogram used for retransmission timeout.
46const unsigned kRTOPercentile = 99;
ttuttlecf1158bf2016-03-18 16:37:4447
[email protected]ae1b30b2013-05-23 23:06:0348} // namespace
49
[email protected]a6c84f42013-06-07 20:39:3850// Runtime statistics of DNS server.
51struct DnsSession::ServerStats {
52 ServerStats(base::TimeDelta rtt_estimate_param, RttBuckets* buckets)
53 : last_failure_count(0), rtt_estimate(rtt_estimate_param) {
54 rtt_histogram.reset(new base::SampleVector(buckets));
[email protected]a144bd22013-07-29 21:53:1055 // Seed histogram with 2 samples at |rtt_estimate| timeout.
pkasting6b68a162014-12-01 22:10:2956 rtt_histogram->Accumulate(
57 static_cast<base::HistogramBase::Sample>(rtt_estimate.InMilliseconds()),
58 2);
[email protected]a6c84f42013-06-07 20:39:3859 }
60
61 // Count of consecutive failures after last success.
62 int last_failure_count;
63
64 // Last time when server returned failure or timeout.
65 base::Time last_failure;
66 // Last time when server returned success.
67 base::Time last_success;
68
69 // Estimated RTT using moving average.
70 base::TimeDelta rtt_estimate;
71 // Estimated error in the above.
72 base::TimeDelta rtt_deviation;
73
74 // A histogram of observed RTT .
danakj22f90e72016-04-16 01:55:4075 std::unique_ptr<base::SampleVector> rtt_histogram;
[email protected]a6c84f42013-06-07 20:39:3876
77 DISALLOW_COPY_AND_ASSIGN(ServerStats);
78};
79
80// static
81base::LazyInstance<DnsSession::RttBuckets>::Leaky DnsSession::rtt_buckets_ =
82 LAZY_INSTANCE_INITIALIZER;
83
84DnsSession::RttBuckets::RttBuckets() : base::BucketRanges(kRTTBucketCount + 1) {
ttuttlecf1158bf2016-03-18 16:37:4485 base::Histogram::InitializeBucketRanges(1, kRTTMaxMs, this);
[email protected]a6c84f42013-06-07 20:39:3886}
87
danakj22f90e72016-04-16 01:55:4088DnsSession::SocketLease::SocketLease(
89 scoped_refptr<DnsSession> session,
90 unsigned server_index,
91 std::unique_ptr<DatagramClientSocket> socket)
dchengc7eeda422015-12-26 03:56:4892 : session_(session),
93 server_index_(server_index),
94 socket_(std::move(socket)) {}
[email protected]120d38d2012-12-14 01:42:3295
96DnsSession::SocketLease::~SocketLease() {
dchengc7eeda422015-12-26 03:56:4897 session_->FreeSocket(server_index_, std::move(socket_));
[email protected]120d38d2012-12-14 01:42:3298}
99
[email protected]7556ea22011-12-08 19:29:15100DnsSession::DnsSession(const DnsConfig& config,
danakj22f90e72016-04-16 01:55:40101 std::unique_ptr<DnsSocketPool> socket_pool,
[email protected]7556ea22011-12-08 19:29:15102 const RandIntCallback& rand_int_callback,
103 NetLog* net_log)
104 : config_(config),
dchengc7eeda422015-12-26 03:56:48105 socket_pool_(std::move(socket_pool)),
avi65168052015-12-01 19:27:07106 rand_callback_(base::Bind(rand_int_callback,
107 0,
108 std::numeric_limits<uint16_t>::max())),
[email protected]7556ea22011-12-08 19:29:15109 net_log_(net_log),
[email protected]a6c84f42013-06-07 20:39:38110 server_index_(0) {
[email protected]120d38d2012-12-14 01:42:32111 socket_pool_->Initialize(&config_.nameservers, net_log);
[email protected]a6c84f42013-06-07 20:39:38112 UMA_HISTOGRAM_CUSTOM_COUNTS(
timvolodine23be97452014-09-26 15:44:01113 "AsyncDNS.ServerCount", config_.nameservers.size(), 0, 10, 11);
ttuttlecf1158bf2016-03-18 16:37:44114 UpdateTimeouts(NetworkChangeNotifier::GetConnectionType());
115 InitializeServerStats();
116 NetworkChangeNotifier::AddConnectionTypeObserver(this);
[email protected]120d38d2012-12-14 01:42:32117}
118
[email protected]a6c84f42013-06-07 20:39:38119DnsSession::~DnsSession() {
120 RecordServerStats();
ttuttlecf1158bf2016-03-18 16:37:44121 NetworkChangeNotifier::RemoveConnectionTypeObserver(this);
122}
123
124void DnsSession::UpdateTimeouts(NetworkChangeNotifier::ConnectionType type) {
125 initial_timeout_ = GetTimeDeltaForConnectionTypeFromFieldTrialOrDefault(
126 "AsyncDnsInitialTimeoutMsByConnectionType", config_.timeout, type);
127 max_timeout_ = GetTimeDeltaForConnectionTypeFromFieldTrialOrDefault(
128 "AsyncDnsMaxTimeoutMsByConnectionType",
129 base::TimeDelta::FromMilliseconds(kDefaultMaxTimeoutMs), type);
130}
131
132void DnsSession::InitializeServerStats() {
133 server_stats_.clear();
134 for (size_t i = 0; i < config_.nameservers.size(); ++i) {
danakj22f90e72016-04-16 01:55:40135 server_stats_.push_back(base::WrapUnique(
ttuttlecf1158bf2016-03-18 16:37:44136 new ServerStats(initial_timeout_, rtt_buckets_.Pointer())));
137 }
138}
139
140void DnsSession::OnConnectionTypeChanged(
141 NetworkChangeNotifier::ConnectionType type) {
142 UpdateTimeouts(type);
143 const char* kTrialName = "AsyncDnsFlushServerStatsOnConnectionTypeChange";
144 if (base::FieldTrialList::FindFullName(kTrialName) == "enable") {
145 RecordServerStats();
146 InitializeServerStats();
147 }
[email protected]a6c84f42013-06-07 20:39:38148}
[email protected]7556ea22011-12-08 19:29:15149
avi65168052015-12-01 19:27:07150uint16_t DnsSession::NextQueryId() const {
151 return static_cast<uint16_t>(rand_callback_.Run());
pkasting6b68a162014-12-01 22:10:29152}
[email protected]7556ea22011-12-08 19:29:15153
[email protected]a6c84f42013-06-07 20:39:38154unsigned DnsSession::NextFirstServerIndex() {
155 unsigned index = NextGoodServerIndex(server_index_);
[email protected]7556ea22011-12-08 19:29:15156 if (config_.rotate)
157 server_index_ = (server_index_ + 1) % config_.nameservers.size();
[email protected]d0d49dd82012-01-26 00:03:59158 return index;
[email protected]7556ea22011-12-08 19:29:15159}
160
[email protected]a6c84f42013-06-07 20:39:38161unsigned DnsSession::NextGoodServerIndex(unsigned server_index) {
162 unsigned index = server_index;
163 base::Time oldest_server_failure(base::Time::Now());
164 unsigned oldest_server_failure_index = 0;
165
166 UMA_HISTOGRAM_BOOLEAN("AsyncDNS.ServerIsGood",
167 server_stats_[server_index]->last_failure.is_null());
168
169 do {
170 base::Time cur_server_failure = server_stats_[index]->last_failure;
171 // If number of failures on this server doesn't exceed number of allowed
172 // attempts, return its index.
173 if (server_stats_[server_index]->last_failure_count < config_.attempts) {
174 return index;
175 }
176 // Track oldest failed server.
177 if (cur_server_failure < oldest_server_failure) {
178 oldest_server_failure = cur_server_failure;
179 oldest_server_failure_index = index;
180 }
181 index = (index + 1) % config_.nameservers.size();
182 } while (index != server_index);
183
184 // If we are here it means that there are no successful servers, so we have
185 // to use one that has failed oldest.
186 return oldest_server_failure_index;
187}
188
189void DnsSession::RecordServerFailure(unsigned server_index) {
190 UMA_HISTOGRAM_CUSTOM_COUNTS(
timvolodine23be97452014-09-26 15:44:01191 "AsyncDNS.ServerFailureIndex", server_index, 0, 10, 11);
[email protected]a6c84f42013-06-07 20:39:38192 ++(server_stats_[server_index]->last_failure_count);
193 server_stats_[server_index]->last_failure = base::Time::Now();
194}
195
196void DnsSession::RecordServerSuccess(unsigned server_index) {
197 if (server_stats_[server_index]->last_success.is_null()) {
198 UMA_HISTOGRAM_COUNTS_100("AsyncDNS.ServerFailuresAfterNetworkChange",
199 server_stats_[server_index]->last_failure_count);
200 } else {
201 UMA_HISTOGRAM_COUNTS_100("AsyncDNS.ServerFailuresBeforeSuccess",
202 server_stats_[server_index]->last_failure_count);
203 }
204 server_stats_[server_index]->last_failure_count = 0;
205 server_stats_[server_index]->last_failure = base::Time();
206 server_stats_[server_index]->last_success = base::Time::Now();
207}
208
[email protected]ae1b30b2013-05-23 23:06:03209void DnsSession::RecordRTT(unsigned server_index, base::TimeDelta rtt) {
[email protected]a6c84f42013-06-07 20:39:38210 DCHECK_LT(server_index, server_stats_.size());
[email protected]ae1b30b2013-05-23 23:06:03211
212 // For measurement, assume it is the first attempt (no backoff).
213 base::TimeDelta timeout_jacobson = NextTimeoutFromJacobson(server_index, 0);
214 base::TimeDelta timeout_histogram = NextTimeoutFromHistogram(server_index, 0);
215 UMA_HISTOGRAM_TIMES("AsyncDNS.TimeoutErrorJacobson", rtt - timeout_jacobson);
216 UMA_HISTOGRAM_TIMES("AsyncDNS.TimeoutErrorHistogram",
217 rtt - timeout_histogram);
218 UMA_HISTOGRAM_TIMES("AsyncDNS.TimeoutErrorJacobsonUnder",
219 timeout_jacobson - rtt);
220 UMA_HISTOGRAM_TIMES("AsyncDNS.TimeoutErrorHistogramUnder",
221 timeout_histogram - rtt);
222
223 // Jacobson/Karels algorithm for TCP.
224 // Using parameters: alpha = 1/8, delta = 1/4, beta = 4
[email protected]a6c84f42013-06-07 20:39:38225 base::TimeDelta& estimate = server_stats_[server_index]->rtt_estimate;
226 base::TimeDelta& deviation = server_stats_[server_index]->rtt_deviation;
[email protected]ae1b30b2013-05-23 23:06:03227 base::TimeDelta current_error = rtt - estimate;
228 estimate += current_error / 8; // * alpha
229 base::TimeDelta abs_error = base::TimeDelta::FromInternalValue(
230 std::abs(current_error.ToInternalValue()));
231 deviation += (abs_error - deviation) / 4; // * delta
232
233 // Histogram-based method.
pkasting6b68a162014-12-01 22:10:29234 server_stats_[server_index]->rtt_histogram->Accumulate(
235 static_cast<base::HistogramBase::Sample>(rtt.InMilliseconds()), 1);
[email protected]ae1b30b2013-05-23 23:06:03236}
237
238void DnsSession::RecordLostPacket(unsigned server_index, int attempt) {
239 base::TimeDelta timeout_jacobson =
240 NextTimeoutFromJacobson(server_index, attempt);
241 base::TimeDelta timeout_histogram =
242 NextTimeoutFromHistogram(server_index, attempt);
243 UMA_HISTOGRAM_TIMES("AsyncDNS.TimeoutSpentJacobson", timeout_jacobson);
244 UMA_HISTOGRAM_TIMES("AsyncDNS.TimeoutSpentHistogram", timeout_histogram);
245}
246
[email protected]a6c84f42013-06-07 20:39:38247void DnsSession::RecordServerStats() {
248 for (size_t index = 0; index < server_stats_.size(); ++index) {
249 if (server_stats_[index]->last_failure_count) {
250 if (server_stats_[index]->last_success.is_null()) {
251 UMA_HISTOGRAM_COUNTS("AsyncDNS.ServerFailuresWithoutSuccess",
252 server_stats_[index]->last_failure_count);
253 } else {
254 UMA_HISTOGRAM_COUNTS("AsyncDNS.ServerFailuresAfterSuccess",
255 server_stats_[index]->last_failure_count);
256 }
257 }
258 }
259}
260
261
[email protected]ae1b30b2013-05-23 23:06:03262base::TimeDelta DnsSession::NextTimeout(unsigned server_index, int attempt) {
ttuttlecf1158bf2016-03-18 16:37:44263 // Respect initial timeout (from config or field trial) if it exceeds max.
264 if (initial_timeout_ > max_timeout_)
265 return initial_timeout_;
[email protected]a144bd22013-07-29 21:53:10266 return NextTimeoutFromHistogram(server_index, attempt);
[email protected]7556ea22011-12-08 19:29:15267}
268
[email protected]120d38d2012-12-14 01:42:32269// Allocate a socket, already connected to the server address.
danakj22f90e72016-04-16 01:55:40270std::unique_ptr<DnsSession::SocketLease> DnsSession::AllocateSocket(
271 unsigned server_index,
272 const NetLog::Source& source) {
273 std::unique_ptr<DatagramClientSocket> socket;
[email protected]120d38d2012-12-14 01:42:32274
275 socket = socket_pool_->AllocateSocket(server_index);
[email protected]dd946bb2013-06-12 22:53:01276 if (!socket.get())
danakj22f90e72016-04-16 01:55:40277 return std::unique_ptr<SocketLease>();
[email protected]120d38d2012-12-14 01:42:32278
[email protected]ae1b30b2013-05-23 23:06:03279 socket->NetLog().BeginEvent(NetLog::TYPE_SOCKET_IN_USE,
280 source.ToEventParametersCallback());
[email protected]120d38d2012-12-14 01:42:32281
dchengc7eeda422015-12-26 03:56:48282 SocketLease* lease = new SocketLease(this, server_index, std::move(socket));
danakj22f90e72016-04-16 01:55:40283 return std::unique_ptr<SocketLease>(lease);
[email protected]120d38d2012-12-14 01:42:32284}
285
danakj22f90e72016-04-16 01:55:40286std::unique_ptr<StreamSocket> DnsSession::CreateTCPSocket(
287 unsigned server_index,
288 const NetLog::Source& source) {
[email protected]bdb65982012-12-20 20:44:59289 return socket_pool_->CreateTCPSocket(server_index, source);
290}
291
[email protected]120d38d2012-12-14 01:42:32292// Release a socket.
[email protected]ae1b30b2013-05-23 23:06:03293void DnsSession::FreeSocket(unsigned server_index,
danakj22f90e72016-04-16 01:55:40294 std::unique_ptr<DatagramClientSocket> socket) {
[email protected]120d38d2012-12-14 01:42:32295 DCHECK(socket.get());
296
297 socket->NetLog().EndEvent(NetLog::TYPE_SOCKET_IN_USE);
298
dchengc7eeda422015-12-26 03:56:48299 socket_pool_->FreeSocket(server_index, std::move(socket));
[email protected]120d38d2012-12-14 01:42:32300}
[email protected]7556ea22011-12-08 19:29:15301
[email protected]ae1b30b2013-05-23 23:06:03302base::TimeDelta DnsSession::NextTimeoutFromJacobson(unsigned server_index,
303 int attempt) {
[email protected]a6c84f42013-06-07 20:39:38304 DCHECK_LT(server_index, server_stats_.size());
[email protected]ae1b30b2013-05-23 23:06:03305
[email protected]a6c84f42013-06-07 20:39:38306 base::TimeDelta timeout = server_stats_[server_index]->rtt_estimate +
307 4 * server_stats_[server_index]->rtt_deviation;
[email protected]ae1b30b2013-05-23 23:06:03308
309 timeout = std::max(timeout, base::TimeDelta::FromMilliseconds(kMinTimeoutMs));
310
311 // The timeout doubles every full round.
312 unsigned num_backoffs = attempt / config_.nameservers.size();
313
ttuttlecf1158bf2016-03-18 16:37:44314 return std::min(timeout * (1 << num_backoffs), max_timeout_);
[email protected]ae1b30b2013-05-23 23:06:03315}
316
317base::TimeDelta DnsSession::NextTimeoutFromHistogram(unsigned server_index,
318 int attempt) {
[email protected]a6c84f42013-06-07 20:39:38319 DCHECK_LT(server_index, server_stats_.size());
[email protected]ae1b30b2013-05-23 23:06:03320
mostynb91e0da982015-01-20 19:17:27321 static_assert(std::numeric_limits<base::HistogramBase::Count>::is_signed,
322 "histogram base count assumed to be signed");
[email protected]ae1b30b2013-05-23 23:06:03323
324 // Use fixed percentile of observed samples.
[email protected]a6c84f42013-06-07 20:39:38325 const base::SampleVector& samples =
326 *server_stats_[server_index]->rtt_histogram;
327
[email protected]ae1b30b2013-05-23 23:06:03328 base::HistogramBase::Count total = samples.TotalCount();
329 base::HistogramBase::Count remaining_count = kRTOPercentile * total / 100;
330 size_t index = 0;
[email protected]a6c84f42013-06-07 20:39:38331 while (remaining_count > 0 && index < rtt_buckets_.Get().size()) {
[email protected]ae1b30b2013-05-23 23:06:03332 remaining_count -= samples.GetCountAtIndex(index);
333 ++index;
334 }
335
336 base::TimeDelta timeout =
[email protected]a6c84f42013-06-07 20:39:38337 base::TimeDelta::FromMilliseconds(rtt_buckets_.Get().range(index));
[email protected]ae1b30b2013-05-23 23:06:03338
339 timeout = std::max(timeout, base::TimeDelta::FromMilliseconds(kMinTimeoutMs));
340
341 // The timeout still doubles every full round.
342 unsigned num_backoffs = attempt / config_.nameservers.size();
343
ttuttlecf1158bf2016-03-18 16:37:44344 return std::min(timeout * (1 << num_backoffs), max_timeout_);
[email protected]ae1b30b2013-05-23 23:06:03345}
346
[email protected]7556ea22011-12-08 19:29:15347} // namespace net