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. 4 5#include "net/base/address_tracker_linux.h" 6 7#include <errno.h> 8#include <linux/if.h> 9#include <sys/ioctl.h> 10 11#include "base/logging.h" 12#include "base/posix/eintr_wrapper.h" 13#include "base/threading/thread_restrictions.h" 14 15namespace net { 16namespace internal { 17 18namespace { 19 20// Retrieves address from NETLINK address message. 21// Sets |really_deprecated| for IPv6 addresses with preferred lifetimes of 0. 22bool 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; 40 } 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; 69 } 70 } 71 if (local) 72 address = local; 73 if (!address) 74 return false; 75 out->assign(address, address + address_length); 76 return true; 77} 78 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. 84const 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; 96} 97 98} // namespace 99 100AddressTrackerLinux::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) { 110} 111 112AddressTrackerLinux::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()); 126} 127 128AddressTrackerLinux::~AddressTrackerLinux() { 129 CloseSocket(); 130} 131 132void 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; 138 } 139 140 int rv; 141 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; 157 } 158 } 159 160 // Request dump of addresses. 161 struct sockaddr_nl peer = {}; 162 peer.nl_family = AF_NETLINK; 163 164 struct { 165 struct nlmsghdr header; 166 struct rtgenmsg msg; 167 } request = {}; 168 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; 174 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; 182 } 183 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); 190 191 // Request dump of link state 192 request.header.nlmsg_type = RTM_GETLINK; 193 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; 201 } 202 203 // Consume pending message to populate links_online_, but don't notify. 204 ReadMessages(&address_changed, &link_changed, &tunnel_changed); 205 { 206 AddressTrackerAutoLock lock(*this, is_offline_lock_); 207 is_offline_initialized_ = true; 208 is_offline_initialized_cv_.Signal(); 209 } 210 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; 218 } 219 } 220} 221 222void 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(); 228} 229 230AddressTrackerLinux::AddressMap AddressTrackerLinux::GetAddressMap() const { 231 AddressTrackerAutoLock lock(*this, address_map_lock_); 232 return address_map_; 233} 234 235base::hash_set<int> AddressTrackerLinux::GetOnlineLinks() const { 236 AddressTrackerAutoLock lock(*this, online_links_lock_); 237 return online_links_; 238} 239 240NetworkChangeNotifier::ConnectionType 241AddressTrackerLinux::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(); 248 } 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; 253} 254 255void 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; 273 } 274 if (rv < 0) { 275 if ((errno == EAGAIN) || (errno == EWOULDBLOCK)) 276 break; 277 PLOG(ERROR) << "Failed to recv from netlink socket"; 278 return; 279 } 280 HandleMessage(buffer, rv, address_changed, link_changed, tunnel_changed); 281 } 282 if (*link_changed) { 283 bool is_offline; 284 { 285 AddressTrackerAutoLock lock(*this, online_links_lock_); 286 is_offline = online_links_.empty(); 287 } 288 AddressTrackerAutoLock lock(*this, is_offline_lock_); 289 is_offline_ = is_offline; 290 } 291} 292 293void 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; 335 } 336 } 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; 344 } 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; 356 } 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; 363 } 364 } 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; 374 } 375 } break; 376 default: 377 break; 378 } 379 } 380} 381 382void 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(); 394} 395 396void AddressTrackerLinux::OnFileCanWriteWithoutBlocking(int /* fd */) {} 397 398void 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; 402} 403 404bool 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; 407} 408 409AddressTrackerLinux::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()); 417 } 418} 419 420AddressTrackerLinux::AddressTrackerAutoLock::~AddressTrackerAutoLock() { 421 if (tracker_.tracking_) { 422 lock_.AssertAcquired(); 423 lock_.Release(); 424 } 425} 426 427} // namespace internal 428} // namespace net 429