Upstream version 10.39.225.0
[platform/framework/web/crosswalk.git] / src / net / base / address_tracker_linux.cc
index 37911de..27dd201 100644 (file)
@@ -6,11 +6,11 @@
 
 #include <errno.h>
 #include <linux/if.h>
+#include <sys/ioctl.h>
 
 #include "base/logging.h"
 #include "base/posix/eintr_wrapper.h"
 #include "base/threading/thread_restrictions.h"
-#include "net/base/network_change_notifier_linux.h"
 
 namespace net {
 namespace internal {
@@ -76,16 +76,51 @@ bool GetAddress(const struct nlmsghdr* header,
   return true;
 }
 
+// Returns the name for the interface with interface index |interface_index|.
+// The return value points to a function-scoped static so it may be changed by
+// subsequent calls. This function could be replaced with if_indextoname() but
+// net/if.h cannot be mixed with linux/if.h so we'll stick with exclusively
+// talking to the kernel and not the C library.
+const char* GetInterfaceName(int interface_index) {
+  int ioctl_socket = socket(AF_INET, SOCK_DGRAM, 0);
+  if (ioctl_socket < 0)
+    return "";
+  static struct ifreq ifr;
+  memset(&ifr, 0, sizeof(ifr));
+  ifr.ifr_ifindex = interface_index;
+  int rv = ioctl(ioctl_socket, SIOCGIFNAME, &ifr);
+  close(ioctl_socket);
+  if (rv != 0)
+    return "";
+  return ifr.ifr_name;
+}
+
 }  // namespace
 
+AddressTrackerLinux::AddressTrackerLinux()
+    : get_interface_name_(GetInterfaceName),
+      address_callback_(base::Bind(&base::DoNothing)),
+      link_callback_(base::Bind(&base::DoNothing)),
+      tunnel_callback_(base::Bind(&base::DoNothing)),
+      netlink_fd_(-1),
+      is_offline_(true),
+      is_offline_initialized_(false),
+      is_offline_initialized_cv_(&is_offline_lock_),
+      tracking_(false) {
+}
+
 AddressTrackerLinux::AddressTrackerLinux(const base::Closure& address_callback,
-                                         const base::Closure& link_callback)
-    : address_callback_(address_callback),
+                                         const base::Closure& link_callback,
+                                         const base::Closure& tunnel_callback)
+    : get_interface_name_(GetInterfaceName),
+      address_callback_(address_callback),
       link_callback_(link_callback),
+      tunnel_callback_(tunnel_callback),
       netlink_fd_(-1),
       is_offline_(true),
       is_offline_initialized_(false),
