summaryrefslogtreecommitdiffstats
path: root/net/base
diff options
context:
space:
mode:
authorszym@chromium.org <szym@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-07-16 22:27:24 +0000
committerszym@chromium.org <szym@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-07-16 22:27:24 +0000
commit671728091acbcbfa1ec4e2cffdaff112e43949a0 (patch)
tree44803eacadbf212e5def8ff4aaee23b747d0157e /net/base
parent053566af2ee54e9b5cb0ef5d2255b62a3ae300de (diff)
downloadchromium_src-671728091acbcbfa1ec4e2cffdaff112e43949a0.zip
chromium_src-671728091acbcbfa1ec4e2cffdaff112e43949a0.tar.gz
chromium_src-671728091acbcbfa1ec4e2cffdaff112e43949a0.tar.bz2
[net] Adds AddressTrackerLinux which keeps track of interface addresses using rtnetlink.
BUG=100690,113993 TEST=./net_unittests --gtest_filter=AddressTrackerLinuxTest.* Review URL: https://chromiumcodereview.appspot.com/10689015 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@146907 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'net/base')
-rw-r--r--net/base/address_tracker_linux.cc226
-rw-r--r--net/base/address_tracker_linux.h68
-rw-r--r--net/base/address_tracker_linux_unittest.cc259
-rw-r--r--net/base/network_change_notifier.cc16
-rw-r--r--net/base/network_change_notifier.h15
-rw-r--r--net/base/network_change_notifier_linux.cc106
-rw-r--r--net/base/network_change_notifier_linux.h3
-rw-r--r--net/base/network_change_notifier_netlink_linux.cc129
-rw-r--r--net/base/network_change_notifier_netlink_linux.h20
9 files changed, 604 insertions, 238 deletions
diff --git a/net/base/address_tracker_linux.cc b/net/base/address_tracker_linux.cc
new file mode 100644
index 0000000..b97b2b0
--- /dev/null
+++ b/net/base/address_tracker_linux.cc
@@ -0,0 +1,226 @@
+// 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;
+ }
+ // 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;
+ default:
+ break;
+ }
+ }
+ if (local)
+ address = local;
+ if (!address)
+ return false;
+ out->assign(address, address + address_length);
+ 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() {
+ if (netlink_fd_ >= 0)
+ CloseSocket(netlink_fd_);
+}
+
+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, 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));
+ 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);
+ 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);
+ return;
+ }
+
+ netlink_fd_ = sock;
+}
+
+AddressTrackerLinux::AddressMap AddressTrackerLinux::GetAddressMap() const {
+ base::AutoLock lock(lock_);
+ return map_;
+}
+
+bool AddressTrackerLinux::ReadMessages() {
+ char buffer[4096];
+ bool changed = false;
+ for (;;) {
+ int rv = HANDLE_EINTR(recv(netlink_fd_, buffer, sizeof(buffer), 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(buffer, rv);
+ };
+ return changed;
+}
+
+bool AddressTrackerLinux::HandleMessage(const char* buffer, size_t length) {
+ 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;
+ case RTM_NEWADDR: {
+ IPAddressNumber address;
+ if (GetAddress(header, &address)) {
+ base::AutoLock lock(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;
+ } 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;
+ default:
+ break;
+ }
+ }
+ return changed;
+}
+
+void AddressTrackerLinux::OnFileCanReadWithoutBlocking(int fd) {
+ DCHECK_EQ(netlink_fd_, fd);
+ if (ReadMessages())
+ callback_.Run();
+}
+
+void AddressTrackerLinux::OnFileCanWriteWithoutBlocking(int /* fd */) {}
+
+} // namespace internal
+} // namespace net
diff --git a/net/base/address_tracker_linux.h b/net/base/address_tracker_linux.h
new file mode 100644
index 0000000..a70a14c
--- /dev/null
+++ b/net/base/address_tracker_linux.h
@@ -0,0 +1,68 @@
+// 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.
+
+#ifndef NET_BASE_ADDRESS_TRACKER_LINUX_H_
+#define NET_BASE_ADDRESS_TRACKER_LINUX_H_
+
+#include <sys/socket.h> // Needed to include netlink.
+// Mask superfluous definition of |struct net|. This is fixed in Linux 2.6.38.
+#define net net_kernel
+#include <linux/rtnetlink.h>
+#undef net
+
+#include <map>
+
+#include "base/basictypes.h"
+#include "base/callback.h"
+#include "base/compiler_specific.h"
+#include "base/message_loop.h"
+#include "base/synchronization/lock.h"
+#include "net/base/net_util.h"
+
+namespace net {
+namespace internal {
+
+// Keeps track of network interface addresses using rtnetlink. Used by
+// NetworkChangeNotifier to provide signals to registered IPAddressObservers.
+class NET_EXPORT_PRIVATE AddressTrackerLinux
+ : public MessageLoopForIO::Watcher {
+ public:
+ typedef std::map<IPAddressNumber, struct ifaddrmsg> AddressMap;
+
+ // Will run |callback| when the AddressMap changes.
+ explicit AddressTrackerLinux(const base::Closure& callback);
+ virtual ~AddressTrackerLinux();
+
+ // Starts watching system configuration for changes. The current thread must
+ // have a MessageLoopForIO.
+ void Init();
+
+ AddressMap GetAddressMap() const;
+
+ private:
+ friend class AddressTrackerLinuxTest;
+
+ // Returns true if |map_| changed while reading messages from |netlink_fd_|.
+ bool ReadMessages();
+
+ // Returns true if |map_| changed while reading the message from |buffer|.
+ bool HandleMessage(const char* buffer, size_t length);
+
+ // MessageLoopForIO::Watcher:
+ virtual void OnFileCanReadWithoutBlocking(int fd) OVERRIDE;
+ virtual void OnFileCanWriteWithoutBlocking(int /* fd */) OVERRIDE;
+
+ base::Closure callback_;
+
+ int netlink_fd_;
+ MessageLoopForIO::FileDescriptorWatcher watcher_;
+
+ mutable base::Lock lock_;
+ AddressMap map_;
+};
+
+} // namespace internal
+} // namespace net
+
+#endif // NET_BASE_ADDRESS_TRACKER_LINUX_H_
diff --git a/net/base/address_tracker_linux_unittest.cc b/net/base/address_tracker_linux_unittest.cc
new file mode 100644
index 0000000..5c2fbc5
--- /dev/null
+++ b/net/base/address_tracker_linux_unittest.cc
@@ -0,0 +1,259 @@
+// 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 <vector>
+
+#include "base/bind.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace net {
+namespace internal {
+
+void Noop() {}
+
+class AddressTrackerLinuxTest : public testing::Test {
+ protected:
+ AddressTrackerLinuxTest() : tracker_(base::Bind(&Noop)) {}
+
+ bool HandleMessage(char* buf, size_t length) {
+ return tracker_.HandleMessage(buf, length);
+ }
+
+ AddressTrackerLinux::AddressMap GetAddressMap() {
+ return tracker_.GetAddressMap();
+ }
+
+ AddressTrackerLinux tracker_;
+};
+
+namespace {
+
+typedef std::vector<char> Buffer;
+
+class NetlinkMessage {
+ public:
+ explicit NetlinkMessage(uint16 type) : buffer_(NLMSG_HDRLEN) {
+ header()->nlmsg_type = type;
+ Align();
+ }
+
+ void AddPayload(const void* data, size_t length) {
+ CHECK_EQ(static_cast<size_t>(NLMSG_HDRLEN),
+ buffer_.size()) << "Payload must be added first";
+ Append(data, length);
+ Align();
+ }
+
+ void AddAttribute(uint16 type, const void* data, size_t length) {
+ struct nlattr attr;
+ attr.nla_len = NLA_HDRLEN + length;
+ attr.nla_type = type;
+ Append(&attr, sizeof(attr));
+ Align();
+ Append(data, length);
+ Align();
+ }
+
+ void AppendTo(Buffer* output) const {
+ CHECK_EQ(NLMSG_ALIGN(output->size()), output->size());
+ output->reserve(output->size() + NLMSG_LENGTH(buffer_.size()));
+ output->insert(output->end(), buffer_.begin(), buffer_.end());
+ }
+
+ private:
+ void Append(const void* data, size_t length) {
+ const char* chardata = reinterpret_cast<const char*>(data);
+ buffer_.insert(buffer_.end(), chardata, chardata + length);
+ }
+
+ void Align() {
+ header()->nlmsg_len = buffer_.size();
+ buffer_.insert(buffer_.end(), NLMSG_ALIGN(buffer_.size()) - buffer_.size(),
+ 0);
+ CHECK(NLMSG_OK(header(), buffer_.size()));
+ }
+
+ struct nlmsghdr* header() {
+ return reinterpret_cast<struct nlmsghdr*>(&buffer_[0]);
+ }
+
+ Buffer buffer_;
+};
+
+void MakeMessage(uint16 type,
+ uint8 flags,
+ uint8 family,
+ const IPAddressNumber& address,
+ const IPAddressNumber& local,
+ Buffer* output) {
+ NetlinkMessage nlmsg(type);
+ struct ifaddrmsg msg;
+ msg.ifa_family = family;
+ msg.ifa_flags = flags;
+ nlmsg.AddPayload(&msg, sizeof(msg));
+ if (address.size())
+ nlmsg.AddAttribute(IFA_ADDRESS, &address[0], address.size());
+ if (local.size())
+ nlmsg.AddAttribute(IFA_LOCAL, &local[0], local.size());
+ nlmsg.AppendTo(output);
+}
+
+const unsigned char kAddress0[] = { 127, 0, 0, 1 };
+const unsigned char kAddress1[] = { 10, 0, 0, 1 };
+const unsigned char kAddress2[] = { 192, 168, 0, 1 };
+const unsigned char kAddress3[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 1 };
+const unsigned char kAddress4[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255,
+ 169, 254, 0, 1 };
+
+TEST_F(AddressTrackerLinuxTest, NewAddress) {
+ const IPAddressNumber kEmpty;
+ const IPAddressNumber kAddr0(kAddress0, kAddress0 + arraysize(kAddress0));
+ const IPAddressNumber kAddr1(kAddress1, kAddress1 + arraysize(kAddress1));
+ const IPAddressNumber kAddr2(kAddress2, kAddress2 + arraysize(kAddress2));
+ const IPAddressNumber kAddr3(kAddress3, kAddress3 + arraysize(kAddress3));
+
+ Buffer buffer;
+ MakeMessage(RTM_NEWADDR, IFA_F_TEMPORARY, AF_INET, kAddr0, kEmpty, &buffer);
+ EXPECT_TRUE(HandleMessage(&buffer[0], buffer.size()));
+ AddressTrackerLinux::AddressMap map = GetAddressMap();
+ EXPECT_EQ(1u, map.size());
+ EXPECT_TRUE(map.find(kAddr0) != map.end());
+ EXPECT_EQ(IFA_F_TEMPORARY, map[kAddr0].ifa_flags);
+
+ buffer.clear();
+ MakeMessage(RTM_NEWADDR, IFA_F_HOMEADDRESS, AF_INET, kAddr1, kAddr2, &buffer);
+ EXPECT_TRUE(HandleMessage(&buffer[0], buffer.size()));
+ map = GetAddressMap();
+ EXPECT_EQ(2u, map.size());
+ EXPECT_TRUE(map.find(kAddr0) != map.end());
+ EXPECT_TRUE(map.find(kAddr2) != map.end());
+ EXPECT_EQ(IFA_F_HOMEADDRESS, map[kAddr2].ifa_flags);
+
+ buffer.clear();
+ MakeMessage(RTM_NEWADDR, 0, AF_INET6, kEmpty, kAddr3, &buffer);
+ EXPECT_TRUE(HandleMessage(&buffer[0], buffer.size()));
+ map = GetAddressMap();
+ EXPECT_EQ(3u, map.size());
+ EXPECT_TRUE(map.find(kAddr3) != map.end());
+}
+
+TEST_F(AddressTrackerLinuxTest, NewAddressChange) {
+ const IPAddressNumber kEmpty;
+ const IPAddressNumber kAddr0(kAddress0, kAddress0 + arraysize(kAddress0));
+
+ Buffer buffer;
+ MakeMessage(RTM_NEWADDR, IFA_F_TEMPORARY, AF_INET, kAddr0, kEmpty, &buffer);
+ EXPECT_TRUE(HandleMessage(&buffer[0], buffer.size()));
+ AddressTrackerLinux::AddressMap map = GetAddressMap();
+ EXPECT_EQ(1u, map.size());
+ EXPECT_TRUE(map.find(kAddr0) != map.end());
+ EXPECT_EQ(IFA_F_TEMPORARY, map[kAddr0].ifa_flags);
+
+ buffer.clear();
+ MakeMessage(RTM_NEWADDR, IFA_F_HOMEADDRESS, AF_INET, kAddr0, kEmpty, &buffer);
+ EXPECT_TRUE(HandleMessage(&buffer[0], buffer.size()));
+ map = GetAddressMap();
+ EXPECT_EQ(1u, map.size());
+ EXPECT_TRUE(map.find(kAddr0) != map.end());
+ EXPECT_EQ(IFA_F_HOMEADDRESS, map[kAddr0].ifa_flags);
+
+ // Both messages in one buffer.
+ buffer.clear();
+ MakeMessage(RTM_NEWADDR, IFA_F_TEMPORARY, AF_INET, kAddr0, kEmpty, &buffer);
+ MakeMessage(RTM_NEWADDR, IFA_F_HOMEADDRESS, AF_INET, kAddr0, kEmpty, &buffer);
+ EXPECT_TRUE(HandleMessage(&buffer[0], buffer.size()));
+ map = GetAddressMap();
+ EXPECT_EQ(1u, map.size());
+ EXPECT_EQ(IFA_F_HOMEADDRESS, map[kAddr0].ifa_flags);
+}
+
+TEST_F(AddressTrackerLinuxTest, NewAddressDuplicate) {
+ const IPAddressNumber kAddr0(kAddress0, kAddress0 + arraysize(kAddress0));
+
+ Buffer buffer;
+ MakeMessage(RTM_NEWADDR, IFA_F_TEMPORARY, AF_INET, kAddr0, kAddr0, &buffer);
+ EXPECT_TRUE(HandleMessage(&buffer[0], buffer.size()));
+ AddressTrackerLinux::AddressMap map = GetAddressMap();
+ EXPECT_EQ(1u, map.size());
+ EXPECT_TRUE(map.find(kAddr0) != map.end());
+ EXPECT_EQ(IFA_F_TEMPORARY, map[kAddr0].ifa_flags);
+
+ EXPECT_FALSE(HandleMessage(&buffer[0], buffer.size()));
+ map = GetAddressMap();
+ EXPECT_EQ(1u, map.size());
+ EXPECT_EQ(IFA_F_TEMPORARY, map[kAddr0].ifa_flags);
+}
+
+TEST_F(AddressTrackerLinuxTest, DeleteAddress) {
+ const IPAddressNumber kEmpty;
+ const IPAddressNumber kAddr0(kAddress0, kAddress0 + arraysize(kAddress0));
+ const IPAddressNumber kAddr1(kAddress1, kAddress1 + arraysize(kAddress1));
+ const IPAddressNumber kAddr2(kAddress2, kAddress2 + arraysize(kAddress2));
+
+ Buffer buffer;
+ MakeMessage(RTM_NEWADDR, 0, AF_INET, kAddr0, kEmpty, &buffer);
+ MakeMessage(RTM_NEWADDR, 0, AF_INET, kAddr1, kAddr2, &buffer);
+ EXPECT_TRUE(HandleMessage(&buffer[0], buffer.size()));
+ AddressTrackerLinux::AddressMap map = GetAddressMap();
+ EXPECT_EQ(2u, map.size());
+
+ buffer.clear();
+ MakeMessage(RTM_DELADDR, 0, AF_INET, kEmpty, kAddr0, &buffer);
+ EXPECT_TRUE(HandleMessage(&buffer[0], buffer.size()));
+ map = GetAddressMap();
+ EXPECT_EQ(1u, map.size());
+ EXPECT_TRUE(map.find(kAddr0) == map.end());
+ EXPECT_TRUE(map.find(kAddr2) != map.end());
+
+ buffer.clear();
+ MakeMessage(RTM_DELADDR, 0, AF_INET, kAddr2, kAddr1, &buffer);
+ // kAddr1 does not exist in the map.
+ EXPECT_FALSE(HandleMessage(&buffer[0], buffer.size()));
+ map = GetAddressMap();
+ EXPECT_EQ(1u, map.size());
+
+ buffer.clear();
+ MakeMessage(RTM_DELADDR, 0, AF_INET, kAddr2, kEmpty, &buffer);
+ EXPECT_TRUE(HandleMessage(&buffer[0], buffer.size()));
+ map = GetAddressMap();
+ EXPECT_EQ(0u, map.size());
+}
+
+TEST_F(AddressTrackerLinuxTest, IgnoredMessage) {
+ const IPAddressNumber kEmpty;
+ const IPAddressNumber kAddr0(kAddress0, kAddress0 + arraysize(kAddress0));
+ const IPAddressNumber kAddr3(kAddress3, kAddress3 + arraysize(kAddress3));
+
+ Buffer buffer;
+ // Ignored family.
+ MakeMessage(RTM_NEWADDR, 0, AF_UNSPEC, kAddr3, kAddr0, &buffer);
+ // No address.
+ MakeMessage(RTM_NEWADDR, 0, AF_INET, kEmpty, kEmpty, &buffer);
+ // Ignored type.
+ MakeMessage(RTM_DELROUTE, 0, AF_INET6, kAddr3, kEmpty, &buffer);
+ EXPECT_FALSE(HandleMessage(&buffer[0], buffer.size()));
+ EXPECT_EQ(0u, GetAddressMap().size());
+
+ // Valid message after ignored messages.
+ NetlinkMessage nlmsg(RTM_NEWADDR);
+ struct ifaddrmsg msg = {};
+ msg.ifa_family = AF_INET;
+ nlmsg.AddPayload(&msg, sizeof(msg));
+ // Ignored attribute.
+ struct ifa_cacheinfo cache_info = {};
+ nlmsg.AddAttribute(IFA_CACHEINFO, &cache_info, sizeof(cache_info));
+ nlmsg.AddAttribute(IFA_ADDRESS, &kAddr0[0], kAddr0.size());
+ nlmsg.AppendTo(&buffer);
+
+ EXPECT_TRUE(HandleMessage(&buffer[0], buffer.size()));
+ EXPECT_EQ(1u, GetAddressMap().size());
+}
+
+} // namespace
+
+} // namespace internal
+} // namespace net
diff --git a/net/base/network_change_notifier.cc b/net/base/network_change_notifier.cc
index 9183532..8c5f446 100644
--- a/net/base/network_change_notifier.cc
+++ b/net/base/network_change_notifier.cc
@@ -84,6 +84,15 @@ NetworkChangeNotifier::GetConnectionType() {
CONNECTION_UNKNOWN;
}
+#if defined(OS_LINUX)
+// static
+const internal::AddressTrackerLinux*
+NetworkChangeNotifier::GetAddressTracker() {
+ return g_network_change_notifier ?
+ g_network_change_notifier->GetAddressTrackerInternal() : NULL;
+}
+#endif
+
// static
bool NetworkChangeNotifier::IsWatchingDNS() {
if (!g_network_change_notifier)
@@ -155,6 +164,13 @@ NetworkChangeNotifier::NetworkChangeNotifier()
g_network_change_notifier = this;
}
+#if defined(OS_LINUX)
+const internal::AddressTrackerLinux*
+NetworkChangeNotifier::GetAddressTrackerInternal() const {
+ return NULL;
+}
+#endif
+
// static
void NetworkChangeNotifier::NotifyObserversOfIPAddressChange() {
if (g_network_change_notifier) {
diff --git a/net/base/network_change_notifier.h b/net/base/network_change_notifier.h
index 8420a8f..03bcbf3 100644
--- a/net/base/network_change_notifier.h
+++ b/net/base/network_change_notifier.h
@@ -16,6 +16,10 @@ class NetworkChangeNotifierFactory;
namespace internal {
class DnsConfigWatcher;
+
+#if defined(OS_LINUX)
+class AddressTrackerLinux;
+#endif
}
// NetworkChangeNotifier monitors the system for network changes, and notifies
@@ -124,6 +128,11 @@ class NET_EXPORT NetworkChangeNotifier {
// attempt to a particular remote site will be successful.
static ConnectionType GetConnectionType();
+#if defined(OS_LINUX)
+ // Returns the AddressTrackerLinux if present.
+ static const internal::AddressTrackerLinux* GetAddressTracker();
+#endif
+
// Convenience method to determine if the user is offline.
// Returns true if there is currently no internet connection.
//
@@ -173,6 +182,12 @@ class NET_EXPORT NetworkChangeNotifier {
NetworkChangeNotifier();
+#if defined(OS_LINUX)
+ // Returns the AddressTrackerLinux if present.
+ virtual const internal::AddressTrackerLinux*
+ GetAddressTrackerInternal() const;
+#endif
+
// Broadcasts a notification to all registered observers. Note that this
// happens asynchronously, even for observers on the current thread, even in
// tests.
diff --git a/net/base/network_change_notifier_linux.cc b/net/base/network_change_notifier_linux.cc
index b7acd46..1638f97 100644
--- a/net/base/network_change_notifier_linux.cc
+++ b/net/base/network_change_notifier_linux.cc
@@ -9,15 +9,12 @@
#include "net/base/network_change_notifier_linux.h"
-#include <errno.h>
#include <resolv.h>
-#include <sys/socket.h>
#include "base/bind.h"
#include "base/bind_helpers.h"
#include "base/callback.h"
#include "base/compiler_specific.h"
-#include "base/eintr_wrapper.h"
#include "base/memory/weak_ptr.h"
#include "base/synchronization/lock.h"
#include "base/synchronization/waitable_event.h"
@@ -27,16 +24,14 @@
#include "dbus/bus.h"
#include "dbus/message.h"
#include "dbus/object_proxy.h"
+#include "net/base/address_tracker_linux.h"
#include "net/base/net_errors.h"
-#include "net/base/network_change_notifier_netlink_linux.h"
#include "net/dns/dns_config_watcher.h"
namespace net {
namespace {
-const int kInvalidSocket = -1;
-
const char kNetworkManagerServiceName[] = "org.freedesktop.NetworkManager";
const char kNetworkManagerPath[] = "/org/freedesktop/NetworkManager";
const char kNetworkManagerInterface[] = "org.freedesktop.NetworkManager";
@@ -243,56 +238,45 @@ NetworkManagerApi::GetCurrentConnectionType() {
NetworkChangeNotifier::CONNECTION_UNKNOWN;
}
-class NetworkChangeNotifierLinux::Thread
- : public base::Thread, public MessageLoopForIO::Watcher {
+class NetworkChangeNotifierLinux::Thread : public base::Thread {
public:
explicit Thread(dbus::Bus* bus);
virtual ~Thread();
- // MessageLoopForIO::Watcher:
- virtual void OnFileCanReadWithoutBlocking(int fd) OVERRIDE;
- virtual void OnFileCanWriteWithoutBlocking(int /* fd */) OVERRIDE;
-
// Plumbing for NetworkChangeNotifier::GetCurrentConnectionType.
// Safe to call from any thread.
NetworkChangeNotifier::ConnectionType GetCurrentConnectionType() {
return network_manager_api_.GetCurrentConnectionType();
}
+ const internal::AddressTrackerLinux* address_tracker() const {
+ return &address_tracker_;
+ }
+
protected:
// base::Thread
virtual void Init() OVERRIDE;
virtual void CleanUp() OVERRIDE;
private:
- // Starts listening for netlink messages. Also handles the messages if there
- // are any available on the netlink socket.
- void ListenForNotifications();
-
- // Attempts to read from the netlink socket into |buf| of length |len|.
- // Returns the bytes read on synchronous success and ERR_IO_PENDING if the
- // recv() would block. Otherwise, it returns a net error code.
- int ReadNotificationMessage(char* buf, size_t len);
-
- // The netlink socket descriptor.
- int netlink_fd_;
- MessageLoopForIO::FileDescriptorWatcher netlink_watcher_;
-
// Used to detect online/offline state changes.
NetworkManagerApi network_manager_api_;
internal::DnsConfigWatcher dns_watcher_;
+ internal::AddressTrackerLinux address_tracker_;
DISALLOW_COPY_AND_ASSIGN(Thread);
};
NetworkChangeNotifierLinux::Thread::Thread(dbus::Bus* bus)
: base::Thread("NetworkChangeNotifier"),
- netlink_fd_(kInvalidSocket),
network_manager_api_(
base::Bind(&NetworkChangeNotifier::
NotifyObserversOfConnectionTypeChange),
- bus) {
+ bus),
+ address_tracker_(
+ base::Bind(&NetworkChangeNotifier::
+ NotifyObserversOfIPAddressChange)) {
}
NetworkChangeNotifierLinux::Thread::~Thread() {
@@ -300,77 +284,16 @@ NetworkChangeNotifierLinux::Thread::~Thread() {
}
void NetworkChangeNotifierLinux::Thread::Init() {
- netlink_fd_ = InitializeNetlinkSocket();
- if (netlink_fd_ < 0) {
- netlink_fd_ = kInvalidSocket;
- return;
- }
- ListenForNotifications();
-
network_manager_api_.Init();
-
dns_watcher_.Init();
+ address_tracker_.Init();
}
void NetworkChangeNotifierLinux::Thread::CleanUp() {
- if (netlink_fd_ != kInvalidSocket) {
- if (HANDLE_EINTR(close(netlink_fd_)) != 0)
- PLOG(ERROR) << "Failed to close socket";
- netlink_fd_ = kInvalidSocket;
- netlink_watcher_.StopWatchingFileDescriptor();
- }
network_manager_api_.CleanUp();
-
dns_watcher_.CleanUp();
}
-void NetworkChangeNotifierLinux::Thread::OnFileCanReadWithoutBlocking(int fd) {
- DCHECK_EQ(fd, netlink_fd_);
- ListenForNotifications();
-}
-
-void NetworkChangeNotifierLinux::Thread::OnFileCanWriteWithoutBlocking(
- int /* fd */) {
- NOTREACHED();
-}
-
-void NetworkChangeNotifierLinux::Thread::ListenForNotifications() {
- char buf[4096];
- int rv = ReadNotificationMessage(buf, arraysize(buf));
- while (rv > 0) {
- if (HandleNetlinkMessage(buf, rv)) {
- VLOG(1) << "Detected IP address changes.";
- NotifyObserversOfIPAddressChange();
- }
- rv = ReadNotificationMessage(buf, arraysize(buf));
- }
-
- if (rv == ERR_IO_PENDING) {
- rv = MessageLoopForIO::current()->WatchFileDescriptor(netlink_fd_, false,
- MessageLoopForIO::WATCH_READ, &netlink_watcher_, this);
- LOG_IF(ERROR, !rv) << "Failed to watch netlink socket: " << netlink_fd_;
- }
-}
-
-int NetworkChangeNotifierLinux::Thread::ReadNotificationMessage(
- char* buf,
- size_t len) {
- DCHECK_NE(len, 0u);
- DCHECK(buf);
- memset(buf, 0, len);
- int rv = recv(netlink_fd_, buf, len, 0);
- if (rv > 0)
- return rv;
-
- DCHECK_NE(rv, 0);
- if (errno != EAGAIN && errno != EWOULDBLOCK) {
- PLOG(DFATAL) << "recv";
- return ERR_FAILED;
- }
-
- return ERR_IO_PENDING;
-}
-
NetworkChangeNotifierLinux* NetworkChangeNotifierLinux::Create() {
return new NetworkChangeNotifierLinux(NULL);
}
@@ -400,4 +323,9 @@ NetworkChangeNotifierLinux::GetCurrentConnectionType() const {
return notifier_thread_->GetCurrentConnectionType();
}
+const internal::AddressTrackerLinux*
+NetworkChangeNotifierLinux::GetAddressTrackerInternal() const {
+ return notifier_thread_->address_tracker();
+}
+
} // namespace net
diff --git a/net/base/network_change_notifier_linux.h b/net/base/network_change_notifier_linux.h
index c1ba1ee..ba45b0b 100644
--- a/net/base/network_change_notifier_linux.h
+++ b/net/base/network_change_notifier_linux.h
@@ -34,6 +34,9 @@ class NET_EXPORT_PRIVATE NetworkChangeNotifierLinux
// NetworkChangeNotifier:
virtual ConnectionType GetCurrentConnectionType() const OVERRIDE;
+ virtual const internal::AddressTrackerLinux*
+ GetAddressTrackerInternal() const OVERRIDE;
+
// The thread used to listen for notifications. This relays the notification
// to the registered observers without posting back to the thread the object
// was created on.
diff --git a/net/base/network_change_notifier_netlink_linux.cc b/net/base/network_change_notifier_netlink_linux.cc
deleted file mode 100644
index 493e17a..0000000
--- a/net/base/network_change_notifier_netlink_linux.cc
+++ /dev/null
@@ -1,129 +0,0 @@
-// Copyright (c) 2010 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/network_change_notifier_netlink_linux.h"
-
-#include <fcntl.h>
-// socket.h is needed to define types for the linux kernel header netlink.h
-// so it needs to come before netlink.h.
-#include <sys/socket.h>
-#include <linux/netlink.h>
-#include <linux/rtnetlink.h>
-#include <string.h>
-#include <unistd.h>
-
-#include "base/logging.h"
-
-namespace {
-
-// Return true on success, false on failure.
-// Too small a function to bother putting in a library?
-bool SetNonBlocking(int fd) {
- int flags = fcntl(fd, F_GETFL, 0);
- if (-1 == flags)
- return false;
- return fcntl(fd, F_SETFL, flags | O_NONBLOCK) == 0 ? true : false;
-}
-
-bool IsIPv6Update(const struct nlmsghdr* netlink_message_header) {
- const struct ifaddrmsg* address_message =
- reinterpret_cast<struct ifaddrmsg*>(NLMSG_DATA(netlink_message_header));
- return address_message->ifa_family == AF_INET6;
-}
-
-bool IsDuplicateIPv6AddressUpdate(
- const struct nlmsghdr* netlink_message_header) {
- const struct ifaddrmsg* address_message =
- reinterpret_cast<struct ifaddrmsg*>(NLMSG_DATA(netlink_message_header));
- int address_message_length = IFA_PAYLOAD(netlink_message_header);
- const struct rtattr* route_attribute =
- reinterpret_cast<struct rtattr*>(IFA_RTA(address_message));
- DCHECK_EQ(address_message->ifa_family, AF_INET6);
-
- // Look for a cacheinfo attribute, and ignore new address broadcasts
- // where the updated time stamp is newer than the created time stamp.
- while (RTA_OK(route_attribute, address_message_length)) {
- if (route_attribute->rta_type == IFA_CACHEINFO) {
- struct ifa_cacheinfo* cache_info =
- reinterpret_cast<struct ifa_cacheinfo*>(RTA_DATA(route_attribute));
- if (cache_info->cstamp != cache_info->tstamp)
- return true;
- }
- route_attribute = RTA_NEXT(route_attribute, address_message_length);
- }
- return false;
-}
-
-} // namespace
-
-int InitializeNetlinkSocket() {
- int sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
- if (sock < 0) {
- PLOG(ERROR) << "Error creating netlink socket";
- return -1;
- }
-
- if (!SetNonBlocking(sock)) {
- PLOG(ERROR) << "Failed to set netlink socket to non-blocking mode.";
- if (close(sock) != 0)
- PLOG(ERROR) << "Failed to close socket";
- return -1;
- }
-
- struct sockaddr_nl local_addr;
- memset(&local_addr, 0, sizeof(local_addr));
- local_addr.nl_family = AF_NETLINK;
- local_addr.nl_pid = getpid();
- local_addr.nl_groups = RTMGRP_IPV4_IFADDR | RTMGRP_IPV6_IFADDR |
- RTMGRP_NOTIFY;
- int ret = bind(sock, reinterpret_cast<struct sockaddr*>(&local_addr),
- sizeof(local_addr));
- if (ret < 0) {
- PLOG(ERROR) << "Error binding netlink socket";
- if (close(sock) != 0)
- PLOG(ERROR) << "Failed to close socket";
- return -1;
- }
-
- return sock;
-}
-
-bool HandleNetlinkMessage(char* buf, size_t len) {
- const struct nlmsghdr* netlink_message_header =
- reinterpret_cast<struct nlmsghdr*>(buf);
- DCHECK(netlink_message_header);
- for (; NLMSG_OK(netlink_message_header, len);
- netlink_message_header = NLMSG_NEXT(netlink_message_header, len)) {
- int netlink_message_type = netlink_message_header->nlmsg_type;
- switch (netlink_message_type) {
- case NLMSG_DONE:
- NOTREACHED()
- << "This is a monitoring netlink socket. It should never be done.";
- return false;
- case NLMSG_ERROR:
- LOG(ERROR) << "Unexpected netlink error.";
- return false;
- // During IP address changes, we will see all these messages. Only fire
- // the notification when we get a new address or remove an address. We
- // may still end up notifying observers more than strictly necessary, but
- // if the primary interface goes down and back up, then this is necessary.
- case RTM_NEWADDR:
- if (IsIPv6Update(netlink_message_header) &&
- IsDuplicateIPv6AddressUpdate(netlink_message_header))
- return false;
- return true;
- case RTM_DELADDR:
- return true;
- case RTM_NEWLINK:
- case RTM_DELLINK:
- return false;
- default:
- LOG(DFATAL) << "Received unexpected netlink message type: "
- << netlink_message_type;
- return false;
- }
- }
-
- return false;
-}
diff --git a/net/base/network_change_notifier_netlink_linux.h b/net/base/network_change_notifier_netlink_linux.h
deleted file mode 100644
index c06fdc7..0000000
--- a/net/base/network_change_notifier_netlink_linux.h
+++ /dev/null
@@ -1,20 +0,0 @@
-// Copyright (c) 2010 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.
-
-// This file is to hide the netlink implementation details since the netlink.h
-// header contains a struct net; which conflicts with the net namespace. So we
-// separate out all the netlink stuff into these files.
-
-#ifndef NET_BASE_NETWORK_CHANGE_NOTIFIER_NETLINK_LINUX_H_
-#define NET_BASE_NETWORK_CHANGE_NOTIFIER_NETLINK_LINUX_H_
-
-#include <cstddef>
-
-// Returns the file descriptor if successful. Otherwise, returns -1.
-int InitializeNetlinkSocket();
-
-// Returns true if a network change has been detected, otherwise returns false.
-bool HandleNetlinkMessage(char* buf, size_t len);
-
-#endif // NET_BASE_NETWORK_CHANGE_NOTIFIER_NETLINK_LINUX_H_