forked from chromium/chromium
-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[net] Adds AddressTrackerLinux which keeps track of interface address…
…es 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
- Loading branch information
szym@chromium.org
committed
Jul 16, 2012
1 parent
053566a
commit 6717280
Showing
10 changed files
with
607 additions
and
240 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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_ |
Oops, something went wrong.