Roll src/third_party/WebKit 57a7fe6:7c76ab2 (svn 182675:182677)
[chromium-blink-merge.git] / net / base / address_tracker_linux.cc
blob27dd2019ab0dffd6392dee105b7ae11ba7b6e138
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "net/base/address_tracker_linux.h"
7 #include <errno.h>
8 #include <linux/if.h>
9 #include <sys/ioctl.h>
11 #include "base/logging.h"
12 #include "base/posix/eintr_wrapper.h"
13 #include "base/threading/thread_restrictions.h"
15 namespace net {
16 namespace internal {
18 namespace {
20 // Retrieves address from NETLINK address message.
21 // Sets |really_deprecated| for IPv6 addresses with preferred lifetimes of 0.
22 bool GetAddress(const struct nlmsghdr* header,
23 IPAddressNumber* out,
24 bool* really_deprecated) {
25 if (really_deprecated)
26 *really_deprecated = false;
27 const struct ifaddrmsg* msg =
28 reinterpret_cast<struct ifaddrmsg*>(NLMSG_DATA(header));
29 size_t address_length = 0;
30 switch (msg->ifa_family) {
31 case AF_INET:
32 address_length = kIPv4AddressSize;
33 break;
34 case AF_INET6:
35 address_length = kIPv6AddressSize;
36 break;
37 default:
38 // Unknown family.
39 return false;
41 // Use IFA_ADDRESS unless IFA_LOCAL is present. This behavior here is based on
42 // getaddrinfo in glibc (check_pf.c). Judging from kernel implementation of
43 // NETLINK, IPv4 addresses have only the IFA_ADDRESS attribute, while IPv6
44 // have the IFA_LOCAL attribute.
45 unsigned char* address = NULL;
46 unsigned char* local = NULL;
47 size_t length = IFA_PAYLOAD(header);
48 for (const struct rtattr* attr =
49 reinterpret_cast<const struct rtattr*>(IFA_RTA(msg));
50 RTA_OK(attr, length);
51 attr = RTA_NEXT(attr, length)) {
52 switch (attr->rta_type) {
53 case IFA_ADDRESS:
54 DCHECK_GE(RTA_PAYLOAD(attr), address_length);
55 address = reinterpret_cast<unsigned char*>(RTA_DATA(attr));
56 break;
57 case IFA_LOCAL:
58 DCHECK_GE(RTA_PAYLOAD(attr), address_length);
59 local = reinterpret_cast<unsigned char*>(RTA_DATA(attr));
60 break;
61 case IFA_CACHEINFO: {
62 const struct ifa_cacheinfo *cache_info =
63 reinterpret_cast<const struct ifa_cacheinfo*>(RTA_DATA(attr));
64 if (really_deprecated)
65 *really_deprecated = (cache_info->ifa_prefered == 0);
66 } break;
67 default:
68 break;
71 if (local)
72 address = local;
73 if (!address)
74 return false;
75 out->assign(address, address + address_length);
76 return true;
79 // Returns the name for the interface with interface index |interface_index|.
80 // The return value points to a function-scoped static so it may be changed by
81 // subsequent calls. This function could be replaced with if_indextoname() but
82 // net/if.h cannot be mixed with linux/if.h so we'll stick with exclusively
83 // talking to the kernel and not the C library.
84 const char* GetInterfaceName(int interface_index) {
85 int ioctl_socket = socket(AF_INET, SOCK_DGRAM, 0);
86 if (ioctl_socket < 0)
87 return "";
88 static struct ifreq ifr;
89 memset(&ifr, 0, sizeof(ifr));
90 ifr.ifr_ifindex = interface_index;
91 int rv = ioctl(ioctl_socket, SIOCGIFNAME, &ifr);
92 close(ioctl_socket);
93 if (rv != 0)
94 return "";
95 return ifr.ifr_name;
98 } // namespace
100 AddressTrackerLinux::AddressTrackerLinux()
101 : get_interface_name_(GetInterfaceName),
102 address_callback_(base::Bind(&base::DoNothing)),
103 link_callback_(base::Bind(&base::DoNothing)),
104 tunnel_callback_(base::Bind(&base::DoNothing)),
105 netlink_fd_(-1),
106 is_offline_(true),
107 is_offline_initialized_(false),
108 is_offline_initialized_cv_(&is_offline_lock_),
109 tracking_(false) {
112 AddressTrackerLinux::AddressTrackerLinux(const base::Closure& address_callback,
113 const base::Closure& link_callback,
114 const base::Closure& tunnel_callback)
115 : get_interface_name_(GetInterfaceName),
116 address_callback_(address_callback),
117 link_callback_(link_callback),
118 tunnel_callback_(tunnel_callback),
119 netlink_fd_(-1),
120 is_offline_(true),
121 is_offline_initialized_(false),
122 is_offline_initialized_cv_(&is_offline_lock_),
123 tracking_(true) {
124 DCHECK(!address_callback.is_null());
125 DCHECK(!link_callback.is_null());
128 AddressTrackerLinux::~AddressTrackerLinux() {
129 CloseSocket();
132 void AddressTrackerLinux::Init() {
133 netlink_fd_ = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
134 if (netlink_fd_ < 0) {
135 PLOG(ERROR) << "Could not create NETLINK socket";
136 AbortAndForceOnline();
137 return;
140 int rv;
142 if (tracking_) {
143 // Request notifications.
144 struct sockaddr_nl addr = {};
145 addr.nl_family = AF_NETLINK;
146 addr.nl_pid = getpid();
147 // TODO(szym): Track RTMGRP_LINK as well for ifi_type,
148 // http://crbug.com/113993
149 addr.nl_groups =
150 RTMGRP_IPV4_IFADDR | RTMGRP_IPV6_IFADDR | RTMGRP_NOTIFY | RTMGRP_LINK;
151 rv = bind(
152 netlink_fd_, reinterpret_cast<struct sockaddr*>(&addr), sizeof(addr));
153 if (rv < 0) {
154 PLOG(ERROR) << "Could not bind NETLINK socket";
155 AbortAndForceOnline();
156 return;
160 // Request dump of addresses.
161 struct sockaddr_nl peer = {};
162 peer.nl_family = AF_NETLINK;
164 struct {
165 struct nlmsghdr header;
166 struct rtgenmsg msg;
167 } request = {};
169 request.header.nlmsg_len = NLMSG_LENGTH(sizeof(request.msg));
170 request.header.nlmsg_type = RTM_GETADDR;
171 request.header.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
172 request.header.nlmsg_pid = getpid();
173 request.msg.rtgen_family = AF_UNSPEC;
175 rv = HANDLE_EINTR(sendto(netlink_fd_, &request, request.header.nlmsg_len,
176 0, reinterpret_cast<struct sockaddr*>(&peer),
177 sizeof(peer)));
178 if (rv < 0) {
179 PLOG(ERROR) << "Could not send NETLINK request";
180 AbortAndForceOnline();
181 return;
184 // Consume pending message to populate the AddressMap, but don't notify.
185 // Sending another request without first reading responses results in EBUSY.
186 bool address_changed;
187 bool link_changed;
188 bool tunnel_changed;
189 ReadMessages(&address_changed, &link_changed, &tunnel_changed);
191 // Request dump of link state
192 request.header.nlmsg_type = RTM_GETLINK;
194 rv = HANDLE_EINTR(sendto(netlink_fd_, &request, request.header.nlmsg_len, 0,
195 reinterpret_cast<struct sockaddr*>(&peer),
196 sizeof(peer)));
197 if (rv < 0) {
198 PLOG(ERROR) << "Could not send NETLINK request";
199 AbortAndForceOnline();
200 return;
203 // Consume pending message to populate links_online_, but don't notify.
204 ReadMessages(&address_changed, &link_changed, &tunnel_changed);
206 AddressTrackerAutoLock lock(*this, is_offline_lock_);
207 is_offline_initialized_ = true;
208 is_offline_initialized_cv_.Signal();
211 if (tracking_) {
212 rv = base::MessageLoopForIO::current()->WatchFileDescriptor(
213 netlink_fd_, true, base::MessageLoopForIO::WATCH_READ, &watcher_, this);
214 if (rv < 0) {
215 PLOG(ERROR) << "Could not watch NETLINK socket";
216 AbortAndForceOnline();
217 return;
222 void AddressTrackerLinux::AbortAndForceOnline() {
223 CloseSocket();
224 AddressTrackerAutoLock lock(*this, is_offline_lock_);
225 is_offline_ = false;
226 is_offline_initialized_ = true;
227 is_offline_initialized_cv_.Signal();
230 AddressTrackerLinux::AddressMap AddressTrackerLinux::GetAddressMap() const {
231 AddressTrackerAutoLock lock(*this, address_map_lock_);
232 return address_map_;
235 base::hash_set<int> AddressTrackerLinux::GetOnlineLinks() const {
236 AddressTrackerAutoLock lock(*this, online_links_lock_);
237 return online_links_;
240 NetworkChangeNotifier::ConnectionType
241 AddressTrackerLinux::GetCurrentConnectionType() {
242 // http://crbug.com/125097
243 base::ThreadRestrictions::ScopedAllowWait allow_wait;
244 AddressTrackerAutoLock lock(*this, is_offline_lock_);
245 // Make sure the initial offline state is set before returning.
246 while (!is_offline_initialized_) {
247 is_offline_initialized_cv_.Wait();
249 // TODO(droger): Return something more detailed than CONNECTION_UNKNOWN.
250 // http://crbug.com/160537
251 return is_offline_ ? NetworkChangeNotifier::CONNECTION_NONE :
252 NetworkChangeNotifier::CONNECTION_UNKNOWN;
255 void AddressTrackerLinux::ReadMessages(bool* address_changed,
256 bool* link_changed,
257 bool* tunnel_changed) {
258 *address_changed = false;
259 *link_changed = false;
260 *tunnel_changed = false;
261 char buffer[4096];
262 bool first_loop = true;
263 for (;;) {
264 int rv = HANDLE_EINTR(recv(netlink_fd_,
265 buffer,
266 sizeof(buffer),
267 // Block the first time through loop.
268 first_loop ? 0 : MSG_DONTWAIT));
269 first_loop = false;
270 if (rv == 0) {
271 LOG(ERROR) << "Unexpected shutdown of NETLINK socket.";
272 return;
274 if (rv < 0) {
275 if ((errno == EAGAIN) || (errno == EWOULDBLOCK))
276 break;
277 PLOG(ERROR) << "Failed to recv from netlink socket";
278 return;
280 HandleMessage(buffer, rv, address_changed, link_changed, tunnel_changed);
282 if (*link_changed) {
283 bool is_offline;
285 AddressTrackerAutoLock lock(*this, online_links_lock_);
286 is_offline = online_links_.empty();
288 AddressTrackerAutoLock lock(*this, is_offline_lock_);
289 is_offline_ = is_offline;
293 void AddressTrackerLinux::HandleMessage(char* buffer,
294 size_t length,
295 bool* address_changed,
296 bool* link_changed,
297 bool* tunnel_changed) {
298 DCHECK(buffer);
299 for (struct nlmsghdr* header = reinterpret_cast<struct nlmsghdr*>(buffer);
300 NLMSG_OK(header, length);
301 header = NLMSG_NEXT(header, length)) {
302 switch (header->nlmsg_type) {
303 case NLMSG_DONE:
304 return;
305 case NLMSG_ERROR: {
306 const struct nlmsgerr* msg =
307 reinterpret_cast<struct nlmsgerr*>(NLMSG_DATA(header));
308 LOG(ERROR) << "Unexpected netlink error " << msg->error << ".";
309 } return;
310 case RTM_NEWADDR: {
311 IPAddressNumber address;
312 bool really_deprecated;
313 if (GetAddress(header, &address, &really_deprecated)) {
314 AddressTrackerAutoLock lock(*this, address_map_lock_);
315 struct ifaddrmsg* msg =
316 reinterpret_cast<struct ifaddrmsg*>(NLMSG_DATA(header));
317 // Routers may frequently (every few seconds) output the IPv6 ULA
318 // prefix which can cause the linux kernel to frequently output two
319 // back-to-back messages, one without the deprecated flag and one with
320 // the deprecated flag but both with preferred lifetimes of 0. Avoid
321 // interpretting this as an actual change by canonicalizing the two
322 // messages by setting the deprecated flag based on the preferred
323 // lifetime also. http://crbug.com/268042
324 if (really_deprecated)
325 msg->ifa_flags |= IFA_F_DEPRECATED;
326 // Only indicate change if the address is new or ifaddrmsg info has
327 // changed.
328 AddressMap::iterator it = address_map_.find(address);
329 if (it == address_map_.end()) {
330 address_map_.insert(it, std::make_pair(address, *msg));
331 *address_changed = true;
332 } else if (memcmp(&it->second, msg, sizeof(*msg))) {
333 it->second = *msg;
334 *address_changed = true;
337 } break;
338 case RTM_DELADDR: {
339 IPAddressNumber address;
340 if (GetAddress(header, &address, NULL)) {
341 AddressTrackerAutoLock lock(*this, address_map_lock_);
342 if (address_map_.erase(address))
343 *address_changed = true;
345 } break;
346 case RTM_NEWLINK: {
347 const struct ifinfomsg* msg =
348 reinterpret_cast<struct ifinfomsg*>(NLMSG_DATA(header));
349 if (!(msg->ifi_flags & IFF_LOOPBACK) && (msg->ifi_flags & IFF_UP) &&
350 (msg->ifi_flags & IFF_LOWER_UP) && (msg->ifi_flags & IFF_RUNNING)) {
351 AddressTrackerAutoLock lock(*this, online_links_lock_);
352 if (online_links_.insert(msg->ifi_index).second) {
353 *link_changed = true;
354 if (IsTunnelInterface(msg))
355 *tunnel_changed = true;
357 } else {
358 AddressTrackerAutoLock lock(*this, online_links_lock_);
359 if (online_links_.erase(msg->ifi_index)) {
360 *link_changed = true;
361 if (IsTunnelInterface(msg))
362 *tunnel_changed = true;
365 } break;
366 case RTM_DELLINK: {
367 const struct ifinfomsg* msg =
368 reinterpret_cast<struct ifinfomsg*>(NLMSG_DATA(header));
369 AddressTrackerAutoLock lock(*this, online_links_lock_);
370 if (online_links_.erase(msg->ifi_index)) {
371 *link_changed = true;
372 if (IsTunnelInterface(msg))
373 *tunnel_changed = true;
375 } break;
376 default:
377 break;
382 void AddressTrackerLinux::OnFileCanReadWithoutBlocking(int fd) {
383 DCHECK_EQ(netlink_fd_, fd);
384 bool address_changed;
385 bool link_changed;
386 bool tunnel_changed;
387 ReadMessages(&address_changed, &link_changed, &tunnel_changed);
388 if (address_changed)
389 address_callback_.Run();
390 if (link_changed)
391 link_callback_.Run();
392 if (tunnel_changed)
393 tunnel_callback_.Run();
396 void AddressTrackerLinux::OnFileCanWriteWithoutBlocking(int /* fd */) {}
398 void AddressTrackerLinux::CloseSocket() {
399 if (netlink_fd_ >= 0 && IGNORE_EINTR(close(netlink_fd_)) < 0)
400 PLOG(ERROR) << "Could not close NETLINK socket.";
401 netlink_fd_ = -1;
404 bool AddressTrackerLinux::IsTunnelInterface(const struct ifinfomsg* msg) const {
405 // Linux kernel drivers/net/tun.c uses "tun" name prefix.
406 return strncmp(get_interface_name_(msg->ifi_index), "tun", 3) == 0;
409 AddressTrackerLinux::AddressTrackerAutoLock::AddressTrackerAutoLock(
410 const AddressTrackerLinux& tracker,
411 base::Lock& lock)
412 : tracker_(tracker), lock_(lock) {
413 if (tracker_.tracking_) {
414 lock_.Acquire();
415 } else {
416 DCHECK(tracker_.thread_checker_.CalledOnValidThread());
420 AddressTrackerLinux::AddressTrackerAutoLock::~AddressTrackerAutoLock() {
421 if (tracker_.tracking_) {
422 lock_.AssertAcquired();
423 lock_.Release();
427 } // namespace internal
428 } // namespace net