Index: net/base/address_tracker_linux.cc |
diff --git a/net/base/address_tracker_linux.cc b/net/base/address_tracker_linux.cc |
deleted file mode 100644 |
index 731bf4f97785ebf45837c35a4fe4033bd4ffffff..0000000000000000000000000000000000000000 |
--- a/net/base/address_tracker_linux.cc |
+++ /dev/null |
@@ -1,448 +0,0 @@ |
-// Copyright (c) 2012 The Chromium Authors. All rights reserved. |
-// Use of this source code is governed by a BSD-style license that can be |
-// found in the LICENSE file. |
- |
-#include "net/base/address_tracker_linux.h" |
- |
-#include <errno.h> |
-#include <linux/if.h> |
-#include <sys/ioctl.h> |
- |
-#include "base/files/scoped_file.h" |
-#include "base/logging.h" |
-#include "base/posix/eintr_wrapper.h" |
-#include "base/threading/thread_restrictions.h" |
-#include "net/base/net_util_linux.h" |
- |
-namespace net { |
-namespace internal { |
- |
-namespace { |
- |
-// Retrieves address from NETLINK address message. |
-// Sets |really_deprecated| for IPv6 addresses with preferred lifetimes of 0. |
-bool GetAddress(const struct nlmsghdr* header, |
- IPAddressNumber* out, |
- bool* really_deprecated) { |
- if (really_deprecated) |
- *really_deprecated = false; |
- const struct ifaddrmsg* msg = |
- reinterpret_cast<struct ifaddrmsg*>(NLMSG_DATA(header)); |
- size_t address_length = 0; |
- switch (msg->ifa_family) { |
- case AF_INET: |
- address_length = kIPv4AddressSize; |
- break; |
- case AF_INET6: |
- address_length = kIPv6AddressSize; |
- break; |
- default: |
- // Unknown family. |
- return false; |
- } |
- // Use IFA_ADDRESS unless IFA_LOCAL is present. This behavior here is based on |
- // getaddrinfo in glibc (check_pf.c). Judging from kernel implementation of |
- // NETLINK, IPv4 addresses have only the IFA_ADDRESS attribute, while IPv6 |
- // have the IFA_LOCAL attribute. |
- unsigned char* address = NULL; |
- unsigned char* local = NULL; |
- size_t length = IFA_PAYLOAD(header); |
- for (const struct rtattr* attr = |
- reinterpret_cast<const struct rtattr*>(IFA_RTA(msg)); |
- RTA_OK(attr, length); |
- attr = RTA_NEXT(attr, length)) { |
- switch (attr->rta_type) { |
- case IFA_ADDRESS: |
- DCHECK_GE(RTA_PAYLOAD(attr), address_length); |
- address = reinterpret_cast<unsigned char*>(RTA_DATA(attr)); |
- break; |
- case IFA_LOCAL: |
- DCHECK_GE(RTA_PAYLOAD(attr), address_length); |
- local = reinterpret_cast<unsigned char*>(RTA_DATA(attr)); |
- break; |
- case IFA_CACHEINFO: { |
- const struct ifa_cacheinfo *cache_info = |
- reinterpret_cast<const struct ifa_cacheinfo*>(RTA_DATA(attr)); |
- if (really_deprecated) |
- *really_deprecated = (cache_info->ifa_prefered == 0); |
- } break; |
- default: |
- break; |
- } |
- } |
- if (local) |
- address = local; |
- if (!address) |
- return false; |
- out->assign(address, address + address_length); |
- return true; |
-} |
- |
-} // namespace |
- |
-// static |
-char* AddressTrackerLinux::GetInterfaceName(int interface_index, char* buf) { |
- memset(buf, 0, IFNAMSIZ); |
- base::ScopedFD ioctl_socket(socket(AF_INET, SOCK_DGRAM, 0)); |
- if (!ioctl_socket.is_valid()) |
- return buf; |
- |
- struct ifreq ifr = {}; |
- ifr.ifr_ifindex = interface_index; |
- |
- if (ioctl(ioctl_socket.get(), SIOCGIFNAME, &ifr) == 0) |
- strncpy(buf, ifr.ifr_name, IFNAMSIZ - 1); |
- return buf; |
-} |
- |
-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), |
- connection_type_initialized_(false), |
- connection_type_initialized_cv_(&connection_type_lock_), |
- current_connection_type_(NetworkChangeNotifier::CONNECTION_NONE), |
- tracking_(false) { |
-} |
- |
-AddressTrackerLinux::AddressTrackerLinux(const base::Closure& 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), |
- connection_type_initialized_(false), |
- connection_type_initialized_cv_(&connection_type_lock_), |
- current_connection_type_(NetworkChangeNotifier::CONNECTION_NONE), |
- tracking_(true) { |
- DCHECK(!address_callback.is_null()); |
- DCHECK(!link_callback.is_null()); |
-} |
- |
-AddressTrackerLinux::~AddressTrackerLinux() { |
- CloseSocket(); |
-} |
- |
-void AddressTrackerLinux::Init() { |
- netlink_fd_ = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE); |
- if (netlink_fd_ < 0) { |
- PLOG(ERROR) << "Could not create 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. |
- struct sockaddr_nl peer = {}; |
- peer.nl_family = AF_NETLINK; |
- |
- struct { |
- struct nlmsghdr header; |
- struct rtgenmsg msg; |
- } request = {}; |
- |
- request.header.nlmsg_len = NLMSG_LENGTH(sizeof(request.msg)); |
- request.header.nlmsg_type = RTM_GETADDR; |
- request.header.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP; |
- request.header.nlmsg_pid = getpid(); |
- request.msg.rtgen_family = AF_UNSPEC; |
- |
- rv = HANDLE_EINTR(sendto(netlink_fd_, &request, request.header.nlmsg_len, |
- 0, reinterpret_cast<struct sockaddr*>(&peer), |
- sizeof(peer))); |
- if (rv < 0) { |
- PLOG(ERROR) << "Could not send NETLINK request"; |
- AbortAndForceOnline(); |
- return; |
- } |
- |
- // Consume pending message to populate the AddressMap, but don't notify. |
- // Sending another request without first reading responses results in EBUSY. |
- bool address_changed; |
- bool link_changed; |
- bool tunnel_changed; |
- ReadMessages(&address_changed, &link_changed, &tunnel_changed); |
- |
- // Request dump of link state |
- request.header.nlmsg_type = RTM_GETLINK; |
- |
- rv = HANDLE_EINTR(sendto(netlink_fd_, &request, request.header.nlmsg_len, 0, |
- reinterpret_cast<struct sockaddr*>(&peer), |
- sizeof(peer))); |
- if (rv < 0) { |
- PLOG(ERROR) << "Could not send NETLINK request"; |
- AbortAndForceOnline(); |
- return; |
- } |
- |
- // Consume pending message to populate links_online_, but don't notify. |
- ReadMessages(&address_changed, &link_changed, &tunnel_changed); |
- { |
- AddressTrackerAutoLock lock(*this, connection_type_lock_); |
- connection_type_initialized_ = true; |
- connection_type_initialized_cv_.Signal(); |
- } |
- |
- 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(); |
- AddressTrackerAutoLock lock(*this, connection_type_lock_); |
- current_connection_type_ = NetworkChangeNotifier::CONNECTION_UNKNOWN; |
- connection_type_initialized_ = true; |
- connection_type_initialized_cv_.Signal(); |
-} |
- |
-AddressTrackerLinux::AddressMap AddressTrackerLinux::GetAddressMap() const { |
- 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; |
- AddressTrackerAutoLock lock(*this, connection_type_lock_); |
- // Make sure the initial connection type is set before returning. |
- while (!connection_type_initialized_) { |
- connection_type_initialized_cv_.Wait(); |
- } |
- return current_connection_type_; |
-} |
- |
-void AddressTrackerLinux::ReadMessages(bool* address_changed, |
- bool* link_changed, |
- bool* tunnel_changed) { |
- *address_changed = false; |
- *link_changed = false; |
- *tunnel_changed = false; |
- char buffer[4096]; |
- bool first_loop = true; |
- for (;;) { |
- int rv = HANDLE_EINTR(recv(netlink_fd_, |
- buffer, |
- sizeof(buffer), |
- // Block the first time through loop. |
- first_loop ? 0 : MSG_DONTWAIT)); |
- first_loop = false; |
- if (rv == 0) { |
- LOG(ERROR) << "Unexpected shutdown of NETLINK socket."; |
- return; |
- } |
- if (rv < 0) { |
- if ((errno == EAGAIN) || (errno == EWOULDBLOCK)) |
- break; |
- PLOG(ERROR) << "Failed to recv from netlink socket"; |
- return; |
- } |
- HandleMessage(buffer, rv, address_changed, link_changed, tunnel_changed); |
- } |
- if (*link_changed || *address_changed) |
- UpdateCurrentConnectionType(); |
-} |
- |
-void AddressTrackerLinux::HandleMessage(char* buffer, |
- size_t length, |
- bool* address_changed, |
- bool* link_changed, |
- bool* tunnel_changed) { |
- DCHECK(buffer); |
- for (struct nlmsghdr* header = reinterpret_cast<struct nlmsghdr*>(buffer); |
- NLMSG_OK(header, length); |
- header = NLMSG_NEXT(header, length)) { |
- switch (header->nlmsg_type) { |
- case NLMSG_DONE: |
- return; |
- case NLMSG_ERROR: { |
- const struct nlmsgerr* msg = |
- reinterpret_cast<struct nlmsgerr*>(NLMSG_DATA(header)); |
- LOG(ERROR) << "Unexpected netlink error " << msg->error << "."; |
- } return; |
- case RTM_NEWADDR: { |
- IPAddressNumber address; |
- bool really_deprecated; |
- if (GetAddress(header, &address, &really_deprecated)) { |
- 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 |
- // prefix which can cause the linux kernel to frequently output two |
- // back-to-back messages, one without the deprecated flag and one with |
- // the deprecated flag but both with preferred lifetimes of 0. Avoid |
- // interpretting this as an actual change by canonicalizing the two |
- // messages by setting the deprecated flag based on the preferred |
- // lifetime also. http://crbug.com/268042 |
- if (really_deprecated) |
- msg->ifa_flags |= IFA_F_DEPRECATED; |
- // Only indicate change if the address is new or ifaddrmsg info has |
- // changed. |
- AddressMap::iterator it = address_map_.find(address); |
- if (it == address_map_.end()) { |
- address_map_.insert(it, std::make_pair(address, *msg)); |
- *address_changed = true; |
- } else if (memcmp(&it->second, msg, sizeof(*msg))) { |
- it->second = *msg; |
- *address_changed = true; |
- } |
- } |
- } break; |
- case RTM_DELADDR: { |
- IPAddressNumber address; |
- if (GetAddress(header, &address, NULL)) { |
- AddressTrackerAutoLock lock(*this, address_map_lock_); |
- if (address_map_.erase(address)) |
- *address_changed = true; |
- } |
- } break; |
- case RTM_NEWLINK: { |
- const struct ifinfomsg* msg = |
- 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)) { |
- AddressTrackerAutoLock lock(*this, online_links_lock_); |
- if (online_links_.insert(msg->ifi_index).second) { |
- *link_changed = true; |
- if (IsTunnelInterface(msg->ifi_index)) |
- *tunnel_changed = true; |
- } |
- } else { |
- AddressTrackerAutoLock lock(*this, online_links_lock_); |
- if (online_links_.erase(msg->ifi_index)) { |
- *link_changed = true; |
- if (IsTunnelInterface(msg->ifi_index)) |
- *tunnel_changed = true; |
- } |
- } |
- } break; |
- case RTM_DELLINK: { |
- const struct ifinfomsg* msg = |
- reinterpret_cast<struct ifinfomsg*>(NLMSG_DATA(header)); |
- AddressTrackerAutoLock lock(*this, online_links_lock_); |
- if (online_links_.erase(msg->ifi_index)) { |
- *link_changed = true; |
- if (IsTunnelInterface(msg->ifi_index)) |
- *tunnel_changed = true; |
- } |
- } break; |
- default: |
- break; |
- } |
- } |
-} |
- |
-void AddressTrackerLinux::OnFileCanReadWithoutBlocking(int fd) { |
- DCHECK_EQ(netlink_fd_, fd); |
- bool address_changed; |
- bool 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 */) {} |
- |
-void AddressTrackerLinux::CloseSocket() { |
- if (netlink_fd_ >= 0 && IGNORE_EINTR(close(netlink_fd_)) < 0) |
- PLOG(ERROR) << "Could not close NETLINK socket."; |
- netlink_fd_ = -1; |
-} |
- |
-bool AddressTrackerLinux::IsTunnelInterface(int interface_index) const { |
- // Linux kernel drivers/net/tun.c uses "tun" name prefix. |
- char buf[IFNAMSIZ] = {0}; |
- return strncmp(get_interface_name_(interface_index, buf), "tun", 3) == 0; |
-} |
- |
-void AddressTrackerLinux::UpdateCurrentConnectionType() { |
- AddressTrackerLinux::AddressMap address_map = GetAddressMap(); |
- base::hash_set<int> online_links = GetOnlineLinks(); |
- |
- // Strip out tunnel interfaces from online_links |
- for (base::hash_set<int>::const_iterator it = online_links.begin(); |
- it != online_links.end();) { |
- if (IsTunnelInterface(*it)) { |
- base::hash_set<int>::const_iterator tunnel_it = it; |
- ++it; |
- online_links.erase(*tunnel_it); |
- } else { |
- ++it; |
- } |
- } |
- |
- NetworkInterfaceList networks; |
- NetworkChangeNotifier::ConnectionType type = |
- NetworkChangeNotifier::CONNECTION_NONE; |
- if (GetNetworkListImpl(&networks, 0, online_links, address_map, |
- get_interface_name_)) { |
- type = NetworkChangeNotifier::ConnectionTypeFromInterfaceList(networks); |
- } else { |
- type = online_links.empty() ? NetworkChangeNotifier::CONNECTION_NONE |
- : NetworkChangeNotifier::CONNECTION_UNKNOWN; |
- } |
- |
- AddressTrackerAutoLock lock(*this, connection_type_lock_); |
- current_connection_type_ = type; |
-} |
- |
-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 |