xref: /aosp_15_r20/external/cronet/net/base/address_tracker_linux.h (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
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