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