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 
     10 #include "base/logging.h"
     11 #include "base/posix/eintr_wrapper.h"
     12 #include "base/threading/thread_restrictions.h"
     13 #include "net/base/network_change_notifier_linux.h"
     14 
     15 namespace net {
     16 namespace internal {
     17 
     18 namespace {
     19 
     20 // Retrieves address from NETLINK address message.
     21 bool GetAddress(const struct nlmsghdr* header, IPAddressNumber* out) {
     22   const struct ifaddrmsg* msg =
     23       reinterpret_cast<struct ifaddrmsg*>(NLMSG_DATA(header));
     24   size_t address_length = 0;
     25   switch (msg->ifa_family) {
     26     case AF_INET:
     27       address_length = kIPv4AddressSize;
     28       break;
     29     case AF_INET6:
     30       address_length = kIPv6AddressSize;
     31       break;
     32     default:
     33       // Unknown family.
     34       return false;
     35   }
     36   // Use IFA_ADDRESS unless IFA_LOCAL is present. This behavior here is based on
     37   // getaddrinfo in glibc (check_pf.c). Judging from kernel implementation of
     38   // NETLINK, IPv4 addresses have only the IFA_ADDRESS attribute, while IPv6
     39   // have the IFA_LOCAL attribute.
     40   unsigned char* address = NULL;
     41   unsigned char* local = NULL;
     42   size_t length = IFA_PAYLOAD(header);
     43   for (const struct rtattr* attr =
     44            reinterpret_cast<const struct rtattr*>(IFA_RTA(msg));
     45        RTA_OK(attr, length);
     46        attr = RTA_NEXT(attr, length)) {
     47     switch (attr->rta_type) {
     48       case IFA_ADDRESS:
     49         DCHECK_GE(RTA_PAYLOAD(attr), address_length);
     50         address = reinterpret_cast<unsigned char*>(RTA_DATA(attr));
     51         break;
     52       case IFA_LOCAL:
     53         DCHECK_GE(RTA_PAYLOAD(attr), address_length);
     54         local = reinterpret_cast<unsigned char*>(RTA_DATA(attr));
     55         break;
     56       default:
     57         break;
     58     }
     59   }
     60   if (local)
     61     address = local;
     62   if (!address)
     63     return false;
     64   out->assign(address, address + address_length);
     65   return true;
     66 }
     67 
     68 }  // namespace
     69 
     70 AddressTrackerLinux::AddressTrackerLinux(const base::Closure& address_callback,
     71                                          const base::Closure& link_callback)
     72     : address_callback_(address_callback),
     73       link_callback_(link_callback),
     74       netlink_fd_(-1),
     75       is_offline_(true),
     76       is_offline_initialized_(false),
     77       is_offline_initialized_cv_(&is_offline_lock_) {
     78   DCHECK(!address_callback.is_null());
     79   DCHECK(!link_callback.is_null());
     80 }
     81 
     82 AddressTrackerLinux::~AddressTrackerLinux() {
     83   CloseSocket();
     84 }
     85 
     86 void AddressTrackerLinux::Init() {
     87   netlink_fd_ = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
     88   if (netlink_fd_ < 0) {
     89     PLOG(ERROR) << "Could not create NETLINK socket";
     90     AbortAndForceOnline();
     91     return;
     92   }
     93 
     94   // Request notifications.
     95   struct sockaddr_nl addr = {};
     96   addr.nl_family = AF_NETLINK;
     97   addr.nl_pid = getpid();
     98   // TODO(szym): Track RTMGRP_LINK as well for ifi_type, http://crbug.com/113993
     99   addr.nl_groups = RTMGRP_IPV4_IFADDR | RTMGRP_IPV6_IFADDR | RTMGRP_NOTIFY |
    100       RTMGRP_LINK;
    101   int rv = bind(netlink_fd_,
    102                 reinterpret_cast<struct sockaddr*>(&addr),
    103                 sizeof(addr));
    104   if (rv < 0) {
    105     PLOG(ERROR) << "Could not bind NETLINK socket";
    106     AbortAndForceOnline();
    107     return;
    108   }
    109 
    110   // Request dump of addresses.
    111   struct sockaddr_nl peer = {};
    112   peer.nl_family = AF_NETLINK;
    113 
    114   struct {
    115     struct nlmsghdr header;
    116     struct rtgenmsg msg;
    117   } request = {};
    118 
    119   request.header.nlmsg_len = NLMSG_LENGTH(sizeof(request.msg));
    120   request.header.nlmsg_type = RTM_GETADDR;
    121   request.header.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
    122   request.header.nlmsg_pid = getpid();
    123   request.msg.rtgen_family = AF_UNSPEC;
    124 
    125   rv = HANDLE_EINTR(sendto(netlink_fd_, &request, request.header.nlmsg_len,
    126                            0, reinterpret_cast<struct sockaddr*>(&peer),
    127                            sizeof(peer)));
    128   if (rv < 0) {
    129     PLOG(ERROR) << "Could not send NETLINK request";
    130     AbortAndForceOnline();
    131     return;
    132   }
    133 
    134   // Consume pending message to populate the AddressMap, but don't notify.
    135   // Sending another request without first reading responses results in EBUSY.
    136   bool address_changed;
    137   bool link_changed;
    138   ReadMessages(&address_changed, &link_changed);
    139 
    140   // Request dump of link state
    141   request.header.nlmsg_type = RTM_GETLINK;
    142 
    143   rv = HANDLE_EINTR(sendto(netlink_fd_, &request, request.header.nlmsg_len, 0,
    144                            reinterpret_cast<struct sockaddr*>(&peer),
    145                            sizeof(peer)));
    146   if (rv < 0) {
    147     PLOG(ERROR) << "Could not send NETLINK request";
    148     AbortAndForceOnline();
    149     return;
    150   }
    151 
    152   // Consume pending message to populate links_online_, but don't notify.
    153   ReadMessages(&address_changed, &link_changed);
    154   {
    155     base::AutoLock lock(is_offline_lock_);
    156     is_offline_initialized_ = true;
    157     is_offline_initialized_cv_.Signal();
    158   }
    159 
    160   rv = base::MessageLoopForIO::current()->WatchFileDescriptor(
    161       netlink_fd_, true, base::MessageLoopForIO::WATCH_READ, &watcher_, this);
    162   if (rv < 0) {
    163     PLOG(ERROR) << "Could not watch NETLINK socket";
    164     AbortAndForceOnline();
    165     return;
    166   }
    167 }
    168 
    169 void AddressTrackerLinux::AbortAndForceOnline() {
    170   CloseSocket();
    171   base::AutoLock lock(is_offline_lock_);
    172   is_offline_ = false;
    173   is_offline_initialized_ = true;
    174   is_offline_initialized_cv_.Signal();
    175 }
    176 
    177 AddressTrackerLinux::AddressMap AddressTrackerLinux::GetAddressMap() const {
    178   base::AutoLock lock(address_map_lock_);
    179   return address_map_;
    180 }
    181 
    182 NetworkChangeNotifier::ConnectionType
    183 AddressTrackerLinux::GetCurrentConnectionType() {
    184   // http://crbug.com/125097
    185   base::ThreadRestrictions::ScopedAllowWait allow_wait;
    186   base::AutoLock lock(is_offline_lock_);
    187   // Make sure the initial offline state is set before returning.
    188   while (!is_offline_initialized_) {
    189     is_offline_initialized_cv_.Wait();
    190   }
    191   // TODO(droger): Return something more detailed than CONNECTION_UNKNOWN.
    192   // http://crbug.com/160537
    193   return is_offline_ ? NetworkChangeNotifier::CONNECTION_NONE :
    194                        NetworkChangeNotifier::CONNECTION_UNKNOWN;
    195 }
    196 
    197 void AddressTrackerLinux::ReadMessages(bool* address_changed,
    198                                        bool* link_changed) {
    199   *address_changed = false;
    200   *link_changed = false;
    201   char buffer[4096];
    202   bool first_loop = true;
    203   for (;;) {
    204     int rv = HANDLE_EINTR(recv(netlink_fd_,
    205                                buffer,
    206                                sizeof(buffer),
    207                                // Block the first time through loop.
    208                                first_loop ? 0 : MSG_DONTWAIT));
    209     first_loop = false;
    210     if (rv == 0) {
    211       LOG(ERROR) << "Unexpected shutdown of NETLINK socket.";
    212       return;
    213     }
    214     if (rv < 0) {
    215       if ((errno == EAGAIN) || (errno == EWOULDBLOCK))
    216         break;
    217       PLOG(ERROR) << "Failed to recv from netlink socket";
    218       return;
    219     }
    220     HandleMessage(buffer, rv, address_changed, link_changed);
    221   };
    222   if (*link_changed) {
    223     base::AutoLock lock(is_offline_lock_);
    224     is_offline_ = online_links_.empty();
    225   }
    226 }
    227 
    228 void AddressTrackerLinux::HandleMessage(const char* buffer,
    229                                         size_t length,
    230                                         bool* address_changed,
    231                                         bool* link_changed) {
    232   DCHECK(buffer);
    233   for (const struct nlmsghdr* header =
    234           reinterpret_cast<const struct nlmsghdr*>(buffer);
    235        NLMSG_OK(header, length);
    236        header = NLMSG_NEXT(header, length)) {
    237     switch (header->nlmsg_type) {
    238       case NLMSG_DONE:
    239         return;
    240       case NLMSG_ERROR: {
    241         const struct nlmsgerr* msg =
    242             reinterpret_cast<struct nlmsgerr*>(NLMSG_DATA(header));
    243         LOG(ERROR) << "Unexpected netlink error " << msg->error << ".";
    244       } return;
    245       case RTM_NEWADDR: {
    246         IPAddressNumber address;
    247         if (GetAddress(header, &address)) {
    248           base::AutoLock lock(address_map_lock_);
    249           const struct ifaddrmsg* msg =
    250               reinterpret_cast<struct ifaddrmsg*>(NLMSG_DATA(header));
    251           // Only indicate change if the address is new or ifaddrmsg info has
    252           // changed.
    253           AddressMap::iterator it = address_map_.find(address);
    254           if (it == address_map_.end()) {
    255             address_map_.insert(it, std::make_pair(address, *msg));
    256             *address_changed = true;
    257           } else if (memcmp(&it->second, msg, sizeof(*msg))) {
    258             it->second = *msg;
    259             *address_changed = true;
    260           }
    261         }
    262       } break;
    263       case RTM_DELADDR: {
    264         IPAddressNumber address;
    265         if (GetAddress(header, &address)) {
    266           base::AutoLock lock(address_map_lock_);
    267           if (address_map_.erase(address))
    268             *address_changed = true;
    269         }
    270       } break;
    271       case RTM_NEWLINK: {
    272         const struct ifinfomsg* msg =
    273             reinterpret_cast<struct ifinfomsg*>(NLMSG_DATA(header));
    274         if (!(msg->ifi_flags & IFF_LOOPBACK) && (msg->ifi_flags & IFF_UP) &&
    275             (msg->ifi_flags & IFF_LOWER_UP) && (msg->ifi_flags & IFF_RUNNING)) {
    276           if (online_links_.insert(msg->ifi_index).second)
    277             *link_changed = true;
    278         } else {
    279           if (online_links_.erase(msg->ifi_index))
    280             *link_changed = true;
    281         }
    282       } break;
    283       case RTM_DELLINK: {
    284         const struct ifinfomsg* msg =
    285             reinterpret_cast<struct ifinfomsg*>(NLMSG_DATA(header));
    286         if (online_links_.erase(msg->ifi_index))
    287           *link_changed = true;
    288       } break;
    289       default:
    290         break;
    291     }
    292   }
    293 }
    294 
    295 void AddressTrackerLinux::OnFileCanReadWithoutBlocking(int fd) {
    296   DCHECK_EQ(netlink_fd_, fd);
    297   bool address_changed;
    298   bool link_changed;
    299   ReadMessages(&address_changed, &link_changed);
    300   if (address_changed)
    301     address_callback_.Run();
    302   if (link_changed)
    303     link_callback_.Run();
    304 }
    305 
    306 void AddressTrackerLinux::OnFileCanWriteWithoutBlocking(int /* fd */) {}
    307 
    308 void AddressTrackerLinux::CloseSocket() {
    309   if (netlink_fd_ >= 0 && IGNORE_EINTR(close(netlink_fd_)) < 0)
    310     PLOG(ERROR) << "Could not close NETLINK socket.";
    311   netlink_fd_ = -1;
    312 }
    313 
    314 }  // namespace internal
    315 }  // namespace net
    316