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

Unified Diff: net/base/address_tracker_linux.cc

Issue 10689015: [net] Adds AddressTrackerLinux which keeps track of interface addresses using rtnetlink. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Add virtual to dtor. Created 8 years, 6 months 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
new file mode 100644
index 0000000000000000000000000000000000000000..b50aba472a33718bf40251e11d876e37afefad87
--- /dev/null
+++ b/net/base/address_tracker_linux.cc
@@ -0,0 +1,211 @@
+// 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 "base/eintr_wrapper.h"
+#include "base/logging.h"
+#include "net/base/network_change_notifier_linux.h"
+
+namespace net {
+namespace internal {
+
+namespace {
+
+// Retrieves address from NETLINK address message.
+bool GetAddress(const struct nlmsghdr* header, IPAddressNumber* out) {
+ 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;
+ }
+ size_t length = IFA_PAYLOAD(header);
+ const struct rtattr* attr = reinterpret_cast<struct rtattr*>(IFA_RTA(msg));
+ unsigned char *address = NULL;
willchan no longer on Chromium 2012/06/29 01:53:41 be consistent about type* vs type *. chromium pref
+ unsigned char *local = NULL;
+ for (; RTA_OK(attr, length); attr = RTA_NEXT(attr, length)) {
+ switch (attr->rta_type) {
+ case IFA_ADDRESS:
+ local = reinterpret_cast<unsigned char *>(RTA_DATA(attr));
+ break;
+ case IFA_LOCAL:
+ address = reinterpret_cast<unsigned char *>(RTA_DATA(attr));
+ break;
+ default:
+ break;
+ }
+ }
+ if (local)
+ address = local;
+ if (!address)
+ return false;
+ out->assign(address, address + address_length);
+ return true;
+}
+
+} // namespace
+
+AddressTrackerLinux::AddressTrackerLinux() : netlink_fd_(-1) {}
+
+AddressTrackerLinux::~AddressTrackerLinux() {
+ if (netlink_fd_ >= 0) {
+ watcher_.StopWatchingFileDescriptor();
+ close(netlink_fd_);
willchan no longer on Chromium 2012/06/29 01:53:41 HANDLE_EINTR()
+ }
+}
+
+void AddressTrackerLinux::Init() {
+ int sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
+ if (sock < 0) {
+ PLOG(ERROR) << "Could not create NETLINK socket";
+ 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
+ addr.nl_groups = RTMGRP_IPV4_IFADDR | RTMGRP_IPV6_IFADDR | RTMGRP_NOTIFY;
+ int rv = bind(sock, reinterpret_cast<struct sockaddr*>(&addr), sizeof(addr));
+ if (rv < 0) {
+ PLOG(ERROR) << "Could not bind NETLINK socket";
+ close(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";
+ close(sock);
+ return;
+ }
+
+ // Watch for asynchronous messages.
+ if (SetNonBlocking(sock)) {
+ PLOG(ERROR) << "Could not make NETLINK socket non-blocking";
+ close(sock);
+ return;
+ }
+
+ rv = MessageLoopForIO::current()->WatchFileDescriptor(
+ sock, true, MessageLoopForIO::WATCH_READ, &watcher_, this);
+ if (rv < 0) {
+ PLOG(ERROR) << "Could not watch NETLINK socket";
+ close(sock);
+ return;
+ }
+
+ netlink_fd_ = sock;
+ ReadMessages();
+}
+
+AddressTrackerLinux::AddressMap AddressTrackerLinux::GetMap() const {
+ base::AutoLock lock(lock_);
+ return map_;
+}
+
+bool AddressTrackerLinux::ReadMessages() {
+ char buf[4096];
+ bool changed = false;
+ for (;;) {
+ int rv = HANDLE_EINTR(recv(netlink_fd_, buf, sizeof(buf), 0));
+ if (rv == 0) {
+ LOG(ERROR) << "Unexpected shutdown of NETLINK socket.";
+ return false;
+ }
+ if (rv < 0) {
+ if ((errno == EAGAIN) || (errno == EWOULDBLOCK))
+ break;
+ PLOG(ERROR) << "Failed to recv from netlink socket";
+ return false;
+ }
+ changed |= HandleMessage(buf, rv);
+ };
+ return changed;
+}
+
+bool AddressTrackerLinux::HandleMessage(char* buf, size_t len) {
+ DCHECK(buf);
+ bool changed = false;
+ const struct nlmsghdr* header = reinterpret_cast<struct nlmsghdr*>(buf);
+ for (; NLMSG_OK(header, len); header = NLMSG_NEXT(header, len)) {
+ switch (header->nlmsg_type) {
+ case NLMSG_DONE:
+ return changed;
+ case NLMSG_ERROR:
+ LOG(ERROR) << "Unexpected netlink error.";
+ return changed;
+ case RTM_NEWADDR: {
+ IPAddressNumber address;
+ if (GetAddress(header, &address)) {
+ base::AutoLock lock(lock_);
+ const struct ifaddrmsg* msg =
+ reinterpret_cast<struct ifaddrmsg*>(NLMSG_DATA(header));
+ AddressMap::iterator it = map_.find(address);
+ if (it == map_.end()) {
+ map_.insert(it, std::make_pair(address, *msg));
+ changed = true;
+ } else if (memcmp(&it->second, msg, sizeof(*msg))) {
+ it->second = *msg;
+ changed = true;
+ }
+ }
+ } break;
+ case RTM_DELADDR: {
+ IPAddressNumber address;
+ if (GetAddress(header, &address)) {
+ base::AutoLock lock(lock_);
+ if (map_.erase(address))
+ changed = true;
+ }
+ } break;
+ case RTM_NEWLINK:
+ case RTM_DELLINK:
+ // TODO(szym): handle for ifi_type.
+ break;
+ default:
+ break;
+ }
+ }
+ return changed;
+}
+
+void AddressTrackerLinux::OnFileCanReadWithoutBlocking(int fd) {
+ if (ReadMessages())
+ NetworkChangeNotifierLinux::NotifyObserversOfIPAddressChange();
+}
+
+void AddressTrackerLinux::OnFileCanWriteWithoutBlocking(int /* fd */) {}
+
+} // namespace internal
+} // namespace net

Powered by Google App Engine
This is Rietveld 408576698