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