1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
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 #if !defined(OS_BSD)
9 #include <linux/if.h>
10 #endif
11 #include <stdint.h>
12 #include <sys/ioctl.h>
13 #include <utility>
14
15 #include "base/callback_helpers.h"
16 #include "base/files/scoped_file.h"
17 #include "base/logging.h"
18 #include "base/optional.h"
19 #include "base/posix/eintr_wrapper.h"
20 #include "base/task/current_thread.h"
21 #include "base/threading/scoped_blocking_call.h"
22 #include "build/build_config.h"
23 #include "net/base/network_interfaces_linux.h"
24
25 #if defined(OS_ANDROID)
26 #include "base/android/build_info.h"
27 #endif
28
29 namespace net {
30 namespace internal {
31
32 namespace {
33
34 // Some kernel functions such as wireless_send_event and rtnetlink_ifinfo_prep
35 // may send spurious messages over rtnetlink. RTM_NEWLINK messages where
36 // ifi_change == 0 and rta_type == IFLA_WIRELESS should be ignored.
IgnoreWirelessChange(const struct ifinfomsg * msg,int length)37 bool IgnoreWirelessChange(const struct ifinfomsg* msg, int length) {
38 for (const struct rtattr* attr = IFLA_RTA(msg); RTA_OK(attr, length);
39 attr = RTA_NEXT(attr, length)) {
40 if (attr->rta_type == IFLA_WIRELESS && msg->ifi_change == 0)
41 return true;
42 }
43 return false;
44 }
45
46 // Retrieves address from NETLINK address message.
47 // Sets |really_deprecated| for IPv6 addresses with preferred lifetimes of 0.
48 // Precondition: |header| must already be validated with NLMSG_OK.
GetAddress(const struct nlmsghdr * header,int header_length,IPAddress * out,bool * really_deprecated)49 bool GetAddress(const struct nlmsghdr* header,
50 int header_length,
51 IPAddress* out,
52 bool* really_deprecated) {
53 if (really_deprecated)
54 *really_deprecated = false;
55
56 // Extract the message and update |header_length| to be the number of
57 // remaining bytes.
58 const struct ifaddrmsg* msg =
59 reinterpret_cast<const struct ifaddrmsg*>(NLMSG_DATA(header));
60 header_length -= NLMSG_HDRLEN;
61
62 size_t address_length = 0;
63 switch (msg->ifa_family) {
64 case AF_INET:
65 address_length = IPAddress::kIPv4AddressSize;
66 break;
67 case AF_INET6:
68 address_length = IPAddress::kIPv6AddressSize;
69 break;
70 default:
71 // Unknown family.
72 return false;
73 }
74 // Use IFA_ADDRESS unless IFA_LOCAL is present. This behavior here is based on
75 // getaddrinfo in glibc (check_pf.c). Judging from kernel implementation of
76 // NETLINK, IPv4 addresses have only the IFA_ADDRESS attribute, while IPv6
77 // have the IFA_LOCAL attribute.
78 uint8_t* address = NULL;
79 uint8_t* local = NULL;
80 int length = IFA_PAYLOAD(header);
81 if (length > header_length) {
82 LOG(ERROR) << "ifaddrmsg length exceeds bounds";
83 return false;
84 }
85 for (const struct rtattr* attr =
86 reinterpret_cast<const struct rtattr*>(IFA_RTA(msg));
87 RTA_OK(attr, length); attr = RTA_NEXT(attr, length)) {
88 switch (attr->rta_type) {
89 case IFA_ADDRESS:
90 if (RTA_PAYLOAD(attr) < address_length) {
91 LOG(ERROR) << "attr does not have enough bytes to read an address";
92 return false;
93 }
94 address = reinterpret_cast<uint8_t*>(RTA_DATA(attr));
95 break;
96 case IFA_LOCAL:
97 if (RTA_PAYLOAD(attr) < address_length) {
98 LOG(ERROR) << "attr does not have enough bytes to read an address";
99 return false;
100 }
101 local = reinterpret_cast<uint8_t*>(RTA_DATA(attr));
102 break;
103 case IFA_CACHEINFO: {
104 if (RTA_PAYLOAD(attr) < sizeof(struct ifa_cacheinfo)) {
105 LOG(ERROR)
106 << "attr does not have enough bytes to read an ifa_cacheinfo";
107 return false;
108 }
109 const struct ifa_cacheinfo* cache_info =
110 reinterpret_cast<const struct ifa_cacheinfo*>(RTA_DATA(attr));
111 if (really_deprecated)
112 *really_deprecated = (cache_info->ifa_prefered == 0);
113 } break;
114 default:
115 break;
116 }
117 }
118 if (local)
119 address = local;
120 if (!address)
121 return false;
122 *out = IPAddress(address, address_length);
123 return true;
124 }
125
126 // SafelyCastNetlinkMsgData<T> performs a bounds check before casting |header|'s
127 // data to a |T*|. When the bounds check fails, returns nullptr.
128 template <typename T>
SafelyCastNetlinkMsgData(const struct nlmsghdr * header,int length)129 T* SafelyCastNetlinkMsgData(const struct nlmsghdr* header, int length) {
130 DCHECK(NLMSG_OK(header, static_cast<__u32>(length)));
131 if (length <= 0 || static_cast<size_t>(length) < NLMSG_HDRLEN + sizeof(T))
132 return nullptr;
133 return reinterpret_cast<const T*>(NLMSG_DATA(header));
134 }
135
136 } // namespace
137
138 // static
GetInterfaceName(int interface_index,char * buf)139 char* AddressTrackerLinux::GetInterfaceName(int interface_index, char* buf) {
140 memset(buf, 0, IFNAMSIZ);
141 base::ScopedFD ioctl_socket = GetSocketForIoctl();
142 if (!ioctl_socket.is_valid())
143 return buf;
144
145 struct ifreq ifr = {};
146 ifr.ifr_ifindex = interface_index;
147
148 if (ioctl(ioctl_socket.get(), SIOCGIFNAME, &ifr) == 0)
149 strncpy(buf, ifr.ifr_name, IFNAMSIZ - 1);
150 return buf;
151 }
152
AddressTrackerLinux()153 AddressTrackerLinux::AddressTrackerLinux()
154 : get_interface_name_(GetInterfaceName),
155 address_callback_(base::DoNothing()),
156 link_callback_(base::DoNothing()),
157 tunnel_callback_(base::DoNothing()),
158 ignored_interfaces_(),
159 connection_type_initialized_(false),
160 connection_type_initialized_cv_(&connection_type_lock_),
161 current_connection_type_(NetworkChangeNotifier::CONNECTION_NONE),
162 tracking_(false),
163 threads_waiting_for_connection_type_initialization_(0) {}
164
AddressTrackerLinux(const base::RepeatingClosure & address_callback,const base::RepeatingClosure & link_callback,const base::RepeatingClosure & tunnel_callback,const std::unordered_set<std::string> & ignored_interfaces)165 AddressTrackerLinux::AddressTrackerLinux(
166 const base::RepeatingClosure& address_callback,
167 const base::RepeatingClosure& link_callback,
168 const base::RepeatingClosure& tunnel_callback,
169 const std::unordered_set<std::string>& ignored_interfaces)
170 : get_interface_name_(GetInterfaceName),
171 address_callback_(address_callback),
172 link_callback_(link_callback),
173 tunnel_callback_(tunnel_callback),
174 ignored_interfaces_(ignored_interfaces),
175 connection_type_initialized_(false),
176 connection_type_initialized_cv_(&connection_type_lock_),
177 current_connection_type_(NetworkChangeNotifier::CONNECTION_NONE),
178 tracking_(true),
179 threads_waiting_for_connection_type_initialization_(0) {
180 DCHECK(!address_callback.is_null());
181 DCHECK(!link_callback.is_null());
182 }
183
184 AddressTrackerLinux::~AddressTrackerLinux() = default;
185
Init()186 void AddressTrackerLinux::Init() {
187 #if defined(OS_ANDROID)
188 // RTM_GETLINK stopped working in Android 11 (see
189 // https://developer.android.com/preview/privacy/mac-address),
190 // so AddressTrackerLinux should not be used in later versions
191 // of Android. Chromium code doesn't need it past Android P.
192 DCHECK_LT(base::android::BuildInfo::GetInstance()->sdk_int(),
193 base::android::SDK_VERSION_P);
194 #endif
195 #if !defined(OS_FREEBSD) && !defined(OS_DRAGONFLY)
196 netlink_fd_.reset(socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE));
197 if (!netlink_fd_.is_valid()) {
198 PLOG(ERROR) << "Could not create NETLINK socket";
199 AbortAndForceOnline();
200 return;
201 }
202
203 int rv;
204
205 if (tracking_) {
206 // Request notifications.
207 struct sockaddr_nl addr = {};
208 addr.nl_family = AF_NETLINK;
209 addr.nl_pid = getpid();
210 // TODO(szym): Track RTMGRP_LINK as well for ifi_type,
211 // http://crbug.com/113993
212 addr.nl_groups =
213 RTMGRP_IPV4_IFADDR | RTMGRP_IPV6_IFADDR | RTMGRP_NOTIFY | RTMGRP_LINK;
214 rv = bind(netlink_fd_.get(), reinterpret_cast<struct sockaddr*>(&addr),
215 sizeof(addr));
216 if (rv < 0) {
217 PLOG(ERROR) << "Could not bind NETLINK socket";
218 AbortAndForceOnline();
219 return;
220 }
221 }
222
223 // Request dump of addresses.
224 struct sockaddr_nl peer = {};
225 peer.nl_family = AF_NETLINK;
226
227 struct {
228 struct nlmsghdr header;
229 struct rtgenmsg msg;
230 } request = {};
231
232 request.header.nlmsg_len = NLMSG_LENGTH(sizeof(request.msg));
233 request.header.nlmsg_type = RTM_GETADDR;
234 request.header.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
235 request.header.nlmsg_pid = getpid();
236 request.msg.rtgen_family = AF_UNSPEC;
237
238 rv = HANDLE_EINTR(
239 sendto(netlink_fd_.get(), &request, request.header.nlmsg_len, 0,
240 reinterpret_cast<struct sockaddr*>(&peer), sizeof(peer)));
241 if (rv < 0) {
242 PLOG(ERROR) << "Could not send NETLINK request";
243 AbortAndForceOnline();
244 return;
245 }
246
247 // Consume pending message to populate the AddressMap, but don't notify.
248 // Sending another request without first reading responses results in EBUSY.
249 bool address_changed;
250 bool link_changed;
251 bool tunnel_changed;
252 ReadMessages(&address_changed, &link_changed, &tunnel_changed);
253
254 // Request dump of link state
255 request.header.nlmsg_type = RTM_GETLINK;
256
257 rv = HANDLE_EINTR(
258 sendto(netlink_fd_.get(), &request, request.header.nlmsg_len, 0,
259 reinterpret_cast<struct sockaddr*>(&peer), sizeof(peer)));
260 if (rv < 0) {
261 PLOG(ERROR) << "Could not send NETLINK request";
262 AbortAndForceOnline();
263 return;
264 }
265
266 // Consume pending message to populate links_online_, but don't notify.
267 ReadMessages(&address_changed, &link_changed, &tunnel_changed);
268 {
269 AddressTrackerAutoLock lock(*this, connection_type_lock_);
270 connection_type_initialized_ = true;
271 connection_type_initialized_cv_.Broadcast();
272 }
273
274 if (tracking_) {
275 watcher_ = base::FileDescriptorWatcher::WatchReadable(
276 netlink_fd_.get(),
277 base::BindRepeating(&AddressTrackerLinux::OnFileCanReadWithoutBlocking,
278 base::Unretained(this)));
279 }
280 #endif // !OS_FREEBSD
281 }
282
AbortAndForceOnline()283 void AddressTrackerLinux::AbortAndForceOnline() {
284 watcher_.reset();
285 netlink_fd_.reset();
286 AddressTrackerAutoLock lock(*this, connection_type_lock_);
287 current_connection_type_ = NetworkChangeNotifier::CONNECTION_UNKNOWN;
288 connection_type_initialized_ = true;
289 connection_type_initialized_cv_.Broadcast();
290 }
291
292 #if !defined(OS_BSD)
GetAddressMap() const293 AddressTrackerLinux::AddressMap AddressTrackerLinux::GetAddressMap() const {
294 AddressTrackerAutoLock lock(*this, address_map_lock_);
295 return address_map_;
296 }
297
GetOnlineLinks() const298 std::unordered_set<int> AddressTrackerLinux::GetOnlineLinks() const {
299 AddressTrackerAutoLock lock(*this, online_links_lock_);
300 return online_links_;
301 }
302
IsInterfaceIgnored(int interface_index) const303 bool AddressTrackerLinux::IsInterfaceIgnored(int interface_index) const {
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 #endif // !OS_BSD
312
313 NetworkChangeNotifier::ConnectionType
GetCurrentConnectionType()314 AddressTrackerLinux::GetCurrentConnectionType() {
315 // http://crbug.com/125097
316 base::ScopedAllowBaseSyncPrimitivesOutsideBlockingScope allow_wait;
317 AddressTrackerAutoLock lock(*this, connection_type_lock_);
318 // Make sure the initial connection type is set before returning.
319 threads_waiting_for_connection_type_initialization_++;
320 while (!connection_type_initialized_) {
321 connection_type_initialized_cv_.Wait();
322 }
323 threads_waiting_for_connection_type_initialization_--;
324 return current_connection_type_;
325 }
326
ReadMessages(bool * address_changed,bool * link_changed,bool * tunnel_changed)327 void AddressTrackerLinux::ReadMessages(bool* address_changed,
328 bool* link_changed,
329 bool* tunnel_changed) {
330 *address_changed = false;
331 *link_changed = false;
332 *tunnel_changed = false;
333 char buffer[4096];
334 bool first_loop = true;
335 {
336 base::Optional<base::ScopedBlockingCall> blocking_call;
337 if (tracking_) {
338 // If the loop below takes a long time to run, a new thread should added
339 // to the current thread pool to ensure forward progress of all tasks.
340 blocking_call.emplace(FROM_HERE, base::BlockingType::MAY_BLOCK);
341 }
342
343 for (;;) {
344 int rv = HANDLE_EINTR(recv(netlink_fd_.get(), buffer, sizeof(buffer),
345 // Block the first time through loop.
346 first_loop ? 0 : MSG_DONTWAIT));
347 first_loop = false;
348 if (rv == 0) {
349 LOG(ERROR) << "Unexpected shutdown of NETLINK socket.";
350 return;
351 }
352 if (rv < 0) {
353 if ((errno == EAGAIN) || (errno == EWOULDBLOCK))
354 break;
355 PLOG(ERROR) << "Failed to recv from netlink socket";
356 return;
357 }
358 HandleMessage(buffer, rv, address_changed, link_changed, tunnel_changed);
359 }
360 }
361 if (*link_changed || *address_changed)
362 UpdateCurrentConnectionType();
363 }
364
HandleMessage(const char * buffer,int length,bool * address_changed,bool * link_changed,bool * tunnel_changed)365 void AddressTrackerLinux::HandleMessage(const char* buffer,
366 int length,
367 bool* address_changed,
368 bool* link_changed,
369 bool* tunnel_changed) {
370 #if !defined(OS_FREEBSD) && !defined(OS_DRAGONFLY)
371 DCHECK(buffer);
372 // Note that NLMSG_NEXT decrements |length| to reflect the number of bytes
373 // remaining in |buffer|.
374 for (const struct nlmsghdr* header =
375 reinterpret_cast<const struct nlmsghdr*>(buffer);
376 length >= 0 && NLMSG_OK(header, static_cast<__u32>(length));
377 header = NLMSG_NEXT(header, length)) {
378 // The |header| pointer should never precede |buffer|.
379 DCHECK_LE(buffer, reinterpret_cast<const char*>(header));
380 switch (header->nlmsg_type) {
381 case NLMSG_DONE:
382 return;
383 case NLMSG_ERROR: {
384 const struct nlmsgerr* msg =
385 SafelyCastNetlinkMsgData<const struct nlmsgerr>(header, length);
386 if (msg == nullptr)
387 return;
388 LOG(ERROR) << "Unexpected netlink error " << msg->error << ".";
389 } return;
390 case RTM_NEWADDR: {
391 IPAddress address;
392 bool really_deprecated;
393 const struct ifaddrmsg* msg =
394 SafelyCastNetlinkMsgData<const struct ifaddrmsg>(header, length);
395 if (msg == nullptr)
396 return;
397 if (IsInterfaceIgnored(msg->ifa_index))
398 break;
399 if (GetAddress(header, length, &address, &really_deprecated)) {
400 struct ifaddrmsg msg_copy = *msg;
401 AddressTrackerAutoLock lock(*this, address_map_lock_);
402 // Routers may frequently (every few seconds) output the IPv6 ULA
403 // prefix which can cause the linux kernel to frequently output two
404 // back-to-back messages, one without the deprecated flag and one with
405 // the deprecated flag but both with preferred lifetimes of 0. Avoid
406 // interpretting this as an actual change by canonicalizing the two
407 // messages by setting the deprecated flag based on the preferred
408 // lifetime also. http://crbug.com/268042
409 if (really_deprecated)
410 msg_copy.ifa_flags |= IFA_F_DEPRECATED;
411 // Only indicate change if the address is new or ifaddrmsg info has
412 // changed.
413 auto it = address_map_.find(address);
414 if (it == address_map_.end()) {
415 address_map_.insert(it, std::make_pair(address, msg_copy));
416 *address_changed = true;
417 } else if (memcmp(&it->second, &msg_copy, sizeof(msg_copy))) {
418 it->second = msg_copy;
419 *address_changed = true;
420 }
421 }
422 } break;
423 case RTM_DELADDR: {
424 IPAddress address;
425 const struct ifaddrmsg* msg =
426 SafelyCastNetlinkMsgData<const struct ifaddrmsg>(header, length);
427 if (msg == nullptr)
428 return;
429 if (IsInterfaceIgnored(msg->ifa_index))
430 break;
431 if (GetAddress(header, length, &address, nullptr)) {
432 AddressTrackerAutoLock lock(*this, address_map_lock_);
433 if (address_map_.erase(address))
434 *address_changed = true;
435 }
436 } break;
437 case RTM_NEWLINK: {
438 const struct ifinfomsg* msg =
439 SafelyCastNetlinkMsgData<const struct ifinfomsg>(header, length);
440 if (msg == nullptr)
441 return;
442 if (IsInterfaceIgnored(msg->ifi_index))
443 break;
444 if (IgnoreWirelessChange(msg, IFLA_PAYLOAD(header))) {
445 VLOG(2) << "Ignoring RTM_NEWLINK message";
446 break;
447 }
448 if (!(msg->ifi_flags & IFF_LOOPBACK) && (msg->ifi_flags & IFF_UP) &&
449 (msg->ifi_flags & IFF_LOWER_UP) && (msg->ifi_flags & IFF_RUNNING)) {
450 AddressTrackerAutoLock lock(*this, online_links_lock_);
451 if (online_links_.insert(msg->ifi_index).second) {
452 *link_changed = true;
453 if (IsTunnelInterface(msg->ifi_index))
454 *tunnel_changed = true;
455 }
456 } else {
457 AddressTrackerAutoLock lock(*this, online_links_lock_);
458 if (online_links_.erase(msg->ifi_index)) {
459 *link_changed = true;
460 if (IsTunnelInterface(msg->ifi_index))
461 *tunnel_changed = true;
462 }
463 }
464 } break;
465 case RTM_DELLINK: {
466 const struct ifinfomsg* msg =
467 SafelyCastNetlinkMsgData<const struct ifinfomsg>(header, length);
468 if (msg == nullptr)
469 return;
470 if (IsInterfaceIgnored(msg->ifi_index))
471 break;
472 AddressTrackerAutoLock lock(*this, online_links_lock_);
473 if (online_links_.erase(msg->ifi_index)) {
474 *link_changed = true;
475 if (IsTunnelInterface(msg->ifi_index))
476 *tunnel_changed = true;
477 }
478 } break;
479 default:
480 break;
481 }
482 }
483 #else // !OS_FREEBSD && !OS_DRAGONFLY
484 NOTIMPLEMENTED();
485 AbortAndForceOnline();
486 #endif // !OS_FREEBSD && !OS_DRAGONFLY
487 }
488
OnFileCanReadWithoutBlocking()489 void AddressTrackerLinux::OnFileCanReadWithoutBlocking() {
490 bool address_changed;
491 bool link_changed;
492 bool tunnel_changed;
493 ReadMessages(&address_changed, &link_changed, &tunnel_changed);
494 if (address_changed)
495 address_callback_.Run();
496 if (link_changed)
497 link_callback_.Run();
498 if (tunnel_changed)
499 tunnel_callback_.Run();
500 }
501
IsTunnelInterface(int interface_index) const502 bool AddressTrackerLinux::IsTunnelInterface(int interface_index) const {
503 char buf[IFNAMSIZ] = {0};
504 return IsTunnelInterfaceName(get_interface_name_(interface_index, buf));
505 }
506
507 // static
IsTunnelInterfaceName(const char * name)508 bool AddressTrackerLinux::IsTunnelInterfaceName(const char* name) {
509 // Linux kernel drivers/net/tun.c uses "tun" name prefix.
510 return strncmp(name, "tun", 3) == 0;
511 }
512
UpdateCurrentConnectionType()513 void AddressTrackerLinux::UpdateCurrentConnectionType() {
514 #if !defined(OS_FREEBSD)
515 AddressTrackerLinux::AddressMap address_map = GetAddressMap();
516 std::unordered_set<int> online_links = GetOnlineLinks();
517
518 // Strip out tunnel interfaces from online_links
519 for (auto it = online_links.cbegin(); it != online_links.cend();) {
520 if (IsTunnelInterface(*it)) {
521 it = online_links.erase(it);
522 } else {
523 ++it;
524 }
525 }
526
527 NetworkInterfaceList networks;
528 NetworkChangeNotifier::ConnectionType type =
529 NetworkChangeNotifier::CONNECTION_NONE;
530 if (GetNetworkListImpl(&networks, 0, online_links, address_map,
531 get_interface_name_)) {
532 type = NetworkChangeNotifier::ConnectionTypeFromInterfaceList(networks);
533 } else {
534 type = online_links.empty() ? NetworkChangeNotifier::CONNECTION_NONE
535 : NetworkChangeNotifier::CONNECTION_UNKNOWN;
536 }
537
538 AddressTrackerAutoLock lock(*this, connection_type_lock_);
539 current_connection_type_ = type;
540 #else
541 NOTIMPLEMENTED();
542 #endif
543 }
544
GetThreadsWaitingForConnectionTypeInitForTesting()545 int AddressTrackerLinux::GetThreadsWaitingForConnectionTypeInitForTesting() {
546 AddressTrackerAutoLock lock(*this, connection_type_lock_);
547 return threads_waiting_for_connection_type_initialization_;
548 }
549
AddressTrackerAutoLock(const AddressTrackerLinux & tracker,base::Lock & lock)550 AddressTrackerLinux::AddressTrackerAutoLock::AddressTrackerAutoLock(
551 const AddressTrackerLinux& tracker,
552 base::Lock& lock)
553 : tracker_(tracker), lock_(lock) {
554 if (tracker_.tracking_) {
555 lock_.Acquire();
556 } else {
557 DCHECK(tracker_.thread_checker_.CalledOnValidThread());
558 }
559 }
560
~AddressTrackerAutoLock()561 AddressTrackerLinux::AddressTrackerAutoLock::~AddressTrackerAutoLock() {
562 if (tracker_.tracking_) {
563 lock_.AssertAcquired();
564 lock_.Release();
565 }
566 }
567
568 } // namespace internal
569 } // namespace net
570