Home | History | Annotate | Download | only in base
      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 
     15 namespace net {
     16 namespace internal {
     17 
     18 namespace {
     19 
     20 // Retrieves address from NETLINK address message.
     21 // Sets |really_deprecated| for IPv6 addresses with preferred lifetimes of 0.
     22 bool 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.
     84 const 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 
    100 AddressTrackerLinux::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 
    112 AddressTrackerLinux::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 
    128 AddressTrackerLinux::~AddressTrackerLinux() {
    129   CloseSocket();
    130 }
    131 
    132 void 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 
    222 void 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 
    230 AddressTrackerLinux::AddressMap AddressTrackerLinux::GetAddressMap() const {
    231   AddressTrackerAutoLock lock(*this, address_map_lock_);
    232   return address_map_;
    233 }
    234 
    235 base::hash_set<int> AddressTrackerLinux::GetOnlineLinks() const {
    236   AddressTrackerAutoLock lock(*this, online_links_lock_);
    237   return online_links_;
    238 }
    239 
    240 NetworkChangeNotifier::ConnectionType
    241 AddressTrackerLinux::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 
    255 void 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 
    293 void 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 
    382 void 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 
    396 void AddressTrackerLinux::OnFileCanWriteWithoutBlocking(int /* fd */) {}
    397 
    398 void 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 
    404 bool 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 
    409 AddressTrackerLinux::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 
    420 AddressTrackerLinux::AddressTrackerAutoLock::~AddressTrackerAutoLock() {
    421   if (tracker_.tracking_) {
    422     lock_.AssertAcquired();
    423     lock_.Release();
    424   }
    425 }
    426 
    427 }  // namespace internal
    428 }  // namespace net
    429