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 #include "net/base/address_tracker_linux.h"
6
7 #include <errno.h>
8 #include <linux/if.h>
9 #include <stdint.h>
10 #include <sys/ioctl.h>
11
12 #include <optional>
13 #include <utility>
14 #include <vector>
15
16 #include "base/check.h"
17 #include "base/compiler_specific.h"
18 #include "base/containers/span.h"
19 #include "base/dcheck_is_on.h"
20 #include "base/files/scoped_file.h"
21 #include "base/functional/bind.h"
22 #include "base/functional/callback_helpers.h"
23 #include "base/logging.h"
24 #include "base/memory/page_size.h"
25 #include "base/posix/eintr_wrapper.h"
26 #include "base/sequence_checker.h"
27 #include "base/task/current_thread.h"
28 #include "base/threading/scoped_blocking_call.h"
29 #include "base/threading/thread_restrictions.h"
30 #include "build/build_config.h"
31 #include "net/base/network_interfaces_linux.h"
32
33 #if BUILDFLAG(IS_ANDROID)
34 #include "base/android/build_info.h"
35 #endif
36
37 namespace net::internal {
38
39 namespace {
40
41 // Some kernel functions such as wireless_send_event and rtnetlink_ifinfo_prep
42 // may send spurious messages over rtnetlink. RTM_NEWLINK messages where
43 // ifi_change == 0 and rta_type == IFLA_WIRELESS should be ignored.
IgnoreWirelessChange(const struct ifinfomsg * msg,int length)44 bool IgnoreWirelessChange(const struct ifinfomsg* msg, int length) {
45 for (const struct rtattr* attr = IFLA_RTA(msg); RTA_OK(attr, length);
46 attr = RTA_NEXT(attr, length)) {
47 if (attr->rta_type == IFLA_WIRELESS && msg->ifi_change == 0)
48 return true;
49 }
50 return false;
51 }
52
53 // Retrieves address from NETLINK address message.
54 // Sets |really_deprecated| for IPv6 addresses with preferred lifetimes of 0.
55 // Precondition: |header| must already be validated with NLMSG_OK.
GetAddress(const struct nlmsghdr * header,int header_length,IPAddress * out,bool * really_deprecated)56 bool GetAddress(const struct nlmsghdr* header,
57 int header_length,
58 IPAddress* out,
59 bool* really_deprecated) {
60 if (really_deprecated)
61 *really_deprecated = false;
62
63 // Extract the message and update |header_length| to be the number of
64 // remaining bytes.
65 const struct ifaddrmsg* msg =
66 reinterpret_cast<const struct ifaddrmsg*>(NLMSG_DATA(header));
67 header_length -= NLMSG_HDRLEN;
68
69 size_t address_length = 0;
70 switch (msg->ifa_family) {
71 case AF_INET:
72 address_length = IPAddress::kIPv4AddressSize;
73 break;
74 case AF_INET6:
75 address_length = IPAddress::kIPv6AddressSize;
76 break;
77 default:
78 // Unknown family.
79 return false;
80 }
81 // Use IFA_ADDRESS unless IFA_LOCAL is present. This behavior here is based on
82 // getaddrinfo in glibc (check_pf.c). Judging from kernel implementation of
83 // NETLINK, IPv4 addresses have only the IFA_ADDRESS attribute, while IPv6
84 // have the IFA_LOCAL attribute.
85 uint8_t* address = nullptr;
86 uint8_t* local = nullptr;
87 int length = IFA_PAYLOAD(header);
88 if (length > header_length) {
89 LOG(ERROR) << "ifaddrmsg length exceeds bounds";
90 return false;
91 }
92 for (const struct rtattr* attr =
93 reinterpret_cast<const struct rtattr*>(IFA_RTA(msg));
94 RTA_OK(attr, length); attr = RTA_NEXT(attr, length)) {
95 switch (attr->rta_type) {
96 case IFA_ADDRESS:
97 if (RTA_PAYLOAD(attr) < address_length) {
98 LOG(ERROR) << "attr does not have enough bytes to read an address";
99 return false;
100 }
101 address = reinterpret_cast<uint8_t*>(RTA_DATA(attr));
102 break;
103 case IFA_LOCAL:
104 if (RTA_PAYLOAD(attr) < address_length) {
105 LOG(ERROR) << "attr does not have enough bytes to read an address";
106 return false;
107 }
108 local = reinterpret_cast<uint8_t*>(RTA_DATA(attr));
109 break;
110 case IFA_CACHEINFO: {
111 if (RTA_PAYLOAD(attr) < sizeof(struct ifa_cacheinfo)) {
112 LOG(ERROR)
113 << "attr does not have enough bytes to read an ifa_cacheinfo";
114 return false;
115 }
116 const struct ifa_cacheinfo* cache_info =
117 reinterpret_cast<const struct ifa_cacheinfo*>(RTA_DATA(attr));
118 if (really_deprecated)
119 *really_deprecated = (cache_info->ifa_prefered == 0);
120 } break;
121 default:
122 break;
123 }
124 }
125 if (local)
126 address = local;
127 if (!address)
128 return false;
129 // SAFETY: `address` is only set above after `RTA_PAYLOAD` is checked against
130 // `address_length`.
131 *out = IPAddress(UNSAFE_BUFFERS(base::span(address, address_length)));
132 return true;
133 }
134
135 // SafelyCastNetlinkMsgData<T> performs a bounds check before casting |header|'s
136 // data to a |T*|. When the bounds check fails, returns nullptr.
137 template <typename T>
SafelyCastNetlinkMsgData(const struct nlmsghdr * header,int length)138 T* SafelyCastNetlinkMsgData(const struct nlmsghdr* header, int length) {
139 DCHECK(NLMSG_OK(header, static_cast<__u32>(length)));
140 if (length <= 0 || static_cast<size_t>(length) < NLMSG_HDRLEN + sizeof(T))
141 return nullptr;
142 return reinterpret_cast<const T*>(NLMSG_DATA(header));
143 }
144
145 } // namespace
146
147 // static
GetInterfaceName(int interface_index,char * buf)148 char* AddressTrackerLinux::GetInterfaceName(int interface_index, char* buf) {
149 memset(buf, 0, IFNAMSIZ);
150 base::ScopedFD ioctl_socket = GetSocketForIoctl();
151 if (!ioctl_socket.is_valid())
152 return buf;
153
154 struct ifreq ifr = {};
155 ifr.ifr_ifindex = interface_index;
156
157 if (ioctl(ioctl_socket.get(), SIOCGIFNAME, &ifr) == 0)
158 strncpy(buf, ifr.ifr_name, IFNAMSIZ - 1);
159 return buf;
160 }
161
AddressTrackerLinux()162 AddressTrackerLinux::AddressTrackerLinux()
163 : get_interface_name_(GetInterfaceName),
164 address_callback_(base::DoNothing()),
165 link_callback_(base::DoNothing()),
166 tunnel_callback_(base::DoNothing()),
167 ignored_interfaces_(),
168 connection_type_initialized_cv_(&connection_type_lock_),
169 tracking_(false) {}
170
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)171 AddressTrackerLinux::AddressTrackerLinux(
172 const base::RepeatingClosure& address_callback,
173 const base::RepeatingClosure& link_callback,
174 const base::RepeatingClosure& tunnel_callback,
175 const std::unordered_set<std::string>& ignored_interfaces,
176 scoped_refptr<base::SequencedTaskRunner> blocking_thread_runner)
177 : get_interface_name_(GetInterfaceName),
178 address_callback_(address_callback),
179 link_callback_(link_callback),
180 tunnel_callback_(tunnel_callback),
181 ignored_interfaces_(ignored_interfaces),
182 connection_type_initialized_cv_(&connection_type_lock_),
183 tracking_(true),
184 sequenced_task_runner_(std::move(blocking_thread_runner)) {
185 DCHECK(!address_callback.is_null());
186 DCHECK(!link_callback.is_null());
187 DETACH_FROM_SEQUENCE(sequence_checker_);
188 }
189
190 AddressTrackerLinux::~AddressTrackerLinux() = default;
191
InitWithFdForTesting(base::ScopedFD fd)192 void AddressTrackerLinux::InitWithFdForTesting(base::ScopedFD fd) {
193 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
194
195 netlink_fd_ = std::move(fd);
196 DumpInitialAddressesAndWatch();
197 }
198
Init()199 void AddressTrackerLinux::Init() {
200 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
201 #if BUILDFLAG(IS_ANDROID)
202 // RTM_GETLINK stopped working in Android 11 (see
203 // https://developer.android.com/preview/privacy/mac-address),
204 // so AddressTrackerLinux should not be used in later versions
205 // of Android. Chromium code doesn't need it past Android P.
206 DCHECK_LT(base::android::BuildInfo::GetInstance()->sdk_int(),
207 base::android::SDK_VERSION_P);
208 #endif
209 netlink_fd_.reset(socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE));
210 if (!netlink_fd_.is_valid()) {
211 PLOG(ERROR) << "Could not create NETLINK socket";
212 AbortAndForceOnline();
213 return;
214 }
215
216 int rv;
217
218 if (tracking_) {
219 // Request notifications.
220 struct sockaddr_nl addr = {};
221 addr.nl_family = AF_NETLINK;
222 addr.nl_pid = 0; // Let the kernel select a unique value.
223 // TODO(szym): Track RTMGRP_LINK as well for ifi_type,
224 // http://crbug.com/113993
225 addr.nl_groups =
226 RTMGRP_IPV4_IFADDR | RTMGRP_IPV6_IFADDR | RTMGRP_NOTIFY | RTMGRP_LINK;
227 rv = bind(netlink_fd_.get(), reinterpret_cast<struct sockaddr*>(&addr),
228 sizeof(addr));
229 if (rv < 0) {
230 PLOG(ERROR) << "Could not bind NETLINK socket";
231 AbortAndForceOnline();
232 return;
233 }
234 }
235
236 DumpInitialAddressesAndWatch();
237 }
238
DidTrackingInitSucceedForTesting() const239 bool AddressTrackerLinux::DidTrackingInitSucceedForTesting() const {
240 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
241 CHECK(tracking_);
242 return watcher_ != nullptr;
243 }
244
AbortAndForceOnline()245 void AddressTrackerLinux::AbortAndForceOnline() {
246 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
247 watcher_.reset();
248 netlink_fd_.reset();
249 AddressTrackerAutoLock lock(*this, connection_type_lock_);
250 current_connection_type_ = NetworkChangeNotifier::CONNECTION_UNKNOWN;
251 connection_type_initialized_ = true;
252 connection_type_initialized_cv_.Broadcast();
253 }
254
GetAddressMap() const255 AddressTrackerLinux::AddressMap AddressTrackerLinux::GetAddressMap() const {
256 AddressTrackerAutoLock lock(*this, address_map_lock_);
257 return address_map_;
258 }
259
GetOnlineLinks() const260 std::unordered_set<int> AddressTrackerLinux::GetOnlineLinks() const {
261 AddressTrackerAutoLock lock(*this, online_links_lock_);
262 return online_links_;
263 }
264
GetAddressTrackerLinux()265 AddressTrackerLinux* AddressTrackerLinux::GetAddressTrackerLinux() {
266 return this;
267 }
268
269 std::pair<AddressTrackerLinux::AddressMap, std::unordered_set<int>>
GetInitialDataAndStartRecordingDiffs()270 AddressTrackerLinux::GetInitialDataAndStartRecordingDiffs() {
271 DCHECK(tracking_);
272 AddressTrackerAutoLock lock_address_map(*this, address_map_lock_);
273 AddressTrackerAutoLock lock_online_links(*this, online_links_lock_);
274 address_map_diff_ = AddressMapDiff();
275 online_links_diff_ = OnlineLinksDiff();
276 return {address_map_, online_links_};
277 }
278
SetDiffCallback(DiffCallback diff_callback)279 void AddressTrackerLinux::SetDiffCallback(DiffCallback diff_callback) {
280 DCHECK(tracking_);
281 DCHECK(sequenced_task_runner_);
282
283 if (!sequenced_task_runner_->RunsTasksInCurrentSequence()) {
284 sequenced_task_runner_->PostTask(
285 FROM_HERE, base::BindOnce(&AddressTrackerLinux::SetDiffCallback,
286 weak_ptr_factory_.GetWeakPtr(),
287 std::move(diff_callback)));
288 return;
289 }
290
291 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
292 #if DCHECK_IS_ON()
293 {
294 // GetInitialDataAndStartRecordingDiffs() must be called before
295 // SetDiffCallback().
296 AddressTrackerAutoLock lock_address_map(*this, address_map_lock_);
297 AddressTrackerAutoLock lock_online_links(*this, online_links_lock_);
298 DCHECK(address_map_diff_.has_value());
299 DCHECK(online_links_diff_.has_value());
300 }
301 #endif // DCHECK_IS_ON()
302 diff_callback_ = std::move(diff_callback);
303 RunDiffCallback();
304 }
305
IsInterfaceIgnored(int interface_index) const306 bool AddressTrackerLinux::IsInterfaceIgnored(int interface_index) const {
307 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
308 if (ignored_interfaces_.empty())
309 return false;
310
311 char buf[IFNAMSIZ] = {0};
312 const char* interface_name = get_interface_name_(interface_index, buf);
313 return ignored_interfaces_.find(interface_name) != ignored_interfaces_.end();
314 }
315
316 NetworkChangeNotifier::ConnectionType
GetCurrentConnectionType()317 AddressTrackerLinux::GetCurrentConnectionType() {
318 // http://crbug.com/125097
319 base::ScopedAllowBaseSyncPrimitivesOutsideBlockingScope allow_wait;
320 AddressTrackerAutoLock lock(*this, connection_type_lock_);
321 // Make sure the initial connection type is set before returning.
322 threads_waiting_for_connection_type_initialization_++;
323 while (!connection_type_initialized_) {
324 connection_type_initialized_cv_.Wait();
325 }
326 threads_waiting_for_connection_type_initialization_--;
327 return current_connection_type_;
328 }
329
DumpInitialAddressesAndWatch()330 void AddressTrackerLinux::DumpInitialAddressesAndWatch() {
331 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
332
333 // Request dump of addresses.
334 struct sockaddr_nl peer = {};
335 peer.nl_family = AF_NETLINK;
336
337 struct {
338 struct nlmsghdr header;
339 struct rtgenmsg msg;
340 } request = {};
341
342 request.header.nlmsg_len = NLMSG_LENGTH(sizeof(request.msg));
343 request.header.nlmsg_type = RTM_GETADDR;
344 request.header.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
345 request.header.nlmsg_pid = 0; // This field is opaque to netlink.
346 request.msg.rtgen_family = AF_UNSPEC;
347
348 int rv = HANDLE_EINTR(
349 sendto(netlink_fd_.get(), &request, request.header.nlmsg_len, 0,
350 reinterpret_cast<struct sockaddr*>(&peer), sizeof(peer)));
351 if (rv < 0) {
352 PLOG(ERROR) << "Could not send NETLINK request";
353 AbortAndForceOnline();
354 return;
355 }
356
357 // Consume pending message to populate the AddressMap, but don't notify.
358 // Sending another request without first reading responses results in EBUSY.
359 bool address_changed;
360 bool link_changed;
361 bool tunnel_changed;
362 ReadMessages(&address_changed, &link_changed, &tunnel_changed);
363
364 // Request dump of link state
365 request.header.nlmsg_type = RTM_GETLINK;
366
367 rv = HANDLE_EINTR(
368 sendto(netlink_fd_.get(), &request, request.header.nlmsg_len, 0,
369 reinterpret_cast<struct sockaddr*>(&peer), sizeof(peer)));
370 if (rv < 0) {
371 PLOG(ERROR) << "Could not send NETLINK request";
372 AbortAndForceOnline();
373 return;
374 }
375
376 // Consume pending message to populate links_online_, but don't notify.
377 ReadMessages(&address_changed, &link_changed, &tunnel_changed);
378 {
379 AddressTrackerAutoLock lock(*this, connection_type_lock_);
380 connection_type_initialized_ = true;
381 connection_type_initialized_cv_.Broadcast();
382 }
383
384 if (tracking_) {
385 DCHECK(!sequenced_task_runner_ ||
386 sequenced_task_runner_->RunsTasksInCurrentSequence());
387
388 watcher_ = base::FileDescriptorWatcher::WatchReadable(
389 netlink_fd_.get(),
390 base::BindRepeating(&AddressTrackerLinux::OnFileCanReadWithoutBlocking,
391 base::Unretained(this)));
392 }
393 }
394
ReadMessages(bool * address_changed,bool * link_changed,bool * tunnel_changed)395 void AddressTrackerLinux::ReadMessages(bool* address_changed,
396 bool* link_changed,
397 bool* tunnel_changed) {
398 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
399 *address_changed = false;
400 *link_changed = false;
401 *tunnel_changed = false;
402 bool first_loop = true;
403
404 // Varying sources have different opinions regarding the buffer size needed
405 // for netlink messages to avoid truncation:
406 // - The official documentation on netlink says messages are generally 8kb
407 // or the system page size, whichever is *larger*:
408 // https://www.kernel.org/doc/html/v6.2/userspace-api/netlink/intro.html#buffer-sizing
409 // - The kernel headers would imply that messages are generally the system
410 // page size or 8kb, whichever is *smaller*:
411 // https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/tree/include/linux/netlink.h?h=v6.2.2#n226
412 // (libmnl follows this.)
413 // - The netlink(7) man page's example always uses a fixed size 8kb buffer:
414 // https://man7.org/linux/man-pages/man7/netlink.7.html
415 // Here, we follow the guidelines in the documentation, for two primary
416 // reasons:
417 // - Erring on the side of a larger size is the safer way to go to avoid
418 // MSG_TRUNC.
419 // - Since this is heap-allocated anyway, there's no risk to the stack by
420 // using the larger size.
421
422 constexpr size_t kMinNetlinkBufferSize = 8 * 1024;
423 std::vector<char> buffer(
424 std::max(base::GetPageSize(), kMinNetlinkBufferSize));
425
426 {
427 std::optional<base::ScopedBlockingCall> blocking_call;
428 if (tracking_) {
429 // If the loop below takes a long time to run, a new thread should added
430 // to the current thread pool to ensure forward progress of all tasks.
431 blocking_call.emplace(FROM_HERE, base::BlockingType::MAY_BLOCK);
432 }
433
434 for (;;) {
435 int rv =
436 HANDLE_EINTR(recv(netlink_fd_.get(), buffer.data(), buffer.size(),
437 // Block the first time through loop.
438 first_loop ? 0 : MSG_DONTWAIT));
439 first_loop = false;
440 if (rv == 0) {
441 LOG(ERROR) << "Unexpected shutdown of NETLINK socket.";
442 return;
443 }
444 if (rv < 0) {
445 if ((errno == EAGAIN) || (errno == EWOULDBLOCK))
446 break;
447 PLOG(ERROR) << "Failed to recv from netlink socket";
448 return;
449 }
450 HandleMessage(buffer.data(), rv, address_changed, link_changed,
451 tunnel_changed);
452 }
453 }
454 if (*link_changed || *address_changed)
455 UpdateCurrentConnectionType();
456 }
457
HandleMessage(const char * buffer,int length,bool * address_changed,bool * link_changed,bool * tunnel_changed)458 void AddressTrackerLinux::HandleMessage(const char* buffer,
459 int length,
460 bool* address_changed,
461 bool* link_changed,
462 bool* tunnel_changed) {
463 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
464 DCHECK(buffer);
465 // Note that NLMSG_NEXT decrements |length| to reflect the number of bytes
466 // remaining in |buffer|.
467 for (const struct nlmsghdr* header =
468 reinterpret_cast<const struct nlmsghdr*>(buffer);
469 length >= 0 && NLMSG_OK(header, static_cast<__u32>(length));
470 header = NLMSG_NEXT(header, length)) {
471 // The |header| pointer should never precede |buffer|.
472 DCHECK_LE(buffer, reinterpret_cast<const char*>(header));
473 switch (header->nlmsg_type) {
474 case NLMSG_DONE:
475 return;
476 case NLMSG_ERROR: {
477 const struct nlmsgerr* msg =
478 SafelyCastNetlinkMsgData<const struct nlmsgerr>(header, length);
479 if (msg == nullptr)
480 return;
481 LOG(ERROR) << "Unexpected netlink error " << msg->error << ".";
482 } return;
483 case RTM_NEWADDR: {
484 IPAddress address;
485 bool really_deprecated;
486 const struct ifaddrmsg* msg =
487 SafelyCastNetlinkMsgData<const struct ifaddrmsg>(header, length);
488 if (msg == nullptr)
489 return;
490 if (IsInterfaceIgnored(msg->ifa_index))
491 break;
492 if (GetAddress(header, length, &address, &really_deprecated)) {
493 struct ifaddrmsg msg_copy = *msg;
494 AddressTrackerAutoLock lock(*this, address_map_lock_);
495 // Routers may frequently (every few seconds) output the IPv6 ULA
496 // prefix which can cause the linux kernel to frequently output two
497 // back-to-back messages, one without the deprecated flag and one with
498 // the deprecated flag but both with preferred lifetimes of 0. Avoid
499 // interpreting this as an actual change by canonicalizing the two
500 // messages by setting the deprecated flag based on the preferred
501 // lifetime also. http://crbug.com/268042
502 if (really_deprecated)
503 msg_copy.ifa_flags |= IFA_F_DEPRECATED;
504 // Only indicate change if the address is new or ifaddrmsg info has
505 // changed.
506 auto it = address_map_.find(address);
507 if (it == address_map_.end()) {
508 address_map_.insert(it, std::pair(address, msg_copy));
509 *address_changed = true;
510 } else if (memcmp(&it->second, &msg_copy, sizeof(msg_copy))) {
511 it->second = msg_copy;
512 *address_changed = true;
513 }
514 if (*address_changed && address_map_diff_.has_value()) {
515 (*address_map_diff_)[address] = msg_copy;
516 }
517 }
518 } break;
519 case RTM_DELADDR: {
520 IPAddress address;
521 const struct ifaddrmsg* msg =
522 SafelyCastNetlinkMsgData<const struct ifaddrmsg>(header, length);
523 if (msg == nullptr)
524 return;
525 if (IsInterfaceIgnored(msg->ifa_index))
526 break;
527 if (GetAddress(header, length, &address, nullptr)) {
528 AddressTrackerAutoLock lock(*this, address_map_lock_);
529 if (address_map_.erase(address)) {
530 *address_changed = true;
531 if (address_map_diff_.has_value()) {
532 (*address_map_diff_)[address] = std::nullopt;
533 }
534 }
535 }
536 } break;
537 case RTM_NEWLINK: {
538 const struct ifinfomsg* msg =
539 SafelyCastNetlinkMsgData<const struct ifinfomsg>(header, length);
540 if (msg == nullptr)
541 return;
542 if (IsInterfaceIgnored(msg->ifi_index))
543 break;
544 if (IgnoreWirelessChange(msg, IFLA_PAYLOAD(header))) {
545 VLOG(2) << "Ignoring RTM_NEWLINK message";
546 break;
547 }
548 if (!(msg->ifi_flags & IFF_LOOPBACK) && (msg->ifi_flags & IFF_UP) &&
549 (msg->ifi_flags & IFF_LOWER_UP) && (msg->ifi_flags & IFF_RUNNING)) {
550 AddressTrackerAutoLock lock(*this, online_links_lock_);
551 if (online_links_.insert(msg->ifi_index).second) {
552 *link_changed = true;
553 if (online_links_diff_.has_value()) {
554 (*online_links_diff_)[msg->ifi_index] = true;
555 }
556 if (IsTunnelInterface(msg->ifi_index))
557 *tunnel_changed = true;
558 }
559 } else {
560 AddressTrackerAutoLock lock(*this, online_links_lock_);
561 if (online_links_.erase(msg->ifi_index)) {
562 *link_changed = true;
563 if (online_links_diff_.has_value()) {
564 (*online_links_diff_)[msg->ifi_index] = false;
565 }
566 if (IsTunnelInterface(msg->ifi_index))
567 *tunnel_changed = true;
568 }
569 }
570 } break;
571 case RTM_DELLINK: {
572 const struct ifinfomsg* msg =
573 SafelyCastNetlinkMsgData<const struct ifinfomsg>(header, length);
574 if (msg == nullptr)
575 return;
576 if (IsInterfaceIgnored(msg->ifi_index))
577 break;
578 AddressTrackerAutoLock lock(*this, online_links_lock_);
579 if (online_links_.erase(msg->ifi_index)) {
580 *link_changed = true;
581 if (online_links_diff_.has_value()) {
582 (*online_links_diff_)[msg->ifi_index] = false;
583 }
584 if (IsTunnelInterface(msg->ifi_index))
585 *tunnel_changed = true;
586 }
587 } break;
588 default:
589 break;
590 }
591 }
592 }
593
OnFileCanReadWithoutBlocking()594 void AddressTrackerLinux::OnFileCanReadWithoutBlocking() {
595 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
596 bool address_changed;
597 bool link_changed;
598 bool tunnel_changed;
599 ReadMessages(&address_changed, &link_changed, &tunnel_changed);
600 if (diff_callback_) {
601 RunDiffCallback();
602 }
603 if (address_changed) {
604 address_callback_.Run();
605 }
606 if (link_changed) {
607 link_callback_.Run();
608 }
609 if (tunnel_changed) {
610 tunnel_callback_.Run();
611 }
612 }
613
IsTunnelInterface(int interface_index) const614 bool AddressTrackerLinux::IsTunnelInterface(int interface_index) const {
615 char buf[IFNAMSIZ] = {0};
616 return IsTunnelInterfaceName(get_interface_name_(interface_index, buf));
617 }
618
619 // static
IsTunnelInterfaceName(const char * name)620 bool AddressTrackerLinux::IsTunnelInterfaceName(const char* name) {
621 // Linux kernel drivers/net/tun.c uses "tun" name prefix.
622 return strncmp(name, "tun", 3) == 0;
623 }
624
UpdateCurrentConnectionType()625 void AddressTrackerLinux::UpdateCurrentConnectionType() {
626 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
627 AddressTrackerLinux::AddressMap address_map = GetAddressMap();
628 std::unordered_set<int> online_links = GetOnlineLinks();
629
630 // Strip out tunnel interfaces from online_links
631 for (auto it = online_links.cbegin(); it != online_links.cend();) {
632 if (IsTunnelInterface(*it)) {
633 it = online_links.erase(it);
634 } else {
635 ++it;
636 }
637 }
638
639 NetworkInterfaceList networks;
640 NetworkChangeNotifier::ConnectionType type =
641 NetworkChangeNotifier::CONNECTION_NONE;
642 if (GetNetworkListImpl(&networks, 0, online_links, address_map,
643 get_interface_name_)) {
644 type = NetworkChangeNotifier::ConnectionTypeFromInterfaceList(networks);
645 } else {
646 type = online_links.empty() ? NetworkChangeNotifier::CONNECTION_NONE
647 : NetworkChangeNotifier::CONNECTION_UNKNOWN;
648 }
649
650 AddressTrackerAutoLock lock(*this, connection_type_lock_);
651 current_connection_type_ = type;
652 }
653
RunDiffCallback()654 void AddressTrackerLinux::RunDiffCallback() {
655 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
656 DCHECK(tracking_);
657 DCHECK(address_map_diff_.has_value());
658 DCHECK(online_links_diff_.has_value());
659 // It's fine to access `address_map_diff_` and `online_links_diff_` without
660 // any locking here, as the only time they are ever accessed on another thread
661 // is in GetInitialDataAndStartRecordingDiffs(). But
662 // GetInitialDataAndStartRecordingDiffs() must be called before
663 // SetDiffCallback(), which must be called before RunDiffCallback(), so this
664 // function cannot overlap with any modifications on another thread.
665
666 // There should be a diff or the DiffCallback shouldn't be run.
667 if (address_map_diff_->empty() && online_links_diff_->empty()) {
668 return;
669 }
670 diff_callback_.Run(address_map_diff_.value(), online_links_diff_.value());
671 address_map_diff_->clear();
672 online_links_diff_->clear();
673 }
674
GetThreadsWaitingForConnectionTypeInitForTesting()675 int AddressTrackerLinux::GetThreadsWaitingForConnectionTypeInitForTesting() {
676 AddressTrackerAutoLock lock(*this, connection_type_lock_);
677 return threads_waiting_for_connection_type_initialization_;
678 }
679
AddressTrackerAutoLock(const AddressTrackerLinux & tracker,base::Lock & lock)680 AddressTrackerLinux::AddressTrackerAutoLock::AddressTrackerAutoLock(
681 const AddressTrackerLinux& tracker,
682 base::Lock& lock)
683 : tracker_(tracker), lock_(lock) {
684 if (tracker_->tracking_) {
685 lock_->Acquire();
686 } else {
687 DCHECK_CALLED_ON_VALID_SEQUENCE(tracker_->sequence_checker_);
688 }
689 }
690
~AddressTrackerAutoLock()691 AddressTrackerLinux::AddressTrackerAutoLock::~AddressTrackerAutoLock() {
692 if (tracker_->tracking_) {
693 lock_->AssertAcquired();
694 lock_->Release();
695 } else {
696 DCHECK_CALLED_ON_VALID_SEQUENCE(tracker_->sequence_checker_);
697 }
698 }
699
700 } // namespace net::internal
701