blob: 52220d28161490ea4f331f7a8aaea1f17cd65200 [file] [log] [blame]
// Copyright 2012 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "net/base/network_change_notifier_linux.h"
#include <string>
#include "base/compiler_specific.h"
#include "base/functional/bind.h"
#include "base/functional/callback_helpers.h"
#include "base/task/sequenced_task_runner.h"
#include "base/task/task_traits.h"
#include "base/task/thread_pool.h"
#include "base/threading/thread.h"
#include "net/base/address_tracker_linux.h"
#include "net/dns/dns_config_service_posix.h"
namespace net {
// A collection of objects that live on blocking threads.
class NetworkChangeNotifierLinux::BlockingThreadObjects {
public:
explicit BlockingThreadObjects(
const std::unordered_set<std::string>& ignored_interfaces,
scoped_refptr<base::SequencedTaskRunner> blocking_thread_runner);
BlockingThreadObjects(const BlockingThreadObjects&) = delete;
BlockingThreadObjects& operator=(const BlockingThreadObjects&) = delete;
// Plumbing for NetworkChangeNotifier::GetCurrentConnectionType.
// Safe to call from any thread.
NetworkChangeNotifier::ConnectionType GetCurrentConnectionType() {
return address_tracker_.GetCurrentConnectionType();
}
internal::AddressTrackerLinux* address_tracker() { return &address_tracker_; }
// Begin watching for netlink changes.
void Init();
void InitForTesting(base::ScopedFD netlink_fd); // IN-TEST
private:
void OnIPAddressChanged();
void OnLinkChanged();
// Used to detect online/offline state and IP address changes.
internal::AddressTrackerLinux address_tracker_;
NetworkChangeNotifier::ConnectionType last_type_ =
NetworkChangeNotifier::CONNECTION_NONE;
};
NetworkChangeNotifierLinux::BlockingThreadObjects::BlockingThreadObjects(
const std::unordered_set<std::string>& ignored_interfaces,
scoped_refptr<base::SequencedTaskRunner> blocking_thread_runner)
: address_tracker_(
base::BindRepeating(&NetworkChangeNotifierLinux::
BlockingThreadObjects::OnIPAddressChanged,
base::Unretained(this)),
base::BindRepeating(
&NetworkChangeNotifierLinux::BlockingThreadObjects::OnLinkChanged,
base::Unretained(this)),
base::DoNothing(),
ignored_interfaces,
std::move(blocking_thread_runner)) {}
void NetworkChangeNotifierLinux::BlockingThreadObjects::Init() {
address_tracker_.Init();
last_type_ = GetCurrentConnectionType();
}
void NetworkChangeNotifierLinux::BlockingThreadObjects::InitForTesting(
base::ScopedFD netlink_fd) {
address_tracker_.InitWithFdForTesting(std::move(netlink_fd)); // IN-TEST
last_type_ = GetCurrentConnectionType();
}
void NetworkChangeNotifierLinux::BlockingThreadObjects::OnIPAddressChanged() {
NetworkChangeNotifier::NotifyObserversOfIPAddressChange();
// When the IP address of a network interface is added/deleted, the
// connection type may have changed.
OnLinkChanged();
}
void NetworkChangeNotifierLinux::BlockingThreadObjects::OnLinkChanged() {
if (last_type_ != GetCurrentConnectionType()) {
NetworkChangeNotifier::NotifyObserversOfConnectionTypeChange();
last_type_ = GetCurrentConnectionType();
double max_bandwidth_mbps =
NetworkChangeNotifier::GetMaxBandwidthMbpsForConnectionSubtype(
last_type_ == CONNECTION_NONE ? SUBTYPE_NONE : SUBTYPE_UNKNOWN);
NetworkChangeNotifier::NotifyObserversOfMaxBandwidthChange(
max_bandwidth_mbps, last_type_);
}
}
// static
std::unique_ptr<NetworkChangeNotifierLinux>
NetworkChangeNotifierLinux::CreateWithSocketForTesting(
const std::unordered_set<std::string>& ignored_interfaces,
base::ScopedFD netlink_fd) {
auto ncn_linux = std::make_unique<NetworkChangeNotifierLinux>(
ignored_interfaces, /*initialize_blocking_thread_objects=*/false,
base::PassKey<NetworkChangeNotifierLinux>());
ncn_linux->InitBlockingThreadObjectsForTesting( // IN-TEST
std::move(netlink_fd));
return ncn_linux;
}
NetworkChangeNotifierLinux::NetworkChangeNotifierLinux(
const std::unordered_set<std::string>& ignored_interfaces)
: NetworkChangeNotifierLinux(ignored_interfaces,
/*initialize_blocking_thread_objects*/ true,
base::PassKey<NetworkChangeNotifierLinux>()) {}
NetworkChangeNotifierLinux::NetworkChangeNotifierLinux(
const std::unordered_set<std::string>& ignored_interfaces,
bool initialize_blocking_thread_objects,
base::PassKey<NetworkChangeNotifierLinux>)
: NetworkChangeNotifier(NetworkChangeCalculatorParamsLinux()),
blocking_thread_runner_(
base::ThreadPool::CreateSequencedTaskRunner({base::MayBlock()})),
blocking_thread_objects_(
new BlockingThreadObjects(ignored_interfaces,
blocking_thread_runner_),
// Ensure |blocking_thread_objects_| lives on
// |blocking_thread_runner_| to prevent races where
// NetworkChangeNotifierLinux outlives
// TaskEnvironment. https://crbug.com/938126
base::OnTaskRunnerDeleter(blocking_thread_runner_)) {
if (initialize_blocking_thread_objects) {
blocking_thread_runner_->PostTask(
FROM_HERE,
base::BindOnce(&NetworkChangeNotifierLinux::BlockingThreadObjects::Init,
// The Unretained pointer is safe here because it's
// posted before the deleter can post.
base::Unretained(blocking_thread_objects_.get())));
}
}
NetworkChangeNotifierLinux::~NetworkChangeNotifierLinux() {
ClearGlobalPointer();
}
// static
NetworkChangeNotifier::NetworkChangeCalculatorParams
NetworkChangeNotifierLinux::NetworkChangeCalculatorParamsLinux() {
NetworkChangeCalculatorParams params;
// Delay values arrived at by simple experimentation and adjusted so as to
// produce a single signal when switching between network connections.
params.ip_address_offline_delay_ = base::Milliseconds(2000);
params.ip_address_online_delay_ = base::Milliseconds(2000);
params.connection_type_offline_delay_ = base::Milliseconds(1500);
params.connection_type_online_delay_ = base::Milliseconds(500);
return params;
}
void NetworkChangeNotifierLinux::InitBlockingThreadObjectsForTesting(
base::ScopedFD netlink_fd) {
DCHECK(blocking_thread_objects_);
blocking_thread_runner_->PostTask(
FROM_HERE,
base::BindOnce(
&NetworkChangeNotifierLinux::BlockingThreadObjects::InitForTesting,
// The Unretained pointer is safe here because it's
// posted before the deleter can post.
base::Unretained(blocking_thread_objects_.get()),
std::move(netlink_fd)));
}
NetworkChangeNotifier::ConnectionType
NetworkChangeNotifierLinux::GetCurrentConnectionType() const {
return blocking_thread_objects_->GetCurrentConnectionType();
}
AddressMapOwnerLinux* NetworkChangeNotifierLinux::GetAddressMapOwnerInternal() {
return blocking_thread_objects_->address_tracker();
}
} // namespace net