PlzNavigate: Improvements to RFHM commit logic.
[chromium-blink-merge.git] / net / base / address_tracker_linux.cc
blob731bf4f97785ebf45837c35a4fe4033bd4ffffff
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/net_util_linux.h"
17 namespace net {
18 namespace internal {
20 namespace {
22 // Retrieves address from NETLINK address message.
23 // Sets |really_deprecated| for IPv6 addresses with preferred lifetimes of 0.
24 bool GetAddress(const struct nlmsghdr* header,
25 IPAddressNumber* out,
26 bool* really_deprecated) {
27 if (really_deprecated)
28 *really_deprecated = false;
29 const struct ifaddrmsg* msg =
30 reinterpret_cast<struct ifaddrmsg*>(NLMSG_DATA(header));
31 size_t address_length = 0;
32 switch (msg->ifa_family) {
33 case AF_INET:
34 address_length = kIPv4AddressSize;
35 break;
36 case AF_INET6:
37 address_length = kIPv6AddressSize;
38 break;
39 default:
40 // Unknown family.
41 return false;
43 // Use IFA_ADDRESS unless IFA_LOCAL is present. This behavior here is based on
44 // getaddrinfo in glibc (check_pf.c). Judging from kernel implementation of
45 // NETLINK, IPv4 addresses have only the IFA_ADDRESS attribute, while IPv6
46 // have the IFA_LOCAL attribute.
47 unsigned char* address = NULL;
48 unsigned char* local = NULL;
49 size_t length = IFA_PAYLOAD(header);
50 for (const struct rtattr* attr =
51 reinterpret_cast<const struct rtattr*>(IFA_RTA(msg));
52 RTA_OK(attr, length);
53 attr = RTA_NEXT(attr, length)) {
54 switch (attr->rta_type) {
55 case IFA_ADDRESS:
56 DCHECK_GE(RTA_PAYLOAD(attr), address_length);
57 address = reinterpret_cast<unsigned char*>(RTA_DATA(attr));
58 break;
59 case IFA_LOCAL:
60 DCHECK_GE(RTA_PAYLOAD(attr), address_length);
61 local = reinterpret_cast<unsigned char*>(RTA_DATA(attr));
62 break;
63 case IFA_CACHEINFO: {
64 const struct ifa_cacheinfo *cache_info =
65 reinterpret_cast<const struct ifa_cacheinfo*>(RTA_DATA(attr));
66 if (really_deprecated)
67 *really_deprecated = (cache_info->ifa_prefered == 0);
68 } break;
69 default:
70 break;
73 if (local)
74 address = local;
75 if (!address)
76 return false;
77 out->assign(address, address + address_length);
78 return true;
81 } // namespace
83 // static
84 char* AddressTrackerLinux::GetInterfaceName(int interface_index, char* buf) {
85 memset(buf, 0, IFNAMSIZ);
86 base::ScopedFD ioctl_socket(socket(AF_INET, SOCK_DGRAM, 0));
87 if (!ioctl_socket.is_valid())
88 return buf;
90 struct ifreq ifr = {};
91 ifr.ifr_ifindex = interface_index;
93 if (ioctl(ioctl_socket.get(), SIOCGIFNAME, &ifr) == 0)
94 strncpy(buf, ifr.ifr_name, IFNAMSIZ - 1);
95 return buf;
98 AddressTrackerLinux::AddressTrackerLinux()
99 : get_interface_name_(GetInterfaceName),
100 address_callback_(base::Bind(&base::DoNothing)),
101 link_callback_(base::Bind(&base::DoNothing)),
102 tunnel_callback_(base::Bind(&base::DoNothing)),
103 netlink_fd_(-1),
104 connection_type_initialized_(false),
105 connection_type_initialized_cv_(&connection_type_lock_),
106 current_connection_type_(NetworkChangeNotifier::CONNECTION_NONE),
107 tracking_(false) {
110 AddressTrackerLinux::AddressTrackerLinux(const base::Closure& address_callback,
111 const base::Closure& link_callback,
112 const base::Closure& tunnel_callback)
113 : get_interface_name_(GetInterfaceName),
114 address_callback_(address_callback),
115 link_callback_(link_callback),
116 tunnel_callback_(tunnel_callback),
117 netlink_fd_(-1),
118 connection_type_initialized_(false),
119 connection_type_initialized_cv_(&connection_type_lock_),
120 current_connection_type_(NetworkChangeNotifier::CONNECTION_NONE),
121 tracking_(true) {
122 DCHECK(!address_callback.is_null());
123 DCHECK(!link_callback.is_null());
126 AddressTrackerLinux::~AddressTrackerLinux() {
127 CloseSocket();
130 void AddressTrackerLinux::Init() {
131 netlink_fd_ = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
132 if (netlink_fd_ < 0) {
133 PLOG(ERROR) << "Could not create NETLINK socket";
134 AbortAndForceOnline();
135 return;
138 int rv;
140 if (tracking_) {
141 // Request notifications.
142 struct sockaddr_nl addr = {};
143 addr.nl_family = AF_NETLINK;
144 addr.nl_pid = getpid();
145 // TODO(szym): Track RTMGRP_LINK as well for ifi_type,
146 // http://crbug.com/113993
147 addr.nl_groups =
148 RTMGRP_IPV4_IFADDR | RTMGRP_IPV6_IFADDR | RTMGRP_NOTIFY | RTMGRP_LINK;
149 rv = bind(
150 netlink_fd_, reinterpret_cast<struct sockaddr*>(&addr), sizeof(addr));
151 if (rv < 0) {
152 PLOG(ERROR) << "Could not bind NETLINK socket";
153 AbortAndForceOnline();
154 return;
158 // Request dump of addresses.
159 struct sockaddr_nl peer = {};
160 peer.nl_family = AF_NETLINK;
162 struct {
163 struct nlmsghdr header;
164 struct rtgenmsg msg;
165 } request = {};
167 request.header.nlmsg_len = NLMSG_LENGTH(sizeof(request.msg));
168 request.header.nlmsg_type = RTM_GETADDR;
169 request.header.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
170 request.header.nlmsg_pid = getpid();
171 request.msg.rtgen_family = AF_UNSPEC;
173 rv = HANDLE_EINTR(sendto(netlink_fd_, &request, request.header.nlmsg_len,
174 0, reinterpret_cast<struct sockaddr*>(&peer),
175 sizeof(peer)));
176 if (rv < 0) {
177 PLOG(ERROR) << "Could not send NETLINK request";
178 AbortAndForceOnline();
179 return;
182 // Consume pending message to populate the AddressMap, but don't notify.
183 // Sending another request without first reading responses results in EBUSY.
184 bool address_changed;
185 bool link_changed;
186 bool tunnel_changed;
187 ReadMessages(&address_changed, &link_changed, &tunnel_changed);
189 // Request dump of link state
190 request.header.nlmsg_type = RTM_GETLINK;
192 rv = HANDLE_EINTR(sendto(netlink_fd_, &request, request.header.nlmsg_len, 0,
193 reinterpret_cast<struct sockaddr*>(&peer),
194 sizeof(peer)));
195 if (rv < 0) {
196 PLOG(ERROR) << "Could not send NETLINK request";
197 AbortAndForceOnline();
198 return;
201 // Consume pending message to populate links_online_, but don't notify.
202 ReadMessages(&address_changed, &link_changed, &tunnel_changed);
204 AddressTrackerAutoLock lock(*this, connection_type_lock_);
205 connection_type_initialized_ = true;
206 connection_type_initialized_cv_.Signal();
209 if (tracking_) {
210 rv = base::MessageLoopForIO::current()->WatchFileDescriptor(
211 netlink_fd_, true, base::MessageLoopForIO::WATCH_READ, &watcher_, this);
212 if (rv < 0) {
213 PLOG(ERROR) << "Could not watch NETLINK socket";
214 AbortAndForceOnline();
215 return;
220 void AddressTrackerLinux::AbortAndForceOnline() {
221 CloseSocket();
222 AddressTrackerAutoLock lock(*this, connection_type_lock_);
223 current_connection_type_ = NetworkChangeNotifier::CONNECTION_UNKNOWN;
224 connection_type_initialized_ = true;
225 connection_type_initialized_cv_.Signal();
228 AddressTrackerLinux::AddressMap AddressTrackerLinux::GetAddressMap() const {
229 AddressTrackerAutoLock lock(*this, address_map_lock_);
230 return address_map_;
233 base::hash_set<int> AddressTrackerLinux::GetOnlineLinks() const {
234 AddressTrackerAutoLock lock(*this, online_links_lock_);
235 return online_links_;
238 NetworkChangeNotifier::ConnectionType
239 AddressTrackerLinux::GetCurrentConnectionType() {
240 // http://crbug.com/125097
241 base::ThreadRestrictions::ScopedAllowWait allow_wait;
242 AddressTrackerAutoLock lock(*this, connection_type_lock_);
243 // Make sure the initial connection type is set before returning.
244 while (!connection_type_initialized_) {
245 connection_type_initialized_cv_.Wait();
247 return current_connection_type_;
250 void AddressTrackerLinux::ReadMessages(bool* address_changed,
251 bool* link_changed,
252 bool* tunnel_changed) {
253 *address_changed = false;
254 *link_changed = false;
255 *tunnel_changed = false;
256 char buffer[4096];
257 bool first_loop = true;
258 for (;;) {
259 int rv = HANDLE_EINTR(recv(netlink_fd_,
260 buffer,
261 sizeof(buffer),
262 // Block the first time through loop.
263 first_loop ? 0 : MSG_DONTWAIT));
264 first_loop = false;
265 if (rv == 0) {
266 LOG(ERROR) << "Unexpected shutdown of NETLINK socket.";
267 return;
269 if (rv < 0) {
270 if ((errno == EAGAIN) || (errno == EWOULDBLOCK))
271 break;
272 PLOG(ERROR) << "Failed to recv from netlink socket";
273 return;
275 HandleMessage(buffer, rv, address_changed, link_changed, tunnel_changed);
277 if (*link_changed || *address_changed)
278 UpdateCurrentConnectionType();
281 void AddressTrackerLinux::HandleMessage(char* buffer,
282 size_t length,
283 bool* address_changed,
284 bool* link_changed,
285 bool* tunnel_changed) {
286 DCHECK(buffer);
287 for (struct nlmsghdr* header = reinterpret_cast<struct nlmsghdr*>(buffer);
288 NLMSG_OK(header, length);
289 header = NLMSG_NEXT(header, length)) {
290 switch (header->nlmsg_type) {
291 case NLMSG_DONE:
292 return;
293 case NLMSG_ERROR: {
294 const struct nlmsgerr* msg =
295 reinterpret_cast<struct nlmsgerr*>(NLMSG_DATA(header));
296 LOG(ERROR) << "Unexpected netlink error " << msg->error << ".";
297 } return;
298 case RTM_NEWADDR: {
299 IPAddressNumber address;
300 bool really_deprecated;
301 if (GetAddress(header, &address, &really_deprecated)) {
302 AddressTrackerAutoLock lock(*this, address_map_lock_);
303 struct ifaddrmsg* msg =
304 reinterpret_cast<struct ifaddrmsg*>(NLMSG_DATA(header));
305 // Routers may frequently (every few seconds) output the IPv6 ULA
306 // prefix which can cause the linux kernel to frequently output two
307 // back-to-back messages, one without the deprecated flag and one with
308 // the deprecated flag but both with preferred lifetimes of 0. Avoid
309 // interpretting this as an actual change by canonicalizing the two
310 // messages by setting the deprecated flag based on the preferred
311 // lifetime also. http://crbug.com/268042
312 if (really_deprecated)
313 msg->ifa_flags |= IFA_F_DEPRECATED;
314 // Only indicate change if the address is new or ifaddrmsg info has
315 // changed.
316 AddressMap::iterator it = address_map_.find(address);
317 if (it == address_map_.end()) {
318 address_map_.insert(it, std::make_pair(address, *msg));
319 *address_changed = true;
320 } else if (memcmp(&it->second, msg, sizeof(*msg))) {
321 it->second = *msg;
322 *address_changed = true;
325 } break;
326 case RTM_DELADDR: {
327 IPAddressNumber address;
328 if (GetAddress(header, &address, NULL)) {
329 AddressTrackerAutoLock lock(*this, address_map_lock_);
330 if (address_map_.erase(address))
331 *address_changed = true;
333 } break;
334 case RTM_NEWLINK: {
335 const struct ifinfomsg* msg =
336 reinterpret_cast<struct ifinfomsg*>(NLMSG_DATA(header));
337 if (!(msg->ifi_flags & IFF_LOOPBACK) && (msg->ifi_flags & IFF_UP) &&
338 (msg->ifi_flags & IFF_LOWER_UP) && (msg->ifi_flags & IFF_RUNNING)) {
339 AddressTrackerAutoLock lock(*this, online_links_lock_);
340 if (online_links_.insert(msg->ifi_index).second) {
341 *link_changed = true;
342 if (IsTunnelInterface(msg->ifi_index))
343 *tunnel_changed = true;
345 } else {
346 AddressTrackerAutoLock lock(*this, online_links_lock_);
347 if (online_links_.erase(msg->ifi_index)) {
348 *link_changed = true;
349 if (IsTunnelInterface(msg->ifi_index))
350 *tunnel_changed = true;
353 } break;
354 case RTM_DELLINK: {
355 const struct ifinfomsg* msg =
356 reinterpret_cast<struct ifinfomsg*>(NLMSG_DATA(header));
357 AddressTrackerAutoLock lock(*this, online_links_lock_);
358 if (online_links_.erase(msg->ifi_index)) {
359 *link_changed = true;
360 if (IsTunnelInterface(msg->ifi_index))
361 *tunnel_changed = true;
363 } break;
364 default:
365 break;
370 void AddressTrackerLinux::OnFileCanReadWithoutBlocking(int fd) {
371 DCHECK_EQ(netlink_fd_, fd);
372 bool address_changed;
373 bool link_changed;
374 bool tunnel_changed;
375 ReadMessages(&address_changed, &link_changed, &tunnel_changed);
376 if (address_changed)
377 address_callback_.Run();
378 if (link_changed)
379 link_callback_.Run();
380 if (tunnel_changed)
381 tunnel_callback_.Run();
384 void AddressTrackerLinux::OnFileCanWriteWithoutBlocking(int /* fd */) {}
386 void AddressTrackerLinux::CloseSocket() {
387 if (netlink_fd_ >= 0 && IGNORE_EINTR(close(netlink_fd_)) < 0)
388 PLOG(ERROR) << "Could not close NETLINK socket.";
389 netlink_fd_ = -1;
392 bool AddressTrackerLinux::IsTunnelInterface(int interface_index) const {
393 // Linux kernel drivers/net/tun.c uses "tun" name prefix.
394 char buf[IFNAMSIZ] = {0};
395 return strncmp(get_interface_name_(interface_index, buf), "tun", 3) == 0;
398 void AddressTrackerLinux::UpdateCurrentConnectionType() {
399 AddressTrackerLinux::AddressMap address_map = GetAddressMap();
400 base::hash_set<int> online_links = GetOnlineLinks();
402 // Strip out tunnel interfaces from online_links
403 for (base::hash_set<int>::const_iterator it = online_links.begin();
404 it != online_links.end();) {
405 if (IsTunnelInterface(*it)) {
406 base::hash_set<int>::const_iterator tunnel_it = it;
407 ++it;
408 online_links.erase(*tunnel_it);
409 } else {
410 ++it;
414 NetworkInterfaceList networks;
415 NetworkChangeNotifier::ConnectionType type =
416 NetworkChangeNotifier::CONNECTION_NONE;
417 if (GetNetworkListImpl(&networks, 0, online_links, address_map,
418 get_interface_name_)) {
419 type = NetworkChangeNotifier::ConnectionTypeFromInterfaceList(networks);
420 } else {
421 type = online_links.empty() ? NetworkChangeNotifier::CONNECTION_NONE
422 : NetworkChangeNotifier::CONNECTION_UNKNOWN;
425 AddressTrackerAutoLock lock(*this, connection_type_lock_);
426 current_connection_type_ = type;
429 AddressTrackerLinux::AddressTrackerAutoLock::AddressTrackerAutoLock(
430 const AddressTrackerLinux& tracker,
431 base::Lock& lock)
432 : tracker_(tracker), lock_(lock) {
433 if (tracker_.tracking_) {
434 lock_.Acquire();
435 } else {
436 DCHECK(tracker_.thread_checker_.CalledOnValidThread());
440 AddressTrackerLinux::AddressTrackerAutoLock::~AddressTrackerAutoLock() {
441 if (tracker_.tracking_) {
442 lock_.AssertAcquired();
443 lock_.Release();
447 } // namespace internal
448 } // namespace net