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