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