-      is_offline_initialized_cv_(&is_offline_lock_) {
+      is_offline_initialized_cv_(&is_offline_lock_),
+      tracking_(true) {
   DCHECK(!address_callback.is_null());
   DCHECK(!link_callback.is_null());
 }
@@ -102,20 +137,24 @@ void AddressTrackerLinux::Init() {
     return;
   }
 
-  // Request notifications.
-  struct sockaddr_nl addr = {};
-  addr.nl_family = AF_NETLINK;
-  addr.nl_pid = getpid();
-  // TODO(szym): Track RTMGRP_LINK as well for ifi_type, http://crbug.com/113993
-  addr.nl_groups = RTMGRP_IPV4_IFADDR | RTMGRP_IPV6_IFADDR | RTMGRP_NOTIFY |
-      RTMGRP_LINK;
-  int rv = bind(netlink_fd_,
-                reinterpret_cast<struct sockaddr*>(&addr),
-                sizeof(addr));
-  if (rv < 0) {
-    PLOG(ERROR) << "Could not bind NETLINK socket";
-    AbortAndForceOnline();
-    return;
+  int rv;
+
+  if (tracking_) {
+    // Request notifications.
+    struct sockaddr_nl addr = {};
+    addr.nl_family = AF_NETLINK;
+    addr.nl_pid = getpid();
+    // TODO(szym): Track RTMGRP_LINK as well for ifi_type,
+    // http://crbug.com/113993
+    addr.nl_groups =
+        RTMGRP_IPV4_IFADDR | RTMGRP_IPV6_IFADDR | RTMGRP_NOTIFY | RTMGRP_LINK;
+    rv = bind(
+        netlink_fd_, reinterpret_cast<struct sockaddr*>(&addr), sizeof(addr));
+    if (rv < 0) {
+      PLOG(ERROR) << "Could not bind NETLINK socket";
+      AbortAndForceOnline();
+      return;
+    }
   }
 
   // Request dump of addresses.
@@ -146,7 +185,8 @@ void AddressTrackerLinux::Init() {
   // Sending another request without first reading responses results in EBUSY.
   bool address_changed;
   bool link_changed;
-  ReadMessages(&address_changed, &link_changed);
+  bool tunnel_changed;
+  ReadMessages(&address_changed, &link_changed, &tunnel_changed);
 
   // Request dump of link state
   request.header.nlmsg_type = RTM_GETLINK;
@@ -161,40 +201,47 @@ void AddressTrackerLinux::Init() {
   }
 
   // Consume pending message to populate links_online_, but don't notify.
-  ReadMessages(&address_changed, &link_changed);
+  ReadMessages(&address_changed, &link_changed, &tunnel_changed);
   {
-    base::AutoLock lock(is_offline_lock_);
+    AddressTrackerAutoLock lock(*this, is_offline_lock_);
     is_offline_initialized_ = true;
     is_offline_initialized_cv_.Signal();
   }
 
-  rv = base::MessageLoopForIO::current()->WatchFileDescriptor(
-      netlink_fd_, true, base::MessageLoopForIO::WATCH_READ, &watcher_, this);
-  if (rv < 0) {
-    PLOG(ERROR) << "Could not watch NETLINK socket";
-    AbortAndForceOnline();
-    return;
+  if (tracking_) {
+    rv = base::MessageLoopForIO::current()->WatchFileDescriptor(
+        netlink_fd_, true, base::MessageLoopForIO::WATCH_READ, &watcher_, this);
+    if (rv < 0) {
+      PLOG(ERROR) << "Could not watch NETLINK socket";
+      AbortAndForceOnline();
+      return;
+    }
   }
 }
 
 void AddressTrackerLinux::AbortAndForceOnline() {
   CloseSocket();
-  base::AutoLock lock(is_offline_lock_);
+  AddressTrackerAutoLock lock(*this, is_offline_lock_);
   is_offline_ = false;
   is_offline_initialized_ = true;
   is_offline_initialized_cv_.Signal();
 }
 
 AddressTrackerLinux::AddressMap AddressTrackerLinux::GetAddressMap() const {
-  base::AutoLock lock(address_map_lock_);
+  AddressTrackerAutoLock lock(*this, address_map_lock_);
   return address_map_;
 }
 
+base::hash_set<int> AddressTrackerLinux::GetOnlineLinks() const {
+  AddressTrackerAutoLock lock(*this, online_links_lock_);
+  return online_links_;
+}
+
 NetworkChangeNotifier::ConnectionType
 AddressTrackerLinux::GetCurrentConnectionType() {
   // http://crbug.com/125097
   base::ThreadRestrictions::ScopedAllowWait allow_wait;
-  base::AutoLock lock(is_offline_lock_);
+  AddressTrackerAutoLock lock(*this, is_offline_lock_);
   // Make sure the initial offline state is set before returning.
   while (!is_offline_initialized_) {
     is_offline_initialized_cv_.Wait();
@@ -206,9 +253,11 @@ AddressTrackerLinux::GetCurrentConnectionType() {
 }
 
 void AddressTrackerLinux::ReadMessages(bool* address_changed,
-                                       bool* link_changed) {
+                                       bool* link_changed,
+                                       bool* tunnel_changed) {
   *address_changed = false;
   *link_changed = false;
+  *tunnel_changed = false;
   char buffer[4096];
   bool first_loop = true;
   for (;;) {
@@ -228,18 +277,24 @@ void AddressTrackerLinux::ReadMessages(bool* address_changed,
       PLOG(ERROR) << "Failed to recv from netlink socket";
       return;
     }
-    HandleMessage(buffer, rv, address_changed, link_changed);
-  };
+    HandleMessage(buffer, rv, address_changed, link_changed, tunnel_changed);
+  }
   if (*link_changed) {
-    base::AutoLock lock(is_offline_lock_);
-    is_offline_ = online_links_.empty();
+    bool is_offline;
+    {
+      AddressTrackerAutoLock lock(*this, online_links_lock_);
+      is_offline = online_links_.empty();
+    }
+    AddressTrackerAutoLock lock(*this, is_offline_lock_);
+    is_offline_ = is_offline;
   }
 }
 
 void AddressTrackerLinux::HandleMessage(char* buffer,
                                         size_t length,
                                         bool* address_changed,
-                                        bool* link_changed) {
+                                        bool* link_changed,
+                                        bool* tunnel_changed) {
   DCHECK(buffer);
   for (struct nlmsghdr* header = reinterpret_cast<struct nlmsghdr*>(buffer);
        NLMSG_OK(header, length);
@@ -256,7 +311,7 @@ void AddressTrackerLinux::HandleMessage(char* buffer,
         IPAddressNumber address;
         bool really_deprecated;
         if (GetAddress(header, &address, &really_deprecated)) {
-          base::AutoLock lock(address_map_lock_);
+          AddressTrackerAutoLock lock(*this, address_map_lock_);
           struct ifaddrmsg* msg =
               reinterpret_cast<struct ifaddrmsg*>(NLMSG_DATA(header));
           // Routers may frequently (every few seconds) output the IPv6 ULA
@@ -283,7 +338,7 @@ void AddressTrackerLinux::HandleMessage(char* buffer,
       case RTM_DELADDR: {
         IPAddressNumber address;
         if (GetAddress(header, &address, NULL)) {
-          base::AutoLock lock(address_map_lock_);
+          AddressTrackerAutoLock lock(*this, address_map_lock_);
           if (address_map_.erase(address))
             *address_changed = true;
         }
@@ -293,18 +348,30 @@ void AddressTrackerLinux::HandleMessage(char* buffer,
             reinterpret_cast<struct ifinfomsg*>(NLMSG_DATA(header));
         if (!(msg->ifi_flags & IFF_LOOPBACK) && (msg->ifi_flags & IFF_UP) &&
             (msg->ifi_flags & IFF_LOWER_UP) && (msg->ifi_flags & IFF_RUNNING)) {
-          if (online_links_.insert(msg->ifi_index).second)
+          AddressTrackerAutoLock lock(*this, online_links_lock_);
+          if (online_links_.insert(msg->ifi_index).second) {
             *link_changed = true;
+            if (IsTunnelInterface(msg))
+              *tunnel_changed = true;
+          }
         } else {
-          if (online_links_.erase(msg->ifi_index))
+          AddressTrackerAutoLock lock(*this, online_links_lock_);
+          if (online_links_.erase(msg->ifi_index)) {
             *link_changed = true;
+            if (IsTunnelInterface(msg))
+              *tunnel_changed = true;
+          }
         }
       } break;
       case RTM_DELLINK: {
         const struct ifinfomsg* msg =
             reinterpret_cast<struct ifinfomsg*>(NLMSG_DATA(header));
-        if (online_links_.erase(msg->ifi_index))
+        AddressTrackerAutoLock lock(*this, online_links_lock_);
+        if (online_links_.erase(msg->ifi_index)) {
           *link_changed = true;
+          if (IsTunnelInterface(msg))
+            *tunnel_changed = true;
+        }
       } break;
       default:
         break;
@@ -316,11 +383,14 @@ void AddressTrackerLinux::OnFileCanReadWithoutBlocking(int fd) {
   DCHECK_EQ(netlink_fd_, fd);
   bool address_changed;
   bool link_changed;
-  ReadMessages(&address_changed, &link_changed);
+  bool tunnel_changed;
+  ReadMessages(&address_changed, &link_changed, &tunnel_changed);
   if (address_changed)
     address_callback_.Run();
   if (link_changed)
     link_callback_.Run();
+  if (tunnel_changed)
+    tunnel_callback_.Run();
 }
 
 void AddressTrackerLinux::OnFileCanWriteWithoutBlocking(int /* fd */) {}
@@ -331,5 +401,28 @@ void AddressTrackerLinux::CloseSocket() {
   netlink_fd_ = -1;
 }
 
+bool AddressTrackerLinux::IsTunnelInterface(const struct ifinfomsg* msg) const {
+  // Linux kernel drivers/net/tun.c uses "tun" name prefix.
+  return strncmp(get_interface_name_(msg->ifi_index), "tun", 3) == 0;
+}
+
+AddressTrackerLinux::AddressTrackerAutoLock::AddressTrackerAutoLock(
+    const AddressTrackerLinux& tracker,
+    base::Lock& lock)
+    : tracker_(tracker), lock_(lock) {
+  if (tracker_.tracking_) {
+    lock_.Acquire();
+  } else {
+    DCHECK(tracker_.thread_checker_.CalledOnValidThread());
+  }
+}
+
+AddressTrackerLinux::AddressTrackerAutoLock::~AddressTrackerAutoLock() {
+  if (tracker_.tracking_) {
+    lock_.AssertAcquired();
+    lock_.Release();
+  }
+}
+
 }  // namespace internal
 }  // namespace net