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#include <linux/if.h>
9#include <sys/ioctl.h>
10
11#include "base/logging.h"
12#include "base/posix/eintr_wrapper.h"
13#include "base/threading/thread_restrictions.h"
14
15namespace net {
16namespace internal {
17
18namespace {
19
20// Retrieves address from NETLINK address message.
21// Sets |really_deprecated| for IPv6 addresses with preferred lifetimes of 0.
22bool GetAddress(const struct nlmsghdr* header,
23                IPAddressNumber* out,
24                bool* really_deprecated) {
25  if (really_deprecated)
26    *really_deprecated = false;
27  const struct ifaddrmsg* msg =
28      reinterpret_cast<struct ifaddrmsg*>(NLMSG_DATA(header));
29  size_t address_length = 0;
30  switch (msg->ifa_family) {
31    case AF_INET:
32      address_length = kIPv4AddressSize;
33      break;
34    case AF_INET6:
35      address_length = kIPv6AddressSize;
36      break;
37    default:
38      // Unknown family.
39      return false;
40  }
41  // Use IFA_ADDRESS unless IFA_LOCAL is present. This behavior here is based on
42  // getaddrinfo in glibc (check_pf.c). Judging from kernel implementation of
43  // NETLINK, IPv4 addresses have only the IFA_ADDRESS attribute, while IPv6
44  // have the IFA_LOCAL attribute.
45  unsigned char* address = NULL;
46  unsigned char* local = NULL;
47  size_t length = IFA_PAYLOAD(header);
48  for (const struct rtattr* attr =
49           reinterpret_cast<const struct rtattr*>(IFA_RTA(msg));
50       RTA_OK(attr, length);
51       attr = RTA_NEXT(attr, length)) {
52    switch (attr->rta_type) {
53      case IFA_ADDRESS:
54        DCHECK_GE(RTA_PAYLOAD(attr), address_length);
55        address = reinterpret_cast<unsigned char*>(RTA_DATA(attr));
56        break;
57      case IFA_LOCAL:
58        DCHECK_GE(RTA_PAYLOAD(attr), address_length);
59        local = reinterpret_cast<unsigned char*>(RTA_DATA(attr));
60        break;
61      case IFA_CACHEINFO: {
62        const struct ifa_cacheinfo *cache_info =
63            reinterpret_cast<const struct ifa_cacheinfo*>(RTA_DATA(attr));
64        if (really_deprecated)
65          *really_deprecated = (cache_info->ifa_prefered == 0);
66      } break;
67      default:
68        break;
69    }
70  }
71  if (local)
72    address = local;
73  if (!address)
74    return false;
75  out->assign(address, address + address_length);
76  return true;
77}
78
79// Returns the name for the interface with interface index |interface_index|.
80// The return value points to a function-scoped static so it may be changed by
81// subsequent calls. This function could be replaced with if_indextoname() but
82// net/if.h cannot be mixed with linux/if.h so we'll stick with exclusively
83// talking to the kernel and not the C library.
84const char* GetInterfaceName(int interface_index) {
85  int ioctl_socket = socket(AF_INET, SOCK_DGRAM, 0);
86  if (ioctl_socket < 0)
87    return "";
88  static struct ifreq ifr;
89  memset(&ifr, 0, sizeof(ifr));
90  ifr.ifr_ifindex = interface_index;
91  int rv = ioctl(ioctl_socket, SIOCGIFNAME, &ifr);
92  close(ioctl_socket);
93  if (rv != 0)
94    return "";
95  return ifr.ifr_name;
96}
97
98}  // namespace
99
100AddressTrackerLinux::AddressTrackerLinux()
101    : get_interface_name_(GetInterfaceName),
102      address_callback_(base::Bind(&base::DoNothing)),
103      link_callback_(base::Bind(&base::DoNothing)),
104      tunnel_callback_(base::Bind(&base::DoNothing)),
105      netlink_fd_(-1),
106      is_offline_(true),
107      is_offline_initialized_(false),
108      is_offline_initialized_cv_(&is_offline_lock_),
109      tracking_(false) {
110}
111
112AddressTrackerLinux::AddressTrackerLinux(const base::Closure& address_callback,
113                                         const base::Closure& link_callback,
114                                         const base::Closure& tunnel_callback)
115    : get_interface_name_(GetInterfaceName),
116      address_callback_(address_callback),
117      link_callback_(link_callback),
118      tunnel_callback_(tunnel_callback),
119      netlink_fd_(-1),
120      is_offline_(true),
121      is_offline_initialized_(false),
122      is_offline_initialized_cv_(&is_offline_lock_),
123      tracking_(true) {
124  DCHECK(!address_callback.is_null());
125  DCHECK(!link_callback.is_null());
126}
127
128AddressTrackerLinux::~AddressTrackerLinux() {
129  CloseSocket();
130}
131
132void AddressTrackerLinux::Init() {
133  netlink_fd_ = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
134  if (netlink_fd_ < 0) {
135    PLOG(ERROR) << "Could not create NETLINK socket";
136    AbortAndForceOnline();
137    return;
138  }
139
140  int rv;
141
142  if (tracking_) {
143    // Request notifications.
144    struct sockaddr_nl addr = {};
145    addr.nl_family = AF_NETLINK;
146    addr.nl_pid = getpid();
147    // TODO(szym): Track RTMGRP_LINK as well for ifi_type,
148    // http://crbug.com/113993
149    addr.nl_groups =
150        RTMGRP_IPV4_IFADDR | RTMGRP_IPV6_IFADDR | RTMGRP_NOTIFY | RTMGRP_LINK;
151    rv = bind(
152        netlink_fd_, reinterpret_cast<struct sockaddr*>(&addr), sizeof(addr));
153    if (rv < 0) {
154      PLOG(ERROR) << "Could not bind NETLINK socket";
155      AbortAndForceOnline();
156      return;
157    }
158  }
159
160  // Request dump of addresses.
161  struct sockaddr_nl peer = {};
162  peer.nl_family = AF_NETLINK;
163
164  struct {
165    struct nlmsghdr header;
166    struct rtgenmsg msg;
167  } request = {};
168
169  request.header.nlmsg_len = NLMSG_LENGTH(sizeof(request.msg));
170  request.header.nlmsg_type = RTM_GETADDR;
171  request.header.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
172  request.header.nlmsg_pid = getpid();
173  request.msg.rtgen_family = AF_UNSPEC;
174
175  rv = HANDLE_EINTR(sendto(netlink_fd_, &request, request.header.nlmsg_len,
176                           0, reinterpret_cast<struct sockaddr*>(&peer),
177                           sizeof(peer)));
178  if (rv < 0) {
179    PLOG(ERROR) << "Could not send NETLINK request";
180    AbortAndForceOnline();
181    return;
182  }
183
184  // Consume pending message to populate the AddressMap, but don't notify.
185  // Sending another request without first reading responses results in EBUSY.
186  bool address_changed;
187  bool link_changed;
188  bool tunnel_changed;
189  ReadMessages(&address_changed, &link_changed, &tunnel_changed);
190
191  // Request dump of link state
192  request.header.nlmsg_type = RTM_GETLINK;
193
194  rv = HANDLE_EINTR(sendto(netlink_fd_, &request, request.header.nlmsg_len, 0,
195                           reinterpret_cast<struct sockaddr*>(&peer),
196                           sizeof(peer)));
197  if (rv < 0) {
198    PLOG(ERROR) << "Could not send NETLINK request";
199    AbortAndForceOnline();
200    return;
201  }
202
203  // Consume pending message to populate links_online_, but don't notify.
204  ReadMessages(&address_changed, &link_changed, &tunnel_changed);
205  {
206    AddressTrackerAutoLock lock(*this, is_offline_lock_);
207    is_offline_initialized_ = true;
208    is_offline_initialized_cv_.Signal();
209  }
210
211  if (tracking_) {
212    rv = base::MessageLoopForIO::current()->WatchFileDescriptor(
213        netlink_fd_, true, base::MessageLoopForIO::WATCH_READ, &watcher_, this);
214    if (rv < 0) {
215      PLOG(ERROR) << "Could not watch NETLINK socket";
216      AbortAndForceOnline();
217      return;
218    }
219  }
220}
221
222void AddressTrackerLinux::AbortAndForceOnline() {
223  CloseSocket();
224  AddressTrackerAutoLock lock(*this, is_offline_lock_);
225  is_offline_ = false;
226  is_offline_initialized_ = true;
227  is_offline_initialized_cv_.Signal();
228}
229
230AddressTrackerLinux::AddressMap AddressTrackerLinux::GetAddressMap() const {
231  AddressTrackerAutoLock lock(*this, address_map_lock_);
232  return address_map_;
233}
234
235base::hash_set<int> AddressTrackerLinux::GetOnlineLinks() const {
236  AddressTrackerAutoLock lock(*this, online_links_lock_);
237  return online_links_;
238}
239
240NetworkChangeNotifier::ConnectionType
241AddressTrackerLinux::GetCurrentConnectionType() {
242  // http://crbug.com/125097
243  base::ThreadRestrictions::ScopedAllowWait allow_wait;
244  AddressTrackerAutoLock lock(*this, is_offline_lock_);
245  // Make sure the initial offline state is set before returning.
246  while (!is_offline_initialized_) {
247    is_offline_initialized_cv_.Wait();
248  }
249  // TODO(droger): Return something more detailed than CONNECTION_UNKNOWN.
250  // http://crbug.com/160537
251  return is_offline_ ? NetworkChangeNotifier::CONNECTION_NONE :
252                       NetworkChangeNotifier::CONNECTION_UNKNOWN;
253}
254
255void AddressTrackerLinux::ReadMessages(bool* address_changed,
256                                       bool* link_changed,
257                                       bool* tunnel_changed) {
258  *address_changed = false;
259  *link_changed = false;
260  *tunnel_changed = false;
261  char buffer[4096];
262  bool first_loop = true;
263  for (;;) {
264    int rv = HANDLE_EINTR(recv(netlink_fd_,
265                               buffer,
266                               sizeof(buffer),
267                               // Block the first time through loop.
268                               first_loop ? 0 : MSG_DONTWAIT));
269    first_loop = false;
270    if (rv == 0) {
271      LOG(ERROR) << "Unexpected shutdown of NETLINK socket.";
272      return;
273    }
274    if (rv < 0) {
275      if ((errno == EAGAIN) || (errno == EWOULDBLOCK))
276        break;
277      PLOG(ERROR) << "Failed to recv from netlink socket";
278      return;
279    }
280    HandleMessage(buffer, rv, address_changed, link_changed, tunnel_changed);
281  }
282  if (*link_changed) {
283    bool is_offline;
284    {
285      AddressTrackerAutoLock lock(*this, online_links_lock_);
286      is_offline = online_links_.empty();
287    }
288    AddressTrackerAutoLock lock(*this, is_offline_lock_);
289    is_offline_ = is_offline;
290  }
291}
292
293void AddressTrackerLinux::HandleMessage(char* buffer,
294                                        size_t length,
295                                        bool* address_changed,
296                                        bool* link_changed,
297                                        bool* tunnel_changed) {
298  DCHECK(buffer);
299  for (struct nlmsghdr* header = reinterpret_cast<struct nlmsghdr*>(buffer);
300       NLMSG_OK(header, length);
301       header = NLMSG_NEXT(header, length)) {
302    switch (header->nlmsg_type) {
303      case NLMSG_DONE:
304        return;
305      case NLMSG_ERROR: {
306        const struct nlmsgerr* msg =
307            reinterpret_cast<struct nlmsgerr*>(NLMSG_DATA(header));
308        LOG(ERROR) << "Unexpected netlink error " << msg->error << ".";
309      } return;
310      case RTM_NEWADDR: {
311        IPAddressNumber address;
312        bool really_deprecated;
313        if (GetAddress(header, &address, &really_deprecated)) {
314          AddressTrackerAutoLock lock(*this, address_map_lock_);
315          struct ifaddrmsg* msg =
316              reinterpret_cast<struct ifaddrmsg*>(NLMSG_DATA(header));
317          // Routers may frequently (every few seconds) output the IPv6 ULA
318          // prefix which can cause the linux kernel to frequently output two
319          // back-to-back messages, one without the deprecated flag and one with
320          // the deprecated flag but both with preferred lifetimes of 0. Avoid
321          // interpretting this as an actual change by canonicalizing the two
322          // messages by setting the deprecated flag based on the preferred
323          // lifetime also.  http://crbug.com/268042
324          if (really_deprecated)
325            msg->ifa_flags |= IFA_F_DEPRECATED;
326          // Only indicate change if the address is new or ifaddrmsg info has
327          // changed.
328          AddressMap::iterator it = address_map_.find(address);
329          if (it == address_map_.end()) {
330            address_map_.insert(it, std::make_pair(address, *msg));
331            *address_changed = true;
332          } else if (memcmp(&it->second, msg, sizeof(*msg))) {
333            it->second = *msg;
334            *address_changed = true;
335          }
336        }
337      } break;
338      case RTM_DELADDR: {
339        IPAddressNumber address;
340        if (GetAddress(header, &address, NULL)) {
341          AddressTrackerAutoLock lock(*this, address_map_lock_);
342          if (address_map_.erase(address))
343            *address_changed = true;
344        }
345      } break;
346      case RTM_NEWLINK: {
347        const struct ifinfomsg* msg =
348            reinterpret_cast<struct ifinfomsg*>(NLMSG_DATA(header));
349        if (!(msg->ifi_flags & IFF_LOOPBACK) && (msg->ifi_flags & IFF_UP) &&
350            (msg->ifi_flags & IFF_LOWER_UP) && (msg->ifi_flags & IFF_RUNNING)) {
351          AddressTrackerAutoLock lock(*this, online_links_lock_);
352          if (online_links_.insert(msg->ifi_index).second) {
353            *link_changed = true;
354            if (IsTunnelInterface(msg))
355              *tunnel_changed = true;
356          }
357        } else {
358          AddressTrackerAutoLock lock(*this, online_links_lock_);
359          if (online_links_.erase(msg->ifi_index)) {
360            *link_changed = true;
361            if (IsTunnelInterface(msg))
362              *tunnel_changed = true;
363          }
364        }
365      } break;
366      case RTM_DELLINK: {
367        const struct ifinfomsg* msg =
368            reinterpret_cast<struct ifinfomsg*>(NLMSG_DATA(header));
369        AddressTrackerAutoLock lock(*this, online_links_lock_);
370        if (online_links_.erase(msg->ifi_index)) {
371          *link_changed = true;
372          if (IsTunnelInterface(msg))
373            *tunnel_changed = true;
374        }
375      } break;
376      default:
377        break;
378    }
379  }
380}
381
382void AddressTrackerLinux::OnFileCanReadWithoutBlocking(int fd) {
383  DCHECK_EQ(netlink_fd_, fd);
384  bool address_changed;
385  bool link_changed;
386  bool tunnel_changed;
387  ReadMessages(&address_changed, &link_changed, &tunnel_changed);
388  if (address_changed)
389    address_callback_.Run();
390  if (link_changed)
391    link_callback_.Run();
392  if (tunnel_changed)
393    tunnel_callback_.Run();
394}
395
396void AddressTrackerLinux::OnFileCanWriteWithoutBlocking(int /* fd */) {}
397
398void AddressTrackerLinux::CloseSocket() {
399  if (netlink_fd_ >= 0 && IGNORE_EINTR(close(netlink_fd_)) < 0)
400    PLOG(ERROR) << "Could not close NETLINK socket.";
401  netlink_fd_ = -1;
402}
403
404bool AddressTrackerLinux::IsTunnelInterface(const struct ifinfomsg* msg) const {
405  // Linux kernel drivers/net/tun.c uses "tun" name prefix.
406  return strncmp(get_interface_name_(msg->ifi_index), "tun", 3) == 0;
407}
408
409AddressTrackerLinux::AddressTrackerAutoLock::AddressTrackerAutoLock(
410    const AddressTrackerLinux& tracker,
411    base::Lock& lock)
412    : tracker_(tracker), lock_(lock) {
413  if (tracker_.tracking_) {
414    lock_.Acquire();
415  } else {
416    DCHECK(tracker_.thread_checker_.CalledOnValidThread());
417  }
418}
419
420AddressTrackerLinux::AddressTrackerAutoLock::~AddressTrackerAutoLock() {
421  if (tracker_.tracking_) {
422    lock_.AssertAcquired();
423    lock_.Release();
424  }
425}
426
427}  // namespace internal
428}  // namespace net
429