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