Commit f09e9971 authored by guoweis's avatar guoweis Committed by Commit bot

The non-tracking mode in AddressTracker will be used as a way to get a...

The non-tracking mode in AddressTracker will be used as a way to get a snapshot of current system configuration from GetNetworkList whose current implementation doesn't provide IPv6 address attributes.

BUG=411086

Review URL: https://codereview.chromium.org/571743002

Cr-Commit-Position: refs/heads/master@{#296048}
parent 72ad6149
......@@ -97,6 +97,18 @@ const char* GetInterfaceName(int interface_index) {
} // namespace
AddressTrackerLinux::AddressTrackerLinux()
: get_interface_name_(GetInterfaceName),
address_callback_(base::Bind(&base::DoNothing)),
link_callback_(base::Bind(&base::DoNothing)),
tunnel_callback_(base::Bind(&base::DoNothing)),
netlink_fd_(-1),
is_offline_(true),
is_offline_initialized_(false),
is_offline_initialized_cv_(&is_offline_lock_),
tracking_(false) {
}
AddressTrackerLinux::AddressTrackerLinux(const base::Closure& address_callback,
const base::Closure& link_callback,
const base::Closure& tunnel_callback)
......@@ -107,7 +119,8 @@ AddressTrackerLinux::AddressTrackerLinux(const base::Closure& address_callback,
netlink_fd_(-1),
is_offline_(true),
is_offline_initialized_(false),
is_offline_initialized_cv_(&is_offline_lock_) {
is_offline_initialized_cv_(&is_offline_lock_),
tracking_(true) {
DCHECK(!address_callback.is_null());
DCHECK(!link_callback.is_null());
}
......@@ -124,20 +137,24 @@ void AddressTrackerLinux::Init() {
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 |
RTMGRP_LINK;
int rv = bind(netlink_fd_,
reinterpret_cast<struct sockaddr*>(&addr),
sizeof(addr));
if (rv < 0) {
PLOG(ERROR) << "Could not bind NETLINK socket";
AbortAndForceOnline();
return;
int rv;
if (tracking_) {
// 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 | RTMGRP_LINK;
rv = bind(
netlink_fd_, reinterpret_cast<struct sockaddr*>(&addr), sizeof(addr));
if (rv < 0) {
PLOG(ERROR) << "Could not bind NETLINK socket";
AbortAndForceOnline();
return;
}
}
// Request dump of addresses.
......@@ -186,38 +203,45 @@ void AddressTrackerLinux::Init() {
// Consume pending message to populate links_online_, but don't notify.
ReadMessages(&address_changed, &link_changed, &tunnel_changed);
{
base::AutoLock lock(is_offline_lock_);
AddressTrackerAutoLock lock(*this, is_offline_lock_);
is_offline_initialized_ = true;
is_offline_initialized_cv_.Signal();
}
rv = base::MessageLoopForIO::current()->WatchFileDescriptor(
netlink_fd_, true, base::MessageLoopForIO::WATCH_READ, &watcher_, this);
if (rv < 0) {
PLOG(ERROR) << "Could not watch NETLINK socket";
AbortAndForceOnline();
return;
if (tracking_) {
rv = base::MessageLoopForIO::current()->WatchFileDescriptor(
netlink_fd_, true, base::MessageLoopForIO::WATCH_READ, &watcher_, this);
if (rv < 0) {
PLOG(ERROR) << "Could not watch NETLINK socket";
AbortAndForceOnline();
return;
}
}
}
void AddressTrackerLinux::AbortAndForceOnline() {
CloseSocket();
base::AutoLock lock(is_offline_lock_);
AddressTrackerAutoLock lock(*this, is_offline_lock_);
is_offline_ = false;
is_offline_initialized_ = true;
is_offline_initialized_cv_.Signal();
}
AddressTrackerLinux::AddressMap AddressTrackerLinux::GetAddressMap() const {
base::AutoLock lock(address_map_lock_);
AddressTrackerAutoLock lock(*this, address_map_lock_);
return address_map_;
}
base::hash_set<int> AddressTrackerLinux::GetOnlineLinks() const {
AddressTrackerAutoLock lock(*this, online_links_lock_);
return online_links_;
}
NetworkChangeNotifier::ConnectionType
AddressTrackerLinux::GetCurrentConnectionType() {
// http://crbug.com/125097
base::ThreadRestrictions::ScopedAllowWait allow_wait;
base::AutoLock lock(is_offline_lock_);
AddressTrackerAutoLock lock(*this, is_offline_lock_);
// Make sure the initial offline state is set before returning.
while (!is_offline_initialized_) {
is_offline_initialized_cv_.Wait();
......@@ -254,10 +278,15 @@ void AddressTrackerLinux::ReadMessages(bool* address_changed,
return;
}
HandleMessage(buffer, rv, address_changed, link_changed, tunnel_changed);
};
}
if (*link_changed) {
base::AutoLock lock(is_offline_lock_);
is_offline_ = online_links_.empty();
bool is_offline;
{
AddressTrackerAutoLock lock(*this, online_links_lock_);
is_offline = online_links_.empty();
}
AddressTrackerAutoLock lock(*this, is_offline_lock_);
is_offline_ = is_offline;
}
}
......@@ -282,7 +311,7 @@ void AddressTrackerLinux::HandleMessage(char* buffer,
IPAddressNumber address;
bool really_deprecated;
if (GetAddress(header, &address, &really_deprecated)) {
base::AutoLock lock(address_map_lock_);
AddressTrackerAutoLock lock(*this, address_map_lock_);
struct ifaddrmsg* msg =
reinterpret_cast<struct ifaddrmsg*>(NLMSG_DATA(header));
// Routers may frequently (every few seconds) output the IPv6 ULA
......@@ -309,7 +338,7 @@ void AddressTrackerLinux::HandleMessage(char* buffer,
case RTM_DELADDR: {
IPAddressNumber address;
if (GetAddress(header, &address, NULL)) {
base::AutoLock lock(address_map_lock_);
AddressTrackerAutoLock lock(*this, address_map_lock_);
if (address_map_.erase(address))
*address_changed = true;
}
......@@ -319,12 +348,14 @@ void AddressTrackerLinux::HandleMessage(char* buffer,
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)) {
AddressTrackerAutoLock lock(*this, online_links_lock_);
if (online_links_.insert(msg->ifi_index).second) {
*link_changed = true;
if (IsTunnelInterface(msg))
*tunnel_changed = true;
}
} else {
AddressTrackerAutoLock lock(*this, online_links_lock_);
if (online_links_.erase(msg->ifi_index)) {
*link_changed = true;
if (IsTunnelInterface(msg))
......@@ -335,6 +366,7 @@ void AddressTrackerLinux::HandleMessage(char* buffer,
case RTM_DELLINK: {
const struct ifinfomsg* msg =
reinterpret_cast<struct ifinfomsg*>(NLMSG_DATA(header));
AddressTrackerAutoLock lock(*this, online_links_lock_);
if (online_links_.erase(msg->ifi_index)) {
*link_changed = true;
if (IsTunnelInterface(msg))
......@@ -374,5 +406,23 @@ bool AddressTrackerLinux::IsTunnelInterface(const struct ifinfomsg* msg) const {
return strncmp(get_interface_name_(msg->ifi_index), "tun", 3) == 0;
}
AddressTrackerLinux::AddressTrackerAutoLock::AddressTrackerAutoLock(
const AddressTrackerLinux& tracker,
base::Lock& lock)
: tracker_(tracker), lock_(lock) {
if (tracker_.tracking_) {
lock_.Acquire();
} else {
DCHECK(tracker_.thread_checker_.CalledOnValidThread());
}
}
AddressTrackerLinux::AddressTrackerAutoLock::~AddressTrackerAutoLock() {
if (tracker_.tracking_) {
lock_.AssertAcquired();
lock_.Release();
}
}
} // namespace internal
} // namespace net
......@@ -20,6 +20,7 @@
#include "base/message_loop/message_loop.h"
#include "base/synchronization/condition_variable.h"
#include "base/synchronization/lock.h"
#include "base/threading/thread_checker.h"
#include "net/base/net_util.h"
#include "net/base/network_change_notifier.h"
......@@ -33,20 +34,33 @@ class NET_EXPORT_PRIVATE AddressTrackerLinux :
public:
typedef std::map<IPAddressNumber, struct ifaddrmsg> AddressMap;
// Will run |address_callback| when the AddressMap changes, |link_callback|
// when the list of online links changes, and |tunnel_callback| when the list
// of online tunnels changes.
// Non-tracking version constructor: it takes a snapshot of the
// current system configuration. Once Init() returns, the
// configuration is available through GetOnlineLinks() and
// GetAddressMap().
AddressTrackerLinux();
// Tracking version constructor: it will run |address_callback| when
// the AddressMap changes, |link_callback| when the list of online
// links changes, and |tunnel_callback| when the list of online
// tunnels changes.
AddressTrackerLinux(const base::Closure& address_callback,
const base::Closure& link_callback,
const base::Closure& tunnel_callback);
virtual ~AddressTrackerLinux();
// Starts watching system configuration for changes. The current thread must
// have a MessageLoopForIO.
// In tracking mode, it starts watching the system configuration for
// changes. The current thread must have a MessageLoopForIO. In
// non-tracking mode, once Init() returns, a snapshot of the system
// configuration is available through GetOnlineLinks() and
// GetAddressMap().
void Init();
AddressMap GetAddressMap() const;
// Returns set of interface indicies for online interfaces.
base::hash_set<int> GetOnlineLinks() const;
// Implementation of NetworkChangeNotifierLinux::GetCurrentConnectionType().
// Safe to call from any thread, but will block until Init() has completed.
NetworkChangeNotifier::ConnectionType GetCurrentConnectionType();
......@@ -54,6 +68,20 @@ class NET_EXPORT_PRIVATE AddressTrackerLinux :
private:
friend class AddressTrackerLinuxTest;
// In tracking mode, holds |lock| while alive. In non-tracking mode,
// enforces single-threaded access.
class AddressTrackerAutoLock {
public:
AddressTrackerAutoLock(const AddressTrackerLinux& tracker,
base::Lock& lock);
~AddressTrackerAutoLock();
private:
const AddressTrackerLinux& tracker_;
base::Lock& lock_;
DISALLOW_COPY_AND_ASSIGN(AddressTrackerAutoLock);
};
// A function that returns the name of an interface given the interface index
// in |interface_index|.
typedef const char* (*GetInterfaceNameFunction)(int interface_index);
......@@ -105,12 +133,17 @@ class NET_EXPORT_PRIVATE AddressTrackerLinux :
AddressMap address_map_;
// Set of interface indices for links that are currently online.
mutable base::Lock online_links_lock_;
base::hash_set<int> online_links_;
base::Lock is_offline_lock_;
bool is_offline_;
bool is_offline_initialized_;
base::ConditionVariable is_offline_initialized_cv_;
bool tracking_;
// Used to verify single-threaded access in non-tracking mode.
base::ThreadChecker thread_checker_;
};
} // namespace internal
......
......@@ -2,6 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "base/memory/scoped_ptr.h"
#include "net/base/address_tracker_linux.h"
#include <linux/if.h>
......@@ -33,12 +34,18 @@ typedef std::vector<char> Buffer;
class AddressTrackerLinuxTest : public testing::Test {
protected:
AddressTrackerLinuxTest()
: tracker_(base::Bind(&base::DoNothing),
base::Bind(&base::DoNothing),
base::Bind(&base::DoNothing)),
original_get_interface_name_(tracker_.get_interface_name_) {
tracker_.get_interface_name_ = TestGetInterfaceName;
AddressTrackerLinuxTest() {}
void InitializeAddressTracker(bool tracking) {
if (tracking) {
tracker_.reset(new AddressTrackerLinux(base::Bind(&base::DoNothing),
base::Bind(&base::DoNothing),
base::Bind(&base::DoNothing)));
} else {
tracker_.reset(new AddressTrackerLinux());
}
original_get_interface_name_ = tracker_->get_interface_name_;
tracker_->get_interface_name_ = TestGetInterfaceName;
}
bool HandleAddressMessage(const Buffer& buf) {
......@@ -46,7 +53,7 @@ class AddressTrackerLinuxTest : public testing::Test {
bool address_changed = false;
bool link_changed = false;
bool tunnel_changed = false;
tracker_.HandleMessage(&writable_buf[0], buf.size(),
tracker_->HandleMessage(&writable_buf[0], buf.size(),
&address_changed, &link_changed, &tunnel_changed);
EXPECT_FALSE(link_changed);
return address_changed;
......@@ -57,7 +64,7 @@ class AddressTrackerLinuxTest : public testing::Test {
bool address_changed = false;
bool link_changed = false;
bool tunnel_changed = false;
tracker_.HandleMessage(&writable_buf[0], buf.size(),
tracker_->HandleMessage(&writable_buf[0], buf.size(),
&address_changed, &link_changed, &tunnel_changed);
EXPECT_FALSE(address_changed);
return link_changed;
......@@ -68,21 +75,21 @@ class AddressTrackerLinuxTest : public testing::Test {
bool address_changed = false;
bool link_changed = false;
bool tunnel_changed = false;
tracker_.HandleMessage(&writable_buf[0], buf.size(),
tracker_->HandleMessage(&writable_buf[0], buf.size(),
&address_changed, &link_changed, &tunnel_changed);
EXPECT_FALSE(address_changed);
return tunnel_changed;
}
AddressTrackerLinux::AddressMap GetAddressMap() {
return tracker_.GetAddressMap();
return tracker_->GetAddressMap();
}
const base::hash_set<int>* GetOnlineLinks() const {
return &tracker_.online_links_;
const base::hash_set<int> GetOnlineLinks() const {
return tracker_->GetOnlineLinks();
}
AddressTrackerLinux tracker_;
scoped_ptr<AddressTrackerLinux> tracker_;
AddressTrackerLinux::GetInterfaceNameFunction original_get_interface_name_;
};
......@@ -190,6 +197,8 @@ const unsigned char kAddress3[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 1 };
TEST_F(AddressTrackerLinuxTest, NewAddress) {
InitializeAddressTracker(true);
const IPAddressNumber kEmpty;
const IPAddressNumber kAddr0(kAddress0, kAddress0 + arraysize(kAddress0));
const IPAddressNumber kAddr1(kAddress1, kAddress1 + arraysize(kAddress1));
......@@ -224,6 +233,8 @@ TEST_F(AddressTrackerLinuxTest, NewAddress) {
}
TEST_F(AddressTrackerLinuxTest, NewAddressChange) {
InitializeAddressTracker(true);
const IPAddressNumber kEmpty;
const IPAddressNumber kAddr0(kAddress0, kAddress0 + arraysize(kAddress0));
......@@ -258,6 +269,8 @@ TEST_F(AddressTrackerLinuxTest, NewAddressChange) {
}
TEST_F(AddressTrackerLinuxTest, NewAddressDuplicate) {
InitializeAddressTracker(true);
const IPAddressNumber kAddr0(kAddress0, kAddress0 + arraysize(kAddress0));
Buffer buffer;
......@@ -276,6 +289,8 @@ TEST_F(AddressTrackerLinuxTest, NewAddressDuplicate) {
}
TEST_F(AddressTrackerLinuxTest, DeleteAddress) {
InitializeAddressTracker(true);
const IPAddressNumber kEmpty;
const IPAddressNumber kAddr0(kAddress0, kAddress0 + arraysize(kAddress0));
const IPAddressNumber kAddr1(kAddress1, kAddress1 + arraysize(kAddress1));
......@@ -311,6 +326,8 @@ TEST_F(AddressTrackerLinuxTest, DeleteAddress) {
}
TEST_F(AddressTrackerLinuxTest, DeprecatedLifetime) {
InitializeAddressTracker(true);
const IPAddressNumber kEmpty;
const IPAddressNumber kAddr3(kAddress3, kAddress3 + arraysize(kAddress3));
......@@ -351,6 +368,8 @@ TEST_F(AddressTrackerLinuxTest, DeprecatedLifetime) {
}
TEST_F(AddressTrackerLinuxTest, IgnoredMessage) {
InitializeAddressTracker(true);
const IPAddressNumber kEmpty;
const IPAddressNumber kAddr0(kAddress0, kAddress0 + arraysize(kAddress0));
const IPAddressNumber kAddr3(kAddress3, kAddress3 + arraysize(kAddress3));
......@@ -381,6 +400,8 @@ TEST_F(AddressTrackerLinuxTest, IgnoredMessage) {
}
TEST_F(AddressTrackerLinuxTest, AddInterface) {
InitializeAddressTracker(true);
Buffer buffer;
// Ignores loopback.
......@@ -388,69 +409,73 @@ TEST_F(AddressTrackerLinuxTest, AddInterface) {
IFF_LOOPBACK | IFF_UP | IFF_LOWER_UP | IFF_RUNNING,
0, &buffer);
EXPECT_FALSE(HandleLinkMessage(buffer));
EXPECT_TRUE(GetOnlineLinks()->empty());
EXPECT_TRUE(GetOnlineLinks().empty());
// Ignores not IFF_LOWER_UP.
MakeLinkMessage(RTM_NEWLINK, IFF_UP | IFF_RUNNING, 0, &buffer);
EXPECT_FALSE(HandleLinkMessage(buffer));
EXPECT_TRUE(GetOnlineLinks()->empty());
EXPECT_TRUE(GetOnlineLinks().empty());
// Ignores deletion.
MakeLinkMessage(RTM_DELLINK, IFF_UP | IFF_LOWER_UP | IFF_RUNNING, 0, &buffer);
EXPECT_FALSE(HandleLinkMessage(buffer));
EXPECT_TRUE(GetOnlineLinks()->empty());
EXPECT_TRUE(GetOnlineLinks().empty());
// Verify success.
MakeLinkMessage(RTM_NEWLINK, IFF_UP | IFF_LOWER_UP | IFF_RUNNING, 0, &buffer);
EXPECT_TRUE(HandleLinkMessage(buffer));
EXPECT_EQ(1u, GetOnlineLinks()->count(0));
EXPECT_EQ(1u, GetOnlineLinks()->size());
EXPECT_EQ(1u, GetOnlineLinks().count(0));
EXPECT_EQ(1u, GetOnlineLinks().size());
// Ignores redundant enables.
MakeLinkMessage(RTM_NEWLINK, IFF_UP | IFF_LOWER_UP | IFF_RUNNING, 0, &buffer);
EXPECT_FALSE(HandleLinkMessage(buffer));
EXPECT_EQ(1u, GetOnlineLinks()->count(0));
EXPECT_EQ(1u, GetOnlineLinks()->size());
EXPECT_EQ(1u, GetOnlineLinks().count(0));
EXPECT_EQ(1u, GetOnlineLinks().size());
// Verify adding another online device (e.g. VPN) is considered a change.
MakeLinkMessage(RTM_NEWLINK, IFF_UP | IFF_LOWER_UP | IFF_RUNNING, 1, &buffer);
EXPECT_TRUE(HandleLinkMessage(buffer));
EXPECT_EQ(1u, GetOnlineLinks()->count(0));
EXPECT_EQ(1u, GetOnlineLinks()->count(1));
EXPECT_EQ(2u, GetOnlineLinks()->size());
EXPECT_EQ(1u, GetOnlineLinks().count(0));
EXPECT_EQ(1u, GetOnlineLinks().count(1));
EXPECT_EQ(2u, GetOnlineLinks().size());
}
TEST_F(AddressTrackerLinuxTest, RemoveInterface) {
InitializeAddressTracker(true);
Buffer buffer;
// Should disappear when not IFF_LOWER_UP.
MakeLinkMessage(RTM_NEWLINK, IFF_UP | IFF_LOWER_UP | IFF_RUNNING, 0, &buffer);
EXPECT_TRUE(HandleLinkMessage(buffer));
EXPECT_FALSE(GetOnlineLinks()->empty());
EXPECT_FALSE(GetOnlineLinks().empty());
MakeLinkMessage(RTM_NEWLINK, IFF_UP | IFF_RUNNING, 0, &buffer);
EXPECT_TRUE(HandleLinkMessage(buffer));
EXPECT_TRUE(GetOnlineLinks()->empty());
EXPECT_TRUE(GetOnlineLinks().empty());
// Ignores redundant disables.
MakeLinkMessage(RTM_NEWLINK, IFF_UP | IFF_RUNNING, 0, &buffer);
EXPECT_FALSE(HandleLinkMessage(buffer));
EXPECT_TRUE(GetOnlineLinks()->empty());
EXPECT_TRUE(GetOnlineLinks().empty());
// Ignores deleting down interfaces.
MakeLinkMessage(RTM_DELLINK, IFF_UP | IFF_RUNNING, 0, &buffer);
EXPECT_FALSE(HandleLinkMessage(buffer));
EXPECT_TRUE(GetOnlineLinks()->empty());
EXPECT_TRUE(GetOnlineLinks().empty());
// Should disappear when deleted.
MakeLinkMessage(RTM_NEWLINK, IFF_UP | IFF_LOWER_UP | IFF_RUNNING, 0, &buffer);
EXPECT_TRUE(HandleLinkMessage(buffer));
EXPECT_FALSE(GetOnlineLinks()->empty());
EXPECT_FALSE(GetOnlineLinks().empty());
MakeLinkMessage(RTM_DELLINK, IFF_UP | IFF_LOWER_UP | IFF_RUNNING, 0, &buffer);
EXPECT_TRUE(HandleLinkMessage(buffer));
EXPECT_TRUE(GetOnlineLinks()->empty());
EXPECT_TRUE(GetOnlineLinks().empty());
}
TEST_F(AddressTrackerLinuxTest, TunnelInterface) {
InitializeAddressTracker(true);
Buffer buffer;
// Ignores without "tun" prefixed name.
......@@ -493,10 +518,38 @@ TEST_F(AddressTrackerLinuxTest, TunnelInterface) {
// Check AddressTrackerLinux::get_interface_name_ original implementation
// doesn't crash or return NULL.
TEST_F(AddressTrackerLinuxTest, GetInterfaceName) {
InitializeAddressTracker(true);
for (int i = 0; i < 10; i++)
EXPECT_NE((const char*)NULL, original_get_interface_name_(i));
}
TEST_F(AddressTrackerLinuxTest, NonTrackingMode) {
InitializeAddressTracker(false);
const IPAddressNumber kEmpty;
const IPAddressNumber kAddr0(kAddress0, kAddress0 + arraysize(kAddress0));
Buffer buffer;
MakeAddrMessage(
RTM_NEWADDR, IFA_F_TEMPORARY, AF_INET, kAddr0, kEmpty, &buffer);
EXPECT_TRUE(HandleAddressMessage(buffer));
AddressTrackerLinux::AddressMap map = GetAddressMap();
EXPECT_EQ(1u, map.size());
EXPECT_EQ(1u, map.count(kAddr0));
EXPECT_EQ(IFA_F_TEMPORARY, map[kAddr0].ifa_flags);
MakeLinkMessage(RTM_NEWLINK, IFF_UP | IFF_LOWER_UP | IFF_RUNNING, 0, &buffer);
EXPECT_TRUE(HandleLinkMessage(buffer));
EXPECT_EQ(1u, GetOnlineLinks().count(0));
EXPECT_EQ(1u, GetOnlineLinks().size());
}
TEST_F(AddressTrackerLinuxTest, NonTrackingModeInit) {
AddressTrackerLinux tracker;
tracker.Init();
}
} // namespace
} // namespace internal
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment