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