diff options
author | szym@chromium.org <szym@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-07-16 22:27:24 +0000 |
---|---|---|
committer | szym@chromium.org <szym@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-07-16 22:27:24 +0000 |
commit | 671728091acbcbfa1ec4e2cffdaff112e43949a0 (patch) | |
tree | 44803eacadbf212e5def8ff4aaee23b747d0157e /net/base | |
parent | 053566af2ee54e9b5cb0ef5d2255b62a3ae300de (diff) | |
download | chromium_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.cc | 226 | ||||
-rw-r--r-- | net/base/address_tracker_linux.h | 68 | ||||
-rw-r--r-- | net/base/address_tracker_linux_unittest.cc | 259 | ||||
-rw-r--r-- | net/base/network_change_notifier.cc | 16 | ||||
-rw-r--r-- | net/base/network_change_notifier.h | 15 | ||||
-rw-r--r-- | net/base/network_change_notifier_linux.cc | 106 | ||||
-rw-r--r-- | net/base/network_change_notifier_linux.h | 3 | ||||
-rw-r--r-- | net/base/network_change_notifier_netlink_linux.cc | 129 | ||||
-rw-r--r-- | net/base/network_change_notifier_netlink_linux.h | 20 |
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_ |