1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291
|
// 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 <string_view>
#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`.
// 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 std::string GetInterfaceName(int interface_index);
// Does |name| refer to a tunnel interface?
static bool IsTunnelInterfaceName(std::string_view 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|.
using GetInterfaceNameFunction = std::string (*)(int interface_index);
// 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_);
std::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_);
std::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_
|