Pin Chrome's shortcut to the Win10 Start menu on install and OS upgrade.
[chromium-blink-merge.git] / net / base / address_tracker_linux.cc
blob58a122f5c2e410b849d001659bf0f39c2f83facf
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/files/scoped_file.h"
12 #include "base/logging.h"
13 #include "base/posix/eintr_wrapper.h"
14 #include "base/threading/thread_restrictions.h"
15 #include "net/base/network_interfaces_linux.h"
16 #include "net/base/ip_address_number.h"
18 namespace net {
19 namespace internal {
21 namespace {
23 // Some kernel functions such as wireless_send_event and rtnetlink_ifinfo_prep
24 // may send spurious messages over rtnetlink. RTM_NEWLINK messages where
25 // ifi_change == 0 and rta_type == IFLA_WIRELESS should be ignored.
26 bool IgnoreWirelessChange(const struct nlmsghdr* header,
27 const struct ifinfomsg* msg) {
28 size_t length = IFLA_PAYLOAD(header);
29 for (const struct rtattr* attr = IFLA_RTA(msg); RTA_OK(attr, length);
30 attr = RTA_NEXT(attr, length)) {
31 if (attr->rta_type == IFLA_WIRELESS && msg->ifi_change == 0)
32 return true;
34 return false;
37 // Retrieves address from NETLINK address message.
38 // Sets |really_deprecated| for IPv6 addresses with preferred lifetimes of 0.
39 bool GetAddress(const struct nlmsghdr* header,
40 IPAddressNumber* out,
41 bool* really_deprecated) {
42 if (really_deprecated)
43 *really_deprecated = false;
44 const struct ifaddrmsg* msg =
45 reinterpret_cast<struct ifaddrmsg*>(NLMSG_DATA(header));
46 size_t address_length = 0;
47 switch (msg->ifa_family) {
48 case AF_INET:
49 address_length = kIPv4AddressSize;
50 break;
51 case AF_INET6:
52 address_length = kIPv6AddressSize;
53 break;
54 default:
55 // Unknown family.
56 return false;
58 // Use IFA_ADDRESS unless IFA_LOCAL is present. This behavior here is based on
59 // getaddrinfo in glibc (check_pf.c). Judging from kernel implementation of
60 // NETLINK, IPv4 addresses have only the IFA_ADDRESS attribute, while IPv6
61 // have the IFA_LOCAL attribute.
62 unsigned char* address = NULL;
63 unsigned char* local = NULL;
64 size_t length = IFA_PAYLOAD(header);
65 for (const struct rtattr* attr =
66 reinterpret_cast<const struct rtattr*>(IFA_RTA(msg));
67 RTA_OK(attr, length);
68 attr = RTA_NEXT(attr, length)) {
69 switch (attr->rta_type) {
70 case IFA_ADDRESS:
71 DCHECK_GE(RTA_PAYLOAD(attr), address_length);
72 address = reinterpret_cast<unsigned char*>(RTA_DATA(attr));
73 break;
74 case IFA_LOCAL:
75 DCHECK_GE(RTA_PAYLOAD(attr), address_length);
76 local = reinterpret_cast<unsigned char*>(RTA_DATA(attr));
77 break;
78 case IFA_CACHEINFO: {
79 const struct ifa_cacheinfo *cache_info =
80 reinterpret_cast<const struct ifa_cacheinfo*>(RTA_DATA(attr));
81 if (really_deprecated)
82 *really_deprecated = (cache_info->ifa_prefered == 0);
83 } break;
84 default:
85 break;
88 if (local)
89 address = local;
90 if (!address)
91 return false;
92 out->assign(address, address + address_length);
93 return true;
96 } // namespace
98 // static
99 char* AddressTrackerLinux::GetInterfaceName(int interface_index, char* buf) {
100 memset(buf, 0, IFNAMSIZ);
101 base::ScopedFD ioctl_socket(socket(AF_INET, SOCK_DGRAM, 0));
102 if (!ioctl_socket.is_valid())
103 return buf;
105 struct ifreq ifr = {};
106 ifr.ifr_ifindex = interface_index;
108 if (ioctl(ioctl_socket.get(), SIOCGIFNAME, &ifr) == 0)
109 strncpy(buf, ifr.ifr_name, IFNAMSIZ - 1);
110 return buf;
113 AddressTrackerLinux::AddressTrackerLinux()
114 : get_interface_name_(GetInterfaceName),
115 address_callback_(base::Bind(&base::DoNothing)),
116 link_callback_(base::Bind(&base::DoNothing)),
117 tunnel_callback_(base::Bind(&base::DoNothing)),
118 netlink_fd_(-1),
119 connection_type_initialized_(false),
120 connection_type_initialized_cv_(&connection_type_lock_),
121 current_connection_type_(NetworkChangeNotifier::CONNECTION_NONE),
122 tracking_(false) {
125 AddressTrackerLinux::AddressTrackerLinux(
126 const base::Closure& address_callback,
127 const base::Closure& link_callback,
128 const base::Closure& tunnel_callback,
129 const base::hash_set<std::string>& ignored_interfaces)
130 : get_interface_name_(GetInterfaceName),
131 address_callback_(address_callback),
132 link_callback_(link_callback),
133 tunnel_callback_(tunnel_callback),
134 netlink_fd_(-1),
135 ignored_interfaces_(ignored_interfaces),
136 connection_type_initialized_(false),
137 connection_type_initialized_cv_(&connection_type_lock_),
138 current_connection_type_(NetworkChangeNotifier::CONNECTION_NONE),
139 tracking_(true) {
140 DCHECK(!address_callback.is_null());
141 DCHECK(!link_callback.is_null());
144 AddressTrackerLinux::~AddressTrackerLinux() {
145 CloseSocket();
148 void AddressTrackerLinux::Init() {
149 netlink_fd_ = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
150 if (netlink_fd_ < 0) {
151 PLOG(ERROR) << "Could not create NETLINK socket";
152 AbortAndForceOnline();
153 return;
156 int rv;
158 if (tracking_) {
159 // Request notifications.
160 struct sockaddr_nl addr = {};
161 addr.nl_family = AF_NETLINK;
162 addr.nl_pid = getpid();
163 // TODO(szym): Track RTMGRP_LINK as well for ifi_type,
164 // http://crbug.com/113993
165 addr.nl_groups =
166 RTMGRP_IPV4_IFADDR | RTMGRP_IPV6_IFADDR | RTMGRP_NOTIFY | RTMGRP_LINK;
167 rv = bind(
168 netlink_fd_, reinterpret_cast<struct sockaddr*>(&addr), sizeof(addr));
169 if (rv < 0) {
170 PLOG(ERROR) << "Could not bind NETLINK socket";
171 AbortAndForceOnline();
172 return;
176 // Request dump of addresses.
177 struct sockaddr_nl peer = {};
178 peer.nl_family = AF_NETLINK;
180 struct {
181 struct nlmsghdr header;
182 struct rtgenmsg msg;
183 } request = {};
185 request.header.nlmsg_len = NLMSG_LENGTH(sizeof(request.msg));
186 request.header.nlmsg_type = RTM_GETADDR;
187 request.header.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
188 request.header.nlmsg_pid = getpid();
189 request.msg.rtgen_family = AF_UNSPEC;
191 rv = HANDLE_EINTR(sendto(netlink_fd_, &request, request.header.nlmsg_len,
192 0, reinterpret_cast<struct sockaddr*>(&peer),
193 sizeof(peer)));
194 if (rv < 0) {
195 PLOG(ERROR) << "Could not send NETLINK request";
196 AbortAndForceOnline();
197 return;
200 // Consume pending message to populate the AddressMap, but don't notify.
201 // Sending another request without first reading responses results in EBUSY.
202 bool address_changed;
203 bool link_changed;
204 bool tunnel_changed;
205 ReadMessages(&address_changed, &link_changed, &tunnel_changed);
207 // Request dump of link state
208 request.header.nlmsg_type = RTM_GETLINK;
210 rv = HANDLE_EINTR(sendto(netlink_fd_, &request, request.header.nlmsg_len, 0,
211 reinterpret_cast<struct sockaddr*>(&peer),
212 sizeof(peer)));
213 if (rv < 0) {
214 PLOG(ERROR) << "Could not send NETLINK request";
215 AbortAndForceOnline();
216 return;
219 // Consume pending message to populate links_online_, but don't notify.
220 ReadMessages(&address_changed, &link_changed, &tunnel_changed);
222 AddressTrackerAutoLock lock(*this, connection_type_lock_);
223 connection_type_initialized_ = true;
224 connection_type_initialized_cv_.Signal();
227 if (tracking_) {
228 rv = base::MessageLoopForIO::current()->WatchFileDescriptor(
229 netlink_fd_, true, base::MessageLoopForIO::WATCH_READ, &watcher_, this);
230 if (rv < 0) {
231 PLOG(ERROR) << "Could not watch NETLINK socket";
232 AbortAndForceOnline();
233 return;
238 void AddressTrackerLinux::AbortAndForceOnline() {
239 CloseSocket();
240 AddressTrackerAutoLock lock(*this, connection_type_lock_);
241 current_connection_type_ = NetworkChangeNotifier::CONNECTION_UNKNOWN;
242 connection_type_initialized_ = true;
243 connection_type_initialized_cv_.Signal();
246 AddressTrackerLinux::AddressMap AddressTrackerLinux::GetAddressMap() const {
247 AddressTrackerAutoLock lock(*this, address_map_lock_);
248 return address_map_;
251 base::hash_set<int> AddressTrackerLinux::GetOnlineLinks() const {
252 AddressTrackerAutoLock lock(*this, online_links_lock_);
253 return online_links_;
256 bool AddressTrackerLinux::IsInterfaceIgnored(int interface_index) const {
257 if (ignored_interfaces_.empty())
258 return false;
260 char buf[IFNAMSIZ] = {0};
261 const char* interface_name = get_interface_name_(interface_index, buf);
262 return ignored_interfaces_.find(interface_name) != ignored_interfaces_.end();
265 NetworkChangeNotifier::ConnectionType
266 AddressTrackerLinux::GetCurrentConnectionType() {
267 // http://crbug.com/125097
268 base::ThreadRestrictions::ScopedAllowWait allow_wait;
269 AddressTrackerAutoLock lock(*this, connection_type_lock_);
270 // Make sure the initial connection type is set before returning.
271 while (!connection_type_initialized_) {
272 connection_type_initialized_cv_.Wait();
274 return current_connection_type_;
277 void AddressTrackerLinux::ReadMessages(bool* address_changed,
278 bool* link_changed,
279 bool* tunnel_changed) {
280 *address_changed = false;
281 *link_changed = false;
282 *tunnel_changed = false;
283 char buffer[4096];
284 bool first_loop = true;
285 for (;;) {
286 int rv = HANDLE_EINTR(recv(netlink_fd_,
287 buffer,
288 sizeof(buffer),
289 // Block the first time through loop.
290 first_loop ? 0 : MSG_DONTWAIT));
291 first_loop = false;
292 if (rv == 0) {
293 LOG(ERROR) << "Unexpected shutdown of NETLINK socket.";
294 return;
296 if (rv < 0) {
297 if ((errno == EAGAIN) || (errno == EWOULDBLOCK))
298 break;
299 PLOG(ERROR) << "Failed to recv from netlink socket";
300 return;
302 HandleMessage(buffer, rv, address_changed, link_changed, tunnel_changed);
304 if (*link_changed || *address_changed)
305 UpdateCurrentConnectionType();
308 void AddressTrackerLinux::HandleMessage(char* buffer,
309 size_t length,
310 bool* address_changed,
311 bool* link_changed,
312 bool* tunnel_changed) {
313 DCHECK(buffer);
314 for (struct nlmsghdr* header = reinterpret_cast<struct nlmsghdr*>(buffer);
315 NLMSG_OK(header, length);
316 header = NLMSG_NEXT(header, length)) {
317 switch (header->nlmsg_type) {
318 case NLMSG_DONE:
319 return;
320 case NLMSG_ERROR: {
321 const struct nlmsgerr* msg =
322 reinterpret_cast<struct nlmsgerr*>(NLMSG_DATA(header));
323 LOG(ERROR) << "Unexpected netlink error " << msg->error << ".";
324 } return;
325 case RTM_NEWADDR: {
326 IPAddressNumber address;
327 bool really_deprecated;
328 struct ifaddrmsg* msg =
329 reinterpret_cast<struct ifaddrmsg*>(NLMSG_DATA(header));
330 if (IsInterfaceIgnored(msg->ifa_index))
331 break;
332 if (GetAddress(header, &address, &really_deprecated)) {
333 AddressTrackerAutoLock lock(*this, address_map_lock_);
334 // Routers may frequently (every few seconds) output the IPv6 ULA
335 // prefix which can cause the linux kernel to frequently output two
336 // back-to-back messages, one without the deprecated flag and one with
337 // the deprecated flag but both with preferred lifetimes of 0. Avoid
338 // interpretting this as an actual change by canonicalizing the two
339 // messages by setting the deprecated flag based on the preferred
340 // lifetime also. http://crbug.com/268042
341 if (really_deprecated)
342 msg->ifa_flags |= IFA_F_DEPRECATED;
343 // Only indicate change if the address is new or ifaddrmsg info has
344 // changed.
345 AddressMap::iterator it = address_map_.find(address);
346 if (it == address_map_.end()) {
347 address_map_.insert(it, std::make_pair(address, *msg));
348 *address_changed = true;
349 } else if (memcmp(&it->second, msg, sizeof(*msg))) {
350 it->second = *msg;
351 *address_changed = true;
354 } break;
355 case RTM_DELADDR: {
356 IPAddressNumber address;
357 const struct ifaddrmsg* msg =
358 reinterpret_cast<struct ifaddrmsg*>(NLMSG_DATA(header));
359 if (IsInterfaceIgnored(msg->ifa_index))
360 break;
361 if (GetAddress(header, &address, NULL)) {
362 AddressTrackerAutoLock lock(*this, address_map_lock_);
363 if (address_map_.erase(address))
364 *address_changed = true;
366 } break;
367 case RTM_NEWLINK: {
368 const struct ifinfomsg* msg =
369 reinterpret_cast<struct ifinfomsg*>(NLMSG_DATA(header));
370 if (IsInterfaceIgnored(msg->ifi_index))
371 break;
372 if (IgnoreWirelessChange(header, msg)) {
373 VLOG(2) << "Ignoring RTM_NEWLINK message";
374 break;
376 if (!(msg->ifi_flags & IFF_LOOPBACK) && (msg->ifi_flags & IFF_UP) &&
377 (msg->ifi_flags & IFF_LOWER_UP) && (msg->ifi_flags & IFF_RUNNING)) {
378 AddressTrackerAutoLock lock(*this, online_links_lock_);
379 if (online_links_.insert(msg->ifi_index).second) {
380 *link_changed = true;
381 if (IsTunnelInterface(msg->ifi_index))
382 *tunnel_changed = true;
384 } else {
385 AddressTrackerAutoLock lock(*this, online_links_lock_);
386 if (online_links_.erase(msg->ifi_index)) {
387 *link_changed = true;
388 if (IsTunnelInterface(msg->ifi_index))
389 *tunnel_changed = true;
392 } break;
393 case RTM_DELLINK: {
394 const struct ifinfomsg* msg =
395 reinterpret_cast<struct ifinfomsg*>(NLMSG_DATA(header));
396 if (IsInterfaceIgnored(msg->ifi_index))
397 break;
398 AddressTrackerAutoLock lock(*this, online_links_lock_);
399 if (online_links_.erase(msg->ifi_index)) {
400 *link_changed = true;
401 if (IsTunnelInterface(msg->ifi_index))
402 *tunnel_changed = true;
404 } break;
405 default:
406 break;
411 void AddressTrackerLinux::OnFileCanReadWithoutBlocking(int fd) {
412 DCHECK_EQ(netlink_fd_, fd);
413 bool address_changed;
414 bool link_changed;
415 bool tunnel_changed;
416 ReadMessages(&address_changed, &link_changed, &tunnel_changed);
417 if (address_changed)
418 address_callback_.Run();
419 if (link_changed)
420 link_callback_.Run();
421 if (tunnel_changed)
422 tunnel_callback_.Run();
425 void AddressTrackerLinux::OnFileCanWriteWithoutBlocking(int /* fd */) {}
427 void AddressTrackerLinux::CloseSocket() {
428 if (netlink_fd_ >= 0 && IGNORE_EINTR(close(netlink_fd_)) < 0)
429 PLOG(ERROR) << "Could not close NETLINK socket.";
430 netlink_fd_ = -1;
433 bool AddressTrackerLinux::IsTunnelInterface(int interface_index) const {
434 // Linux kernel drivers/net/tun.c uses "tun" name prefix.
435 char buf[IFNAMSIZ] = {0};
436 return strncmp(get_interface_name_(interface_index, buf), "tun", 3) == 0;
439 void AddressTrackerLinux::UpdateCurrentConnectionType() {
440 AddressTrackerLinux::AddressMap address_map = GetAddressMap();
441 base::hash_set<int> online_links = GetOnlineLinks();
443 // Strip out tunnel interfaces from online_links
444 for (base::hash_set<int>::const_iterator it = online_links.begin();
445 it != online_links.end();) {
446 if (IsTunnelInterface(*it)) {
447 base::hash_set<int>::const_iterator tunnel_it = it;
448 ++it;
449 online_links.erase(*tunnel_it);
450 } else {
451 ++it;
455 NetworkInterfaceList networks;
456 NetworkChangeNotifier::ConnectionType type =
457 NetworkChangeNotifier::CONNECTION_NONE;
458 if (GetNetworkListImpl(&networks, 0, online_links, address_map,
459 get_interface_name_)) {
460 type = NetworkChangeNotifier::ConnectionTypeFromInterfaceList(networks);
461 } else {
462 type = online_links.empty() ? NetworkChangeNotifier::CONNECTION_NONE
463 : NetworkChangeNotifier::CONNECTION_UNKNOWN;
466 AddressTrackerAutoLock lock(*this, connection_type_lock_);
467 current_connection_type_ = type;
470 AddressTrackerLinux::AddressTrackerAutoLock::AddressTrackerAutoLock(
471 const AddressTrackerLinux& tracker,
472 base::Lock& lock)
473 : tracker_(tracker), lock_(lock) {
474 if (tracker_.tracking_) {
475 lock_.Acquire();
476 } else {
477 DCHECK(tracker_.thread_checker_.CalledOnValidThread());
481 AddressTrackerLinux::AddressTrackerAutoLock::~AddressTrackerAutoLock() {
482 if (tracker_.tracking_) {
483 lock_.AssertAcquired();
484 lock_.Release();
488 } // namespace internal
489 } // namespace net