xref: /aosp_15_r20/external/cronet/net/base/address_tracker_linux.cc (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 #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