1 // Copyright 2012 The Chromium Authors 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #ifndef NET_BASE_ADDRESS_TRACKER_LINUX_H_ 6 #define NET_BASE_ADDRESS_TRACKER_LINUX_H_ 7 8 #include <sys/socket.h> // Needed to include netlink. 9 10 // Mask superfluous definition of |struct net|. This is fixed in Linux 2.6.38. 11 12 #define net net_kernel 13 #include <linux/rtnetlink.h> 14 #undef net 15 #include <stddef.h> 16 17 #include <map> 18 #include <memory> 19 #include <string> 20 #include <unordered_set> 21 22 #include "base/compiler_specific.h" 23 #include "base/files/file_descriptor_watcher_posix.h" 24 #include "base/files/scoped_file.h" 25 #include "base/functional/callback.h" 26 #include "base/gtest_prod_util.h" 27 #include "base/memory/raw_ref.h" 28 #include "base/sequence_checker.h" 29 #include "base/synchronization/condition_variable.h" 30 #include "base/synchronization/lock.h" 31 #include "base/task/sequenced_task_runner.h" 32 #include "base/thread_annotations.h" 33 #include "net/base/address_map_linux.h" 34 #include "net/base/ip_address.h" 35 #include "net/base/net_export.h" 36 #include "net/base/network_change_notifier.h" 37 38 namespace net::test { 39 class AddressTrackerLinuxTest; 40 } 41 42 namespace net::internal { 43 44 // Keeps track of network interface addresses using rtnetlink. Used by 45 // NetworkChangeNotifier to provide signals to registered IPAddressObservers. 46 // 47 // In tracking mode, this class should mostly be used on a single sequence, 48 // except GetAddressMap() and GetOnlineLinks() (AddressMapOwnerLinux overrides) 49 // which can be called on any thread. The main sequence should be able to block 50 // (e.g. use a base::SequencedTaskRunner with base::MayBlock()). 51 // 52 // In non-tracking mode this should be used on a single thread. 53 class NET_EXPORT_PRIVATE AddressTrackerLinux : public AddressMapOwnerLinux { 54 public: 55 // Non-tracking version constructor: it takes a snapshot of the 56 // current system configuration. Once Init() returns, the 57 // configuration is available through GetOnlineLinks() and 58 // GetAddressMap(). 59 AddressTrackerLinux(); 60 61 // Tracking version constructor: it will run |address_callback| when 62 // the AddressMap changes, |link_callback| when the list of online 63 // links changes, and |tunnel_callback| when the list of online 64 // tunnels changes. 65 // |ignored_interfaces| is the list of interfaces to ignore. Changes to an 66 // ignored interface will not cause any callback to be run. An ignored 67 // interface will not have entries in GetAddressMap() and GetOnlineLinks(). 68 // NOTE: Only ignore interfaces not used to connect to the internet. Adding 69 // interfaces used to connect to the internet can cause critical network 70 // changed signals to be lost allowing incorrect stale state to persist. 71 // 72 // |blocking_thread_runner| is the sequence on which this AddressTrackerLinux 73 // will run. The AddressTrackerLinux can block in tracking mode and so it 74 // should run on a sequence that can block, e.g. a base::SequencedTaskRunner 75 // with base::MayBlock(). If nullptr, SetDiffCallback() cannot be used off of 76 // the AddressTrackerLinux's sequence. 77 AddressTrackerLinux(const base::RepeatingClosure& address_callback, 78 const base::RepeatingClosure& link_callback, 79 const base::RepeatingClosure& tunnel_callback, 80 const std::unordered_set<std::string>& ignored_interfaces, 81 scoped_refptr<base::SequencedTaskRunner> 82 blocking_thread_runner = nullptr); 83 ~AddressTrackerLinux() override; 84 85 // In tracking mode, it starts watching the system configuration for 86 // changes. The current thread must have a MessageLoopForIO. In 87 // non-tracking mode, once Init() returns, a snapshot of the system 88 // configuration is available through GetOnlineLinks() and 89 // GetAddressMap(). 90 void Init(); 91 92 // Same as Init(), except instead of creating and binding a netlink socket, 93 // this AddressTrackerLinux will send and receive messages from |fd|. 94 void InitWithFdForTesting(base::ScopedFD fd); 95 96 // AddressMapOwnerLinux implementation (callable on any thread): 97 AddressMap GetAddressMap() const override; 98 // Returns set of interface indices for online interfaces. 99 std::unordered_set<int> GetOnlineLinks() const override; 100 101 AddressTrackerLinux* GetAddressTrackerLinux() override; 102 103 // This returns the current AddressMap and set of online links, and atomically 104 // starts recording diffs to those structures. This can be called on any 105 // thread, and must be called called before SetDiffCallback() below. Available 106 // only in tracking mode. 107 std::pair<AddressMap, std::unordered_set<int>> 108 GetInitialDataAndStartRecordingDiffs(); 109 110 // Called after GetInitialDataAndStartRecordingDiffs(). 111 // 112 // Whenever the AddressMap or the set of online links (returned by the above 113 // two methods) changes, this callback is called on AddressTrackerLinux's 114 // sequence. On the first call, |diff_callback| is called synchronously with 115 // the set of diffs that have been built since 116 // GetInitialDataAndStartRecordingDiffs() was called. If there are none, 117 // |diff_callback| won't be called. 118 // 119 // This is only available in tracking mode. It can be called on any thread, 120 // but it will post a task to the AddressTrackerLinux's sequence and therefore 121 // will finish asynchronously. The caller MUST ENSURE that the 122 // AddressTrackerLinux is not deleted until this task finishes. 123 // This also requires |sequenced_task_runner_| to be set by the 124 // AddressTrackerLinux constructor above. 125 // 126 // Note that other threads may see updated AddressMaps by calling 127 // GetAddressMap() before |diff_callback| is ever called. 128 void SetDiffCallback(DiffCallback diff_callback); 129 130 // Implementation of NetworkChangeNotifierLinux::GetCurrentConnectionType(). 131 // Safe to call from any thread, but will block until Init() has completed. 132 NetworkChangeNotifier::ConnectionType GetCurrentConnectionType(); 133 134 // Returns the name for the interface with interface index |interface_index|. 135 // |buf| should be a pointer to an array of size IFNAMSIZ. The returned 136 // pointer will point to |buf|. This function acts like if_indextoname which 137 // cannot be used as net/if.h cannot be mixed with linux/if.h. We'll stick 138 // with exclusively talking to the kernel and not the C library. 139 static char* GetInterfaceName(int interface_index, char* buf); 140 141 // Does |name| refer to a tunnel interface? 142 static bool IsTunnelInterfaceName(const char* name); 143 144 private: 145 friend class net::test::AddressTrackerLinuxTest; 146 FRIEND_TEST_ALL_PREFIXES(AddressTrackerLinuxNetlinkTest, 147 TestInitializeTwoTrackers); 148 FRIEND_TEST_ALL_PREFIXES(AddressTrackerLinuxNetlinkTest, 149 TestInitializeTwoTrackersInPidNamespaces); 150 friend int ChildProcessInitializeTrackerForTesting(); 151 152 // In tracking mode, holds |lock| while alive. In non-tracking mode, 153 // enforces single-threaded access. 154 class SCOPED_LOCKABLE AddressTrackerAutoLock { 155 public: 156 AddressTrackerAutoLock(const AddressTrackerLinux& tracker, base::Lock& lock) 157 EXCLUSIVE_LOCK_FUNCTION(lock); 158 AddressTrackerAutoLock(const AddressTrackerAutoLock&) = delete; 159 AddressTrackerAutoLock& operator=(const AddressTrackerAutoLock&) = delete; 160 ~AddressTrackerAutoLock() UNLOCK_FUNCTION(); 161 162 private: 163 const raw_ref<const AddressTrackerLinux> tracker_; 164 const raw_ref<base::Lock> lock_; 165 }; 166 167 // A function that returns the name of an interface given the interface index 168 // in |interface_index|. |ifname| should be a buffer of size IFNAMSIZ. The 169 // function should return a pointer to |ifname|. 170 typedef char* (*GetInterfaceNameFunction)(int interface_index, char* ifname); 171 172 // Retrieves a dump of the current AddressMap and set of online links as part 173 // of initialization. Expects |netlink_fd_| to exist already. 174 void DumpInitialAddressesAndWatch(); 175 176 // Sets |*address_changed| to indicate whether |address_map_| changed and 177 // sets |*link_changed| to indicate if |online_links_| changed and sets 178 // |*tunnel_changed| to indicate if |online_links_| changed with regards to a 179 // tunnel interface while reading messages from |netlink_fd_|. 180 // 181 // If |address_map_| has changed and |address_map_diff_| is not nullopt, 182 // |*address_map_diff_| is populated with the changes to the AddressMap. 183 // Similarly, if |online_links_| has changed and |online_links_diff_| is not 184 // nullopt, |*online_links_diff| is populated with the changes to the set of 185 // online links. 186 void ReadMessages(bool* address_changed, 187 bool* link_changed, 188 bool* tunnel_changed); 189 190 // Sets |*address_changed| to true if |address_map_| changed, sets 191 // |*link_changed| to true if |online_links_| changed, sets |*tunnel_changed| 192 // to true if |online_links_| changed with regards to a tunnel interface while 193 // reading the message from |buffer|. 194 // 195 // If |address_map_| has changed and |address_map_diff_| is not nullopt, 196 // |*address_map_diff_| is populated with the changes to the AddressMap. 197 // Similarly, if |online_links_| has changed and |online_links_diff_| is not 198 // nullopt, |*online_links_diff| is populated with the changes to the set of 199 // online links. 200 void HandleMessage(const char* buffer, 201 int length, 202 bool* address_changed, 203 bool* link_changed, 204 bool* tunnel_changed); 205 206 // Call when some part of initialization failed; forces online and unblocks. 207 void AbortAndForceOnline(); 208 209 // Called by |watcher_| when |netlink_fd_| can be read without blocking. 210 void OnFileCanReadWithoutBlocking(); 211 212 // Does |interface_index| refer to a tunnel interface? 213 bool IsTunnelInterface(int interface_index) const; 214 215 // Is interface with index |interface_index| in list of ignored interfaces? 216 bool IsInterfaceIgnored(int interface_index) const; 217 218 // Updates current_connection_type_ based on the network list. 219 void UpdateCurrentConnectionType(); 220 221 // Passes |address_map_diff_| and |online_links_diff_| to |diff_callback_| as 222 // arguments, and then clears them. 223 void RunDiffCallback(); 224 225 // Used by AddressTrackerLinuxTest, returns the number of threads waiting 226 // for |connection_type_initialized_cv_|. 227 int GetThreadsWaitingForConnectionTypeInitForTesting(); 228 229 // Used by AddressTrackerLinuxNetlinkTest, returns true iff `Init` succeeded. 230 // Undefined for non-tracking mode. 231 bool DidTrackingInitSucceedForTesting() const; 232 address_map_diff_for_testing()233 AddressMapDiff& address_map_diff_for_testing() { 234 AddressTrackerAutoLock lock(*this, address_map_lock_); 235 return address_map_diff_.value(); 236 } online_links_diff_for_testing()237 OnlineLinksDiff& online_links_diff_for_testing() { 238 AddressTrackerAutoLock lock(*this, online_links_lock_); 239 return online_links_diff_.value(); 240 } 241 242 // Gets the name of an interface given the interface index |interface_index|. 243 // May return empty string if it fails but should not return NULL. This is 244 // overridden by tests. 245 GetInterfaceNameFunction get_interface_name_; 246 247 DiffCallback diff_callback_ GUARDED_BY_CONTEXT(sequence_checker_); 248 base::RepeatingClosure address_callback_ 249 GUARDED_BY_CONTEXT(sequence_checker_); 250 base::RepeatingClosure link_callback_ GUARDED_BY_CONTEXT(sequence_checker_); 251 base::RepeatingClosure tunnel_callback_ GUARDED_BY_CONTEXT(sequence_checker_); 252 253 // Note that |watcher_| must be inactive when |netlink_fd_| is closed. 254 base::ScopedFD netlink_fd_ GUARDED_BY_CONTEXT(sequence_checker_); 255 std::unique_ptr<base::FileDescriptorWatcher::Controller> watcher_ 256 GUARDED_BY_CONTEXT(sequence_checker_); 257 258 mutable base::Lock address_map_lock_; 259 AddressMap address_map_ GUARDED_BY(address_map_lock_); 260 std::optional<AddressMapDiff> address_map_diff_; 261 262 // Set of interface indices for links that are currently online. 263 mutable base::Lock online_links_lock_ ACQUIRED_AFTER(address_map_lock_); 264 std::unordered_set<int> online_links_ GUARDED_BY(online_links_lock_); 265 std::optional<OnlineLinksDiff> online_links_diff_; 266 267 // Set of interface names that should be ignored. 268 const std::unordered_set<std::string> ignored_interfaces_; 269 270 base::Lock connection_type_lock_; 271 bool connection_type_initialized_ GUARDED_BY(connection_type_lock_) = false; 272 base::ConditionVariable connection_type_initialized_cv_; 273 NetworkChangeNotifier::ConnectionType current_connection_type_ GUARDED_BY( 274 connection_type_lock_) = NetworkChangeNotifier::CONNECTION_NONE; 275 int threads_waiting_for_connection_type_initialization_ 276 GUARDED_BY(connection_type_lock_) = 0; 277 278 const bool tracking_; 279 280 // This can be set by the tracking constructor. 281 scoped_refptr<base::SequencedTaskRunner> sequenced_task_runner_; 282 // This SequenceChecker is still useful so instance variables above can be 283 // marked GUARDED_BY_CONTEXT(sequence_checker_). 284 SEQUENCE_CHECKER(sequence_checker_); 285 286 base::WeakPtrFactory<AddressTrackerLinux> weak_ptr_factory_{this}; 287 }; 288 289 } // namespace net::internal 290 291 #endif // NET_BASE_ADDRESS_TRACKER_LINUX_H_ 292