File: address_tracker_linux.h

package info (click to toggle)
chromium 139.0.7258.127-1
  • links: PTS, VCS
  • area: main
  • in suites:
  • size: 6,122,068 kB
  • sloc: cpp: 35,100,771; ansic: 7,163,530; javascript: 4,103,002; python: 1,436,920; asm: 946,517; xml: 746,709; pascal: 187,653; perl: 88,691; sh: 88,436; objc: 79,953; sql: 51,488; cs: 44,583; fortran: 24,137; makefile: 22,147; tcl: 15,277; php: 13,980; yacc: 8,984; ruby: 7,485; awk: 3,720; lisp: 3,096; lex: 1,327; ada: 727; jsp: 228; sed: 36
file content (291 lines) | stat: -rw-r--r-- 13,009 bytes parent folder | download | duplicates (5)
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_