Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1011)

Unified Diff: net/base/address_tracker_linux.cc

Issue 11359141: Use Netlink instead of NetworkManager (via D-bus) to monitor network (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Address szym's second round of unittest comments Created 8 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « net/base/address_tracker_linux.h ('k') | net/base/address_tracker_linux_unittest.cc » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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
« no previous file with comments | « net/base/address_tracker_linux.h ('k') | net/base/address_tracker_linux_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698