blob: b6689733d687da8781f942921707da6f423733c5 [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.
#ifndef NET_BASE_ADDRESS_TRACKER_LINUX_H_
#define NET_BASE_ADDRESS_TRACKER_LINUX_H_
#include <sys/socket.h> // Needed to include netlink.
// Mask superfluous definition of |struct net|. This is fixed in Linux 2.6.38.
#define net net_kernel
#include <linux/rtnetlink.h>
#undef net
#include <stddef.h>
#include <map>
#include <memory>
#include <string>
#include <unordered_set>
#include "base/compiler_specific.h"
#include "base/files/file_descriptor_watcher_posix.h"
#include "base/files/scoped_file.h"
#include "base/functional/callback.h"
#include "base/gtest_prod_util.h"
#include "base/memory/raw_ref.h"
#include "base/sequence_checker.h"
#include "base/synchronization/condition_variable.h"
#include "base/synchronization/lock.h"
#include "base/task/sequenced_task_runner.h"
#include "base/thread_annotations.h"
#include "net/base/address_map_linux.h"
#include "net/base/ip_address.h"
#include "net/base/net_export.h"
#include "net/base/network_change_notifier.h"
namespace net::test {
class AddressTrackerLinuxTest;
}
namespace net::internal {
// Keeps track of network interface addresses using rtnetlink. Used by
// NetworkChangeNotifier to provide signals to registered IPAddressObservers.
//
// In tracking mode, this class should mostly be used on a single sequence,
// except GetAddressMap() and GetOnlineLinks() (AddressMapOwnerLinux overrides)
// which can be called on any thread. The main sequence should be able to block
// (e.g. use a base::SequencedTaskRunner with base::MayBlock()).
//
// In non-tracking mode this should be used on a single thread.
class NET_EXPORT_PRIVATE AddressTrackerLinux : public AddressMapOwnerLinux {
public:
// Non-tracking version constructor: it takes a snapshot of the
// current system configuration. Once Init() returns, the
// configuration is available through GetOnlineLinks() and
// GetAddressMap().
AddressTrackerLinux();
// Tracking version constructor: it will run |address_callback| when
// the AddressMap changes, |link_callback| when the list of online
// links changes, and |tunnel_callback| when the list of online
// tunnels changes.
// |ignored_interfaces| is the list of interfaces to ignore. Changes to an
// ignored interface will not cause any callback to be run. An ignored
// interface will not have entries in GetAddressMap() and GetOnlineLinks().
// NOTE: Only ignore interfaces not used to connect to the internet. Adding
// interfaces used to connect to the internet can cause critical network
// changed signals to be lost allowing incorrect stale state to persist.
//
// |blocking_thread_runner| is the sequence on which this AddressTrackerLinux
// will run. The AddressTrackerLinux can block in tracking mode and so it
// should run on a sequence that can block, e.g. a base::SequencedTaskRunner
// with base::MayBlock(). If nullptr, SetDiffCallback() cannot be used off of
// the AddressTrackerLinux's sequence.
AddressTrackerLinux(const base::RepeatingClosure& address_callback,
const base::RepeatingClosure& link_callback,
const base::RepeatingClosure& tunnel_callback,
const std::unordered_set<std::string>& ignored_interfaces,
scoped_refptr<base::SequencedTaskRunner>
blocking_thread_runner = nullptr);
~AddressTrackerLinux() override;
// In tracking mode, it starts watching the system configuration for
// changes. The current thread must have a MessageLoopForIO. In
// non-tracking mode, once Init() returns, a snapshot of the system
// configuration is available through GetOnlineLinks() and
// GetAddressMap().
void Init();
// Same as Init(), except instead of creating and binding a netlink socket,
// this AddressTrackerLinux will send and receive messages from |fd|.
void InitWithFdForTesting(base::ScopedFD fd);
// AddressMapOwnerLinux implementation (callable on any thread):
AddressMap GetAddressMap() const override;
// Returns set of interface indices for online interfaces.
std::unordered_set<int> GetOnlineLinks() const override;
AddressTrackerLinux* GetAddressTrackerLinux() override;
// This returns the current AddressMap and set of online links, and atomically
// starts recording diffs to those structures. This can be called on any
// thread, and must be called called before SetDiffCallback() below. Available
// only in tracking mode.
std::pair<AddressMap, std::unordered_set<int>>
GetInitialDataAndStartRecordingDiffs();
// Called after GetInitialDataAndStartRecordingDiffs().
//
// Whenever the AddressMap or the set of online links (returned by the above
// two methods) changes, this callback is called on AddressTrackerLinux's
// sequence. On the first call, |diff_callback| is called synchronously with
// the set of diffs that have been built since
// GetInitialDataAndStartRecordingDiffs() was called. If there are none,
// |diff_callback| won't be called.
//
// This is only available in tracking mode. It can be called on any thread,
// but it will post a task to the AddressTrackerLinux's sequence and therefore
// will finish asynchronously. The caller MUST ENSURE that the
// AddressTrackerLinux is not deleted until this task finishes.
// This also requires |sequenced_task_runner_| to be set by the
// AddressTrackerLinux constructor above.
//
// Note that other threads may see updated AddressMaps by calling
// GetAddressMap() before |diff_callback| is ever called.
void SetDiffCallback(DiffCallback diff_callback);
// Implementation of NetworkChangeNotifierLinux::GetCurrentConnectionType().
// Safe to call from any thread, but will block until Init() has completed.
NetworkChangeNotifier::ConnectionType GetCurrentConnectionType();
// Returns the name for the interface with interface index |interface_index|.
// |buf| should be a pointer to an array of size IFNAMSIZ. The returned
// pointer will point to |buf|. This function acts like if_indextoname which
// cannot be used as net/if.h cannot be mixed with linux/if.h. We'll stick
// with exclusively talking to the kernel and not the C library.
static char* GetInterfaceName(int interface_index, char* buf);
// Does |name| refer to a tunnel interface?
static bool IsTunnelInterfaceName(const char* name);
private:
friend class net::test::AddressTrackerLinuxTest;
FRIEND_TEST_ALL_PREFIXES(AddressTrackerLinuxNetlinkTest,
TestInitializeTwoTrackers);
FRIEND_TEST_ALL_PREFIXES(AddressTrackerLinuxNetlinkTest,
TestInitializeTwoTrackersInPidNamespaces);
friend int ChildProcessInitializeTrackerForTesting();
// In tracking mode, holds |lock| while alive. In non-tracking mode,
// enforces single-threaded access.
class SCOPED_LOCKABLE AddressTrackerAutoLock {
public:
AddressTrackerAutoLock(const AddressTrackerLinux& tracker, base::Lock& lock)
EXCLUSIVE_LOCK_FUNCTION(lock);
AddressTrackerAutoLock(const AddressTrackerAutoLock&) = delete;
AddressTrackerAutoLock& operator=(const AddressTrackerAutoLock&) = delete;
~AddressTrackerAutoLock() UNLOCK_FUNCTION();
private:
const raw_ref<const AddressTrackerLinux> tracker_;
const raw_ref<base::Lock> lock_;
};
// A function that returns the name of an interface given the interface index
// in |interface_index|. |ifname| should be a buffer of size IFNAMSIZ. The
// function should return a pointer to |ifname|.
typedef char* (*GetInterfaceNameFunction)(int interface_index, char* ifname);
// Retrieves a dump of the current AddressMap and set of online links as part
// of initialization. Expects |netlink_fd_| to exist already.
void DumpInitialAddressesAndWatch();
// Sets |*address_changed| to indicate whether |address_map_| changed and
// sets |*link_changed| to indicate if |online_links_| changed and sets
// |*tunnel_changed| to indicate if |online_links_| changed with regards to a
// tunnel interface while reading messages from |netlink_fd_|.
//
// If |address_map_| has changed and |address_map_diff_| is not nullopt,
// |*address_map_diff_| is populated with the changes to the AddressMap.
// Similarly, if |online_links_| has changed and |online_links_diff_| is not
// nullopt, |*online_links_diff| is populated with the changes to the set of
// online links.
void ReadMessages(bool* address_changed,
bool* link_changed,
bool* tunnel_changed);
// Sets |*address_changed| to true if |address_map_| changed, sets
// |*link_changed| to true if |online_links_| changed, sets |*tunnel_changed|
// to true if |online_links_| changed with regards to a tunnel interface while
// reading the message from |buffer|.
//
// If |address_map_| has changed and |address_map_diff_| is not nullopt,
// |*address_map_diff_| is populated with the changes to the AddressMap.
// Similarly, if |online_links_| has changed and |online_links_diff_| is not
// nullopt, |*online_links_diff| is populated with the changes to the set of
// online links.
void HandleMessage(const char* buffer,
int length,
bool* address_changed,
bool* link_changed,
bool* tunnel_changed);
// Call when some part of initialization failed; forces online and unblocks.
void AbortAndForceOnline();
// Called by |watcher_| when |netlink_fd_| can be read without blocking.
void OnFileCanReadWithoutBlocking();
// Does |interface_index| refer to a tunnel interface?
bool IsTunnelInterface(int interface_index) const;
// Is interface with index |interface_index| in list of ignored interfaces?
bool IsInterfaceIgnored(int interface_index) const;
// Updates current_connection_type_ based on the network list.
void UpdateCurrentConnectionType();
// Passes |address_map_diff_| and |online_links_diff_| to |diff_callback_| as
// arguments, and then clears them.
void RunDiffCallback();
// Used by AddressTrackerLinuxTest, returns the number of threads waiting
// for |connection_type_initialized_cv_|.
int GetThreadsWaitingForConnectionTypeInitForTesting();
// Used by AddressTrackerLinuxNetlinkTest, returns true iff `Init` succeeded.
// Undefined for non-tracking mode.
bool DidTrackingInitSucceedForTesting() const;
AddressMapDiff& address_map_diff_for_testing() {
AddressTrackerAutoLock lock(*this, address_map_lock_);
return address_map_diff_.value();
}
OnlineLinksDiff& online_links_diff_for_testing() {
AddressTrackerAutoLock lock(*this, online_links_lock_);
return online_links_diff_.value();
}
// Gets the name of an interface given the interface index |interface_index|.
// May return empty string if it fails but should not return NULL. This is
// overridden by tests.
GetInterfaceNameFunction get_interface_name_;
DiffCallback diff_callback_ GUARDED_BY_CONTEXT(sequence_checker_);
base::RepeatingClosure address_callback_
GUARDED_BY_CONTEXT(sequence_checker_);
base::RepeatingClosure link_callback_ GUARDED_BY_CONTEXT(sequence_checker_);
base::RepeatingClosure tunnel_callback_ GUARDED_BY_CONTEXT(sequence_checker_);
// Note that |watcher_| must be inactive when |netlink_fd_| is closed.
base::ScopedFD netlink_fd_ GUARDED_BY_CONTEXT(sequence_checker_);
std::unique_ptr<base::FileDescriptorWatcher::Controller> watcher_
GUARDED_BY_CONTEXT(sequence_checker_);
mutable base::Lock address_map_lock_;
AddressMap address_map_ GUARDED_BY(address_map_lock_);
absl::optional<AddressMapDiff> address_map_diff_;
// Set of interface indices for links that are currently online.
mutable base::Lock online_links_lock_ ACQUIRED_AFTER(address_map_lock_);
std::unordered_set<int> online_links_ GUARDED_BY(online_links_lock_);
absl::optional<OnlineLinksDiff> online_links_diff_;
// Set of interface names that should be ignored.
const std::unordered_set<std::string> ignored_interfaces_;
base::Lock connection_type_lock_;
bool connection_type_initialized_ GUARDED_BY(connection_type_lock_) = false;
base::ConditionVariable connection_type_initialized_cv_;
NetworkChangeNotifier::ConnectionType current_connection_type_ GUARDED_BY(
connection_type_lock_) = NetworkChangeNotifier::CONNECTION_NONE;
int threads_waiting_for_connection_type_initialization_
GUARDED_BY(connection_type_lock_) = 0;
const bool tracking_;
// This can be set by the tracking constructor.
scoped_refptr<base::SequencedTaskRunner> sequenced_task_runner_;
// This SequenceChecker is still useful so instance variables above can be
// marked GUARDED_BY_CONTEXT(sequence_checker_).
SEQUENCE_CHECKER(sequence_checker_);
base::WeakPtrFactory<AddressTrackerLinux> weak_ptr_factory_{this};
};
} // namespace net::internal
#endif // NET_BASE_ADDRESS_TRACKER_LINUX_H_