blob: 78e8461cbb3da199f2ebbe675904669295055353 [file] [log] [blame]
// Copyright (c) 2017 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "net/quic/quic_connectivity_probing_manager.h"
#include "base/bind.h"
#include "base/metrics/histogram_macros.h"
#include "base/strings/string_number_conversions.h"
#include "base/values.h"
#include "net/log/net_log.h"
namespace net {
namespace {
// Default to 2 seconds timeout as the maximum timeout.
const int64_t kMaxProbingTimeoutMs = 2000;
std::unique_ptr<base::Value> NetLogStartProbingCallback(
NetworkChangeNotifier::NetworkHandle network,
const quic::QuicSocketAddress* peer_address,
base::TimeDelta initial_timeout,
NetLogCaptureMode capture_mode) {
std::unique_ptr<base::DictionaryValue> dict(new base::DictionaryValue());
dict->SetKey("network", NetLogNumberValue(network));
dict->SetString("peer address", peer_address->ToString());
dict->SetKey("initial_timeout_ms",
NetLogNumberValue(initial_timeout.InMilliseconds()));
return std::move(dict);
}
std::unique_ptr<base::Value> NetLogProbeReceivedCallback(
NetworkChangeNotifier::NetworkHandle network,
const IPEndPoint* self_address,
const quic::QuicSocketAddress* peer_address,
NetLogCaptureMode capture_mode) {
std::unique_ptr<base::DictionaryValue> dict(new base::DictionaryValue());
dict->SetKey("network", NetLogNumberValue(network));
dict->SetString("self address", self_address->ToString());
dict->SetString("peer address", peer_address->ToString());
return std::move(dict);
}
std::unique_ptr<base::Value> NetLogProbingDestinationCallback(
NetworkChangeNotifier::NetworkHandle network,
const quic::QuicSocketAddress* peer_address,
NetLogCaptureMode capture_mode) {
std::unique_ptr<base::DictionaryValue> dict(new base::DictionaryValue());
dict->SetString("network", base::NumberToString(network));
dict->SetString("peer address", peer_address->ToString());
return std::move(dict);
}
} // namespace
QuicConnectivityProbingManager::QuicConnectivityProbingManager(
Delegate* delegate,
base::SequencedTaskRunner* task_runner)
: delegate_(delegate),
is_running_(false),
network_(NetworkChangeNotifier::kInvalidNetworkHandle),
retry_count_(0),
probe_start_time_(base::TimeTicks()),
task_runner_(task_runner),
weak_factory_(this) {
retransmit_timer_.SetTaskRunner(task_runner_);
}
QuicConnectivityProbingManager::~QuicConnectivityProbingManager() {
CancelProbingIfAny();
}
int QuicConnectivityProbingManager::HandleWriteError(
int error_code,
scoped_refptr<QuicChromiumPacketWriter::ReusableIOBuffer> packet) {
// Write error on the probing network is not recoverable.
DVLOG(1) << "Probing packet encounters write error";
// Post a task to notify |delegate_| that this probe failed and cancel
// undergoing probing, which will delete the packet writer.
task_runner_->PostTask(
FROM_HERE,
base::BindOnce(&QuicConnectivityProbingManager::NotifyDelegateProbeFailed,
weak_factory_.GetWeakPtr()));
return error_code;
}
void QuicConnectivityProbingManager::OnWriteError(int error_code) {
// Write error on the probing network.
NotifyDelegateProbeFailed();
}
void QuicConnectivityProbingManager::OnWriteUnblocked() {}
void QuicConnectivityProbingManager::CancelProbing(
NetworkChangeNotifier::NetworkHandle network,
const quic::QuicSocketAddress& peer_address) {
if (is_running_ && network == network_ && peer_address == peer_address_)
CancelProbingIfAny();
}
void QuicConnectivityProbingManager::CancelProbingIfAny() {
if (is_running_) {
net_log_.AddEvent(
NetLogEventType::QUIC_CONNECTIVITY_PROBING_MANAGER_CANCEL_PROBING,
base::Bind(&NetLogProbingDestinationCallback, network_,
&peer_address_));
}
is_running_ = false;
network_ = NetworkChangeNotifier::kInvalidNetworkHandle;
peer_address_ = quic::QuicSocketAddress();
socket_.reset();
writer_.reset();
reader_.reset();
retry_count_ = 0;
probe_start_time_ = base::TimeTicks();
initial_timeout_ = base::TimeDelta();
retransmit_timer_.Stop();
}
void QuicConnectivityProbingManager::StartProbing(
NetworkChangeNotifier::NetworkHandle network,
const quic::QuicSocketAddress& peer_address,
std::unique_ptr<DatagramClientSocket> socket,
std::unique_ptr<QuicChromiumPacketWriter> writer,
std::unique_ptr<QuicChromiumPacketReader> reader,
base::TimeDelta initial_timeout,
const NetLogWithSource& net_log) {
DCHECK(peer_address != quic::QuicSocketAddress());
if (IsUnderProbing(network, peer_address))
return;
// Start a new probe will always cancel the previous one.
CancelProbingIfAny();
is_running_ = true;
network_ = network;
peer_address_ = peer_address;
socket_ = std::move(socket);
writer_ = std::move(writer);
net_log_ = net_log;
probe_start_time_ = base::TimeTicks::Now();
// |this| will listen to all socket write events for the probing
// packet writer.
writer_->set_delegate(this);
reader_ = std::move(reader);
initial_timeout_ = initial_timeout;
net_log_.AddEvent(
NetLogEventType::QUIC_CONNECTIVITY_PROBING_MANAGER_START_PROBING,
base::Bind(&NetLogStartProbingCallback, network_, &peer_address_,
initial_timeout_));
reader_->StartReading();
SendConnectivityProbingPacket(initial_timeout_);
}
void QuicConnectivityProbingManager::OnConnectivityProbingReceived(
const quic::QuicSocketAddress& self_address,
const quic::QuicSocketAddress& peer_address) {
if (!socket_) {
DVLOG(1) << "Probing response is ignored as probing was cancelled "
<< "or succeeded.";
return;
}
IPEndPoint local_address;
socket_->GetLocalAddress(&local_address);
DVLOG(1) << "Current probing is live at self ip:port "
<< local_address.ToString() << ", to peer ip:port "
<< peer_address_.ToString();
if (quic::QuicSocketAddressImpl(local_address) != self_address.impl() ||
peer_address_ != peer_address) {
DVLOG(1) << "Received probing response from peer ip:port "
<< peer_address.ToString() << ", to self ip:port "
<< self_address.ToString() << ". Ignored.";
return;
}
net_log_.AddEvent(
NetLogEventType::QUIC_CONNECTIVITY_PROBING_MANAGER_PROBE_RECEIVED,
base::Bind(&NetLogProbeReceivedCallback, network_, &local_address,
&peer_address_));
UMA_HISTOGRAM_COUNTS_100("Net.QuicSession.ProbingRetryCountUntilSuccess",
retry_count_);
UMA_HISTOGRAM_TIMES("Net.QuicSession.ProbingTimeInMillisecondsUntilSuccess",
base::TimeTicks::Now() - probe_start_time_);
// Notify the delegate that the probe succeeds and reset everything.
delegate_->OnProbeSucceeded(network_, peer_address_, self_address,
std::move(socket_), std::move(writer_),
std::move(reader_));
CancelProbingIfAny();
}
void QuicConnectivityProbingManager::SendConnectivityProbingPacket(
base::TimeDelta timeout) {
net_log_.AddEvent(
NetLogEventType::QUIC_CONNECTIVITY_PROBING_MANAGER_PROBE_SENT,
NetLog::Int64Callback("sent_count", retry_count_));
if (!delegate_->OnSendConnectivityProbingPacket(writer_.get(),
peer_address_)) {
NotifyDelegateProbeFailed();
return;
}
retransmit_timer_.Start(
FROM_HERE, timeout,
base::Bind(
&QuicConnectivityProbingManager::MaybeResendConnectivityProbingPacket,
weak_factory_.GetWeakPtr()));
}
void QuicConnectivityProbingManager::NotifyDelegateProbeFailed() {
if (is_running_) {
delegate_->OnProbeFailed(network_, peer_address_);
CancelProbingIfAny();
}
}
void QuicConnectivityProbingManager::MaybeResendConnectivityProbingPacket() {
// Use exponential backoff for the timeout.
retry_count_++;
int64_t timeout_ms =
(UINT64_C(1) << retry_count_) * initial_timeout_.InMilliseconds();
if (timeout_ms > kMaxProbingTimeoutMs) {
NotifyDelegateProbeFailed();
return;
}
SendConnectivityProbingPacket(base::TimeDelta::FromMilliseconds(timeout_ms));
}
} // namespace net