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

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: 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
Index: net/base/address_tracker_linux.cc
diff --git a/net/base/address_tracker_linux.cc b/net/base/address_tracker_linux.cc
index af2a88dafc6da6c15a3deab72a5c7bb3ac49e71a..1c8e5e08a3a701e29bc984167e44fe833d01e8f4 100644
--- a/net/base/address_tracker_linux.cc
+++ b/net/base/address_tracker_linux.cc
@@ -4,10 +4,13 @@
#include "net/base/address_tracker_linux.h"
+#include <linux/if.h>
+
#include <errno.h>
#include "base/eintr_wrapper.h"
#include "base/logging.h"
+#include "base/threading/thread_restrictions.h"
#include "net/base/network_change_notifier_linux.h"
namespace net {
@@ -70,10 +73,15 @@ void CloseSocket(int fd) {
} // 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_(false),
+ offline_state_initialized_(true /*manual_reset*/, false) {
+ DCHECK(!address_callback.is_null());
+ DCHECK(!link_callback.is_null());
}
AddressTrackerLinux::~AddressTrackerLinux() {
@@ -81,6 +89,55 @@ AddressTrackerLinux::~AddressTrackerLinux() {
CloseSocket(netlink_fd_);
}
+void AddressTrackerLinux::GetInitialSettings() {
+ // 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));
+ 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;
+
+ int 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(netlink_fd_);
+ netlink_fd_ = -1;
+ 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(netlink_fd_);
+ netlink_fd_ = -1;
+ return;
+ }
+
+ // Consume pending message to populate links_online_, but don't notify.
+ ReadMessages(&address_changed, &link_changed);
+}
+
void AddressTrackerLinux::Init() {
int sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
if (sock < 0) {
@@ -93,134 +150,152 @@ 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;
+ addr.nl_groups = RTMGRP_IPV4_IFADDR | RTMGRP_IPV6_IFADDR | RTMGRP_NOTIFY |
+ RTMGRP_LINK;
int rv = bind(sock, reinterpret_cast<struct sockaddr*>(&addr), sizeof(addr));
if (rv < 0) {
PLOG(ERROR) << "Could not bind NETLINK socket";
CloseSocket(sock);
return;
}
+ netlink_fd_ = sock;
- // Watch for asynchronous messages.
- if (SetNonBlocking(sock)) {
- PLOG(ERROR) << "Could not make NETLINK socket non-blocking";
- CloseSocket(sock);
- return;
- }
+ GetInitialSettings();
+ offline_state_initialized_.Signal();
rv = MessageLoopForIO::current()->WatchFileDescriptor(
- sock, true, MessageLoopForIO::WATCH_READ, &watcher_, this);
+ netlink_fd_, true, MessageLoopForIO::WATCH_READ, &watcher_, this);
if (rv < 0) {
PLOG(ERROR) << "Could not watch NETLINK socket";
- CloseSocket(sock);
- 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(sock, &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);
+ CloseSocket(netlink_fd_);
+ netlink_fd_ = -1;
szym 2012/11/11 16:21:55 Since you need to do this at least once, I suggest
pauljensen 2012/11/12 16:45:21 Done.
return;
}
-
- netlink_fd_ = sock;
-
- // Consume any pending messages to populate the AddressMap, but don't notify.
- ReadMessages();
}
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
szym 2012/11/11 16:21:55 Is it still needed? Can't we return offline until
pauljensen 2012/11/12 16:45:21 I'm hesitant to return offline when uninitialized;
+ base::ThreadRestrictions::ScopedAllowWait allow_wait;
+ offline_state_initialized_.Wait();
szym 2012/11/11 16:21:55 I'm concerned that you Wait after locking. I think
pauljensen 2012/11/12 16:45:21 I'm confused; your two sentences contradict each o
+ base::AutoLock lock(is_offline_lock_);
+ // TODO(droger): Return something more detailed than CONNECTION_UNKNOWN.
szym 2012/11/11 16:21:55 Could you find the bug for droger and link it here
pauljensen 2012/11/12 16:45:21 I don't think there is one. I filed one.
+ 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.
szym 2012/11/11 16:21:55 If we are ok with returning offline initially, you
+ 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 */) {}

Powered by Google App Engine
This is Rietveld 408576698