| Index: net/base/address_tracker_linux.cc
|
| diff --git a/net/base/address_tracker_linux.cc b/net/base/address_tracker_linux.cc
|
| index 52bc928122202a0e901bde531476716699f3ace0..67eaa69a50b4a17a3fbad74d94c09d925ce71a5a 100644
|
| --- a/net/base/address_tracker_linux.cc
|
| +++ b/net/base/address_tracker_linux.cc
|
| @@ -5,9 +5,11 @@
|
| #include "net/base/address_tracker_linux.h"
|
|
|
| #include <errno.h>
|
| +#include <linux/if.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 {
|
| @@ -63,28 +65,29 @@ bool GetAddress(const struct nlmsghdr* header, IPAddressNumber* out) {
|
| return true;
|
| }
|
|
|
| -void CloseSocket(int fd) {
|
| - if (HANDLE_EINTR(close(fd)) < 0)
|
| - PLOG(ERROR) << "Could not close NETLINK socket.";
|
| -}
|
| -
|
| } // namespace
|
|
|
| -AddressTrackerLinux::AddressTrackerLinux(const base::Closure& callback)
|
| - : callback_(callback),
|
| - netlink_fd_(-1) {
|
| - DCHECK(!callback.is_null());
|
| +AddressTrackerLinux::AddressTrackerLinux(const base::Closure& address_callback,
|
| + const base::Closure& link_callback)
|
| + : address_callback_(address_callback),
|
| + link_callback_(link_callback),
|
| + netlink_fd_(-1),
|
| + is_offline_(true),
|
| + is_offline_initialized_(false),
|
| + is_offline_initialized_cv_(&is_offline_lock_) {
|
| + DCHECK(!address_callback.is_null());
|
| + DCHECK(!link_callback.is_null());
|
| }
|
|
|
| AddressTrackerLinux::~AddressTrackerLinux() {
|
| - if (netlink_fd_ >= 0)
|
| - CloseSocket(netlink_fd_);
|
| + CloseSocket();
|
| }
|
|
|
| void AddressTrackerLinux::Init() {
|
| - int sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
|
| - if (sock < 0) {
|
| + netlink_fd_ = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
|
| + if (netlink_fd_ < 0) {
|
| PLOG(ERROR) << "Could not create NETLINK socket";
|
| + AbortAndForceOnline();
|
| return;
|
| }
|
|
|
| @@ -93,26 +96,14 @@ void AddressTrackerLinux::Init() {
|
| 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;
|
| - int rv = bind(sock, reinterpret_cast<struct sockaddr*>(&addr), sizeof(addr));
|
| + 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";
|
| - CloseSocket(sock);
|
| - return;
|
| - }
|
| -
|
| - // Watch for asynchronous messages.
|
| - if (SetNonBlocking(sock)) {
|
| - PLOG(ERROR) << "Could not make NETLINK socket non-blocking";
|
| - CloseSocket(sock);
|
| - return;
|
| - }
|
| -
|
| - rv = MessageLoopForIO::current()->WatchFileDescriptor(
|
| - sock, true, MessageLoopForIO::WATCH_READ, &watcher_, this);
|
| - if (rv < 0) {
|
| - PLOG(ERROR) << "Could not watch NETLINK socket";
|
| - CloseSocket(sock);
|
| + AbortAndForceOnline();
|
| return;
|
| }
|
|
|
| @@ -125,105 +116,200 @@ void AddressTrackerLinux::Init() {
|
| struct rtgenmsg msg;
|
| } request = {};
|
|
|
| - request.header.nlmsg_len = NLMSG_LENGTH(sizeof(request.msg));
|
| + request.header.nlmsg_len = NLMSG_LENGTH(sizeof(request));
|
| 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(sock, &request, request.header.nlmsg_len, 0,
|
| + 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;
|
| + ReadMessages(&address_changed, &link_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";
|
| - CloseSocket(sock);
|
| + AbortAndForceOnline();
|
| return;
|
| }
|
|
|
| - netlink_fd_ = sock;
|
| + // Consume pending message to populate links_online_, but don't notify.
|
| + ReadMessages(&address_changed, &link_changed);
|
| + {
|
| + base::AutoLock lock(is_offline_lock_);
|
| + is_offline_initialized_ = true;
|
| + is_offline_initialized_cv_.Signal();
|
| + }
|
| +
|
| + rv = MessageLoopForIO::current()->WatchFileDescriptor(
|
| + netlink_fd_, true, MessageLoopForIO::WATCH_READ, &watcher_, this);
|
| + if (rv < 0) {
|
| + PLOG(ERROR) << "Could not watch NETLINK socket";
|
| + AbortAndForceOnline();
|
| + return;
|
| + }
|
| +}
|
|
|
| - // Consume any pending messages to populate the AddressMap, but don't notify.
|
| - ReadMessages();
|
| +void AddressTrackerLinux::AbortAndForceOnline() {
|
| + CloseSocket();
|
| + base::AutoLock lock(is_offline_lock_);
|
| + is_offline_ = false;
|
| + is_offline_initialized_ = true;
|
| + is_offline_initialized_cv_.Signal();
|
| }
|
|
|
| AddressTrackerLinux::AddressMap AddressTrackerLinux::GetAddressMap() const {
|
| - base::AutoLock lock(lock_);
|
| - return map_;
|
| + base::AutoLock lock(address_map_lock_);
|
| + return address_map_;
|
| +}
|
| +
|
| +NetworkChangeNotifier::ConnectionType
|
| +AddressTrackerLinux::GetCurrentConnectionType() {
|
| + // http://crbug.com/125097
|
| + base::ThreadRestrictions::ScopedAllowWait allow_wait;
|
| + base::AutoLock lock(is_offline_lock_);
|
| + // Make sure the initial offline state is set before returning.
|
| + while (!is_offline_initialized_) {
|
| + is_offline_initialized_cv_.Wait();
|
| + }
|
| + // TODO(droger): Return something more detailed than CONNECTION_UNKNOWN.
|
| + // http://crbug.com/160537
|
| + return is_offline_ ? NetworkChangeNotifier::CONNECTION_NONE :
|
| + NetworkChangeNotifier::CONNECTION_UNKNOWN;
|
| }
|
|
|
| -bool AddressTrackerLinux::ReadMessages() {
|
| +void AddressTrackerLinux::ReadMessages(bool* address_changed,
|
| + bool* link_changed) {
|
| + *address_changed = false;
|
| + *link_changed = false;
|
| char buffer[4096];
|
| - bool changed = false;
|
| + bool first_loop = true;
|
| for (;;) {
|
| - int rv = HANDLE_EINTR(recv(netlink_fd_, buffer, sizeof(buffer), 0));
|
| + 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 false;
|
| + return;
|
| }
|
| if (rv < 0) {
|
| if ((errno == EAGAIN) || (errno == EWOULDBLOCK))
|
| break;
|
| PLOG(ERROR) << "Failed to recv from netlink socket";
|
| - return false;
|
| + return;
|
| }
|
| - changed |= HandleMessage(buffer, rv);
|
| + HandleMessage(buffer, rv, address_changed, link_changed);
|
| };
|
| - return changed;
|
| + if (*link_changed) {
|
| + base::AutoLock lock(is_offline_lock_);
|
| + is_offline_ = online_links_.empty();
|
| + }
|
| }
|
|
|
| -bool AddressTrackerLinux::HandleMessage(const char* buffer, size_t length) {
|
| +void AddressTrackerLinux::HandleMessage(const char* buffer,
|
| + size_t length,
|
| + bool* address_changed,
|
| + bool* link_changed) {
|
| DCHECK(buffer);
|
| - bool changed = false;
|
| for (const struct nlmsghdr* header =
|
| reinterpret_cast<const struct nlmsghdr*>(buffer);
|
| NLMSG_OK(header, length);
|
| header = NLMSG_NEXT(header, length)) {
|
| switch (header->nlmsg_type) {
|
| case NLMSG_DONE:
|
| - return changed;
|
| - case NLMSG_ERROR:
|
| - LOG(ERROR) << "Unexpected netlink error.";
|
| - return changed;
|
| + 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;
|
| if (GetAddress(header, &address)) {
|
| - base::AutoLock lock(lock_);
|
| + base::AutoLock lock(address_map_lock_);
|
| const struct ifaddrmsg* msg =
|
| reinterpret_cast<struct ifaddrmsg*>(NLMSG_DATA(header));
|
| // Only indicate change if the address is new or ifaddrmsg info has
|
| // changed.
|
| - AddressMap::iterator it = map_.find(address);
|
| - if (it == map_.end()) {
|
| - map_.insert(it, std::make_pair(address, *msg));
|
| - changed = true;
|
| + 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;
|
| - changed = true;
|
| + *address_changed = true;
|
| }
|
| }
|
| } break;
|
| case RTM_DELADDR: {
|
| IPAddressNumber address;
|
| if (GetAddress(header, &address)) {
|
| - base::AutoLock lock(lock_);
|
| - if (map_.erase(address))
|
| - changed = true;
|
| + base::AutoLock lock(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)) {
|
| + if (online_links_.insert(msg->ifi_index).second)
|
| + *link_changed = true;
|
| + } else {
|
| + if (online_links_.erase(msg->ifi_index))
|
| + *link_changed = true;
|
| }
|
| } break;
|
| + case RTM_DELLINK: {
|
| + const struct ifinfomsg* msg =
|
| + reinterpret_cast<struct ifinfomsg*>(NLMSG_DATA(header));
|
| + if (online_links_.erase(msg->ifi_index))
|
| + *link_changed = true;
|
| + } break;
|
| default:
|
| break;
|
| }
|
| }
|
| - return changed;
|
| }
|
|
|
| void AddressTrackerLinux::OnFileCanReadWithoutBlocking(int fd) {
|
| DCHECK_EQ(netlink_fd_, fd);
|
| - if (ReadMessages())
|
| - callback_.Run();
|
| + bool address_changed;
|
| + bool link_changed;
|
| + ReadMessages(&address_changed, &link_changed);
|
| + if (address_changed)
|
| + address_callback_.Run();
|
| + if (link_changed)
|
| + link_callback_.Run();
|
| }
|
|
|
| void AddressTrackerLinux::OnFileCanWriteWithoutBlocking(int /* fd */) {}
|
|
|
| +void AddressTrackerLinux::CloseSocket() {
|
| + if (netlink_fd_ >= 0 && HANDLE_EINTR(close(netlink_fd_)) < 0)
|
| + PLOG(ERROR) << "Could not close NETLINK socket.";
|
| + netlink_fd_ = -1;
|
| +}
|
| +
|
| } // namespace internal
|
| } // namespace net
|
|
|