diff options
author | pauljensen@chromium.org <pauljensen@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2014-01-23 18:12:20 +0000 |
---|---|---|
committer | pauljensen@chromium.org <pauljensen@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2014-01-23 18:12:20 +0000 |
commit | fb5dead7e644df782b1e63e97fa532940d1cc7bb (patch) | |
tree | 333192f875355251820a0ca074b24a31768d786d /net | |
parent | 4d89a9279940415b80c79f51beb3394f7e991560 (diff) | |
download | chromium_src-fb5dead7e644df782b1e63e97fa532940d1cc7bb.zip chromium_src-fb5dead7e644df782b1e63e97fa532940d1cc7bb.tar.gz chromium_src-fb5dead7e644df782b1e63e97fa532940d1cc7bb.tar.bz2 |
Work around linux kernel bug by interpreting IPv6 "Preferred Lifetimes" of 0 as implying the address is deprecated. This avoids spurious network changed signals on linux by ignoring more duplicate messages by canonicalizing them before matching them.
BUG=268042
TEST=net_unittests --gtest_filter=AddressTrackerLinuxTest.DeprecatedLifetime
Review URL: https://codereview.chromium.org/109213004
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@246641 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'net')
-rw-r--r-- | net/base/address_tracker_linux.cc | 34 | ||||
-rw-r--r-- | net/base/address_tracker_linux.h | 2 | ||||
-rw-r--r-- | net/base/address_tracker_linux_unittest.cc | 75 |
3 files changed, 95 insertions, 16 deletions
diff --git a/net/base/address_tracker_linux.cc b/net/base/address_tracker_linux.cc index f863ccd..37911de 100644 --- a/net/base/address_tracker_linux.cc +++ b/net/base/address_tracker_linux.cc @@ -18,7 +18,12 @@ namespace internal { namespace { // Retrieves address from NETLINK address message. -bool GetAddress(const struct nlmsghdr* header, IPAddressNumber* out) { +// Sets |really_deprecated| for IPv6 addresses with preferred lifetimes of 0. +bool GetAddress(const struct nlmsghdr* header, + IPAddressNumber* out, + bool* really_deprecated) { + if (really_deprecated) + *really_deprecated = false; const struct ifaddrmsg* msg = reinterpret_cast<struct ifaddrmsg*>(NLMSG_DATA(header)); size_t address_length = 0; @@ -53,6 +58,12 @@ bool GetAddress(const struct nlmsghdr* header, IPAddressNumber* out) { DCHECK_GE(RTA_PAYLOAD(attr), address_length); local = reinterpret_cast<unsigned char*>(RTA_DATA(attr)); break; + case IFA_CACHEINFO: { + const struct ifa_cacheinfo *cache_info = + reinterpret_cast<const struct ifa_cacheinfo*>(RTA_DATA(attr)); + if (really_deprecated) + *really_deprecated = (cache_info->ifa_prefered == 0); + } break; default: break; } @@ -225,13 +236,12 @@ void AddressTrackerLinux::ReadMessages(bool* address_changed, } } -void AddressTrackerLinux::HandleMessage(const char* buffer, +void AddressTrackerLinux::HandleMessage(char* buffer, size_t length, bool* address_changed, bool* link_changed) { DCHECK(buffer); - for (const struct nlmsghdr* header = - reinterpret_cast<const struct nlmsghdr*>(buffer); + for (struct nlmsghdr* header = reinterpret_cast<struct nlmsghdr*>(buffer); NLMSG_OK(header, length); header = NLMSG_NEXT(header, length)) { switch (header->nlmsg_type) { @@ -244,10 +254,20 @@ void AddressTrackerLinux::HandleMessage(const char* buffer, } return; case RTM_NEWADDR: { IPAddressNumber address; - if (GetAddress(header, &address)) { + bool really_deprecated; + if (GetAddress(header, &address, &really_deprecated)) { base::AutoLock lock(address_map_lock_); - const struct ifaddrmsg* msg = + struct ifaddrmsg* msg = reinterpret_cast<struct ifaddrmsg*>(NLMSG_DATA(header)); + // Routers may frequently (every few seconds) output the IPv6 ULA + // prefix which can cause the linux kernel to frequently output two + // back-to-back messages, one without the deprecated flag and one with + // the deprecated flag but both with preferred lifetimes of 0. Avoid + // interpretting this as an actual change by canonicalizing the two + // messages by setting the deprecated flag based on the preferred + // lifetime also. http://crbug.com/268042 + if (really_deprecated) + msg->ifa_flags |= IFA_F_DEPRECATED; // Only indicate change if the address is new or ifaddrmsg info has // changed. AddressMap::iterator it = address_map_.find(address); @@ -262,7 +282,7 @@ void AddressTrackerLinux::HandleMessage(const char* buffer, } break; case RTM_DELADDR: { IPAddressNumber address; - if (GetAddress(header, &address)) { + if (GetAddress(header, &address, NULL)) { base::AutoLock lock(address_map_lock_); if (address_map_.erase(address)) *address_changed = true; diff --git a/net/base/address_tracker_linux.h b/net/base/address_tracker_linux.h index e5ab692..0d56ee5 100644 --- a/net/base/address_tracker_linux.h +++ b/net/base/address_tracker_linux.h @@ -60,7 +60,7 @@ class NET_EXPORT_PRIVATE AddressTrackerLinux : // Sets |*address_changed| to true if |address_map_| changed, sets // |*link_changed| to true if |online_links_| changed while reading the // message from |buffer|. - void HandleMessage(const char* buffer, + void HandleMessage(char* buffer, size_t length, bool* address_changed, bool* link_changed); diff --git a/net/base/address_tracker_linux_unittest.cc b/net/base/address_tracker_linux_unittest.cc index 4708380..19ad3dd 100644 --- a/net/base/address_tracker_linux_unittest.cc +++ b/net/base/address_tracker_linux_unittest.cc @@ -23,18 +23,20 @@ class AddressTrackerLinuxTest : public testing::Test { AddressTrackerLinuxTest() : tracker_(base::Bind(&Noop), base::Bind(&Noop)) {} bool HandleAddressMessage(const Buffer& buf) { + Buffer writable_buf = buf; bool address_changed = false; bool link_changed = false; - tracker_.HandleMessage(&buf[0], buf.size(), + tracker_.HandleMessage(&writable_buf[0], buf.size(), &address_changed, &link_changed); EXPECT_FALSE(link_changed); return address_changed; } bool HandleLinkMessage(const Buffer& buf) { + Buffer writable_buf = buf; bool address_changed = false; bool link_changed = false; - tracker_.HandleMessage(&buf[0], buf.size(), + tracker_.HandleMessage(&writable_buf[0], buf.size(), &address_changed, &link_changed); EXPECT_FALSE(address_changed); return link_changed; @@ -103,12 +105,15 @@ class NetlinkMessage { Buffer buffer_; }; -void MakeAddrMessage(uint16 type, - uint8 flags, - uint8 family, - const IPAddressNumber& address, - const IPAddressNumber& local, - Buffer* output) { +#define INFINITY_LIFE_TIME 0xFFFFFFFF + +void MakeAddrMessageWithCacheInfo(uint16 type, + uint8 flags, + uint8 family, + const IPAddressNumber& address, + const IPAddressNumber& local, + uint32 preferred_lifetime, + Buffer* output) { NetlinkMessage nlmsg(type); struct ifaddrmsg msg = {}; msg.ifa_family = family; @@ -118,9 +123,23 @@ void MakeAddrMessage(uint16 type, nlmsg.AddAttribute(IFA_ADDRESS, &address[0], address.size()); if (local.size()) nlmsg.AddAttribute(IFA_LOCAL, &local[0], local.size()); + struct ifa_cacheinfo cache_info = {}; + cache_info.ifa_prefered = preferred_lifetime; + cache_info.ifa_valid = INFINITY_LIFE_TIME; + nlmsg.AddAttribute(IFA_CACHEINFO, &cache_info, sizeof(cache_info)); nlmsg.AppendTo(output); } +void MakeAddrMessage(uint16 type, + uint8 flags, + uint8 family, + const IPAddressNumber& address, + const IPAddressNumber& local, + Buffer* output) { + MakeAddrMessageWithCacheInfo(type, flags, family, address, local, + INFINITY_LIFE_TIME, output); +} + void MakeLinkMessage(uint16 type, uint32 flags, uint32 index, Buffer* output) { NetlinkMessage nlmsg(type); struct ifinfomsg msg = {}; @@ -258,6 +277,46 @@ TEST_F(AddressTrackerLinuxTest, DeleteAddress) { EXPECT_EQ(0u, map.size()); } +TEST_F(AddressTrackerLinuxTest, DeprecatedLifetime) { + const IPAddressNumber kEmpty; + const IPAddressNumber kAddr3(kAddress3, kAddress3 + arraysize(kAddress3)); + + Buffer buffer; + MakeAddrMessage(RTM_NEWADDR, 0, AF_INET6, kEmpty, kAddr3, &buffer); + EXPECT_TRUE(HandleAddressMessage(buffer)); + AddressTrackerLinux::AddressMap map = GetAddressMap(); + EXPECT_EQ(1u, map.size()); + EXPECT_EQ(1u, map.count(kAddr3)); + EXPECT_EQ(0, map[kAddr3].ifa_flags); + + // Verify 0 preferred lifetime implies deprecated. + buffer.clear(); + MakeAddrMessageWithCacheInfo(RTM_NEWADDR, 0, AF_INET6, kEmpty, kAddr3, 0, + &buffer); + EXPECT_TRUE(HandleAddressMessage(buffer)); + map = GetAddressMap(); + EXPECT_EQ(1u, map.size()); + EXPECT_EQ(IFA_F_DEPRECATED, map[kAddr3].ifa_flags); + + // Verify properly flagged message doesn't imply change. + buffer.clear(); + MakeAddrMessageWithCacheInfo(RTM_NEWADDR, IFA_F_DEPRECATED, AF_INET6, kEmpty, + kAddr3, 0, &buffer); + EXPECT_FALSE(HandleAddressMessage(buffer)); + map = GetAddressMap(); + EXPECT_EQ(1u, map.size()); + EXPECT_EQ(IFA_F_DEPRECATED, map[kAddr3].ifa_flags); + + // Verify implied deprecated doesn't imply change. + buffer.clear(); + MakeAddrMessageWithCacheInfo(RTM_NEWADDR, 0, AF_INET6, kEmpty, kAddr3, 0, + &buffer); + EXPECT_FALSE(HandleAddressMessage(buffer)); + map = GetAddressMap(); + EXPECT_EQ(1u, map.size()); + EXPECT_EQ(IFA_F_DEPRECATED, map[kAddr3].ifa_flags); +} + TEST_F(AddressTrackerLinuxTest, IgnoredMessage) { const IPAddressNumber kEmpty; const IPAddressNumber kAddr0(kAddress0, kAddress0 + arraysize(kAddress0)); |