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