| OLD | NEW |
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "net/base/address_tracker_linux.h" | 5 #include "net/base/address_tracker_linux.h" |
| 6 | 6 |
| 7 #include <errno.h> | 7 #include <errno.h> |
| 8 #include <linux/if.h> | 8 #include <linux/if.h> |
| 9 #include <sys/ioctl.h> | 9 #include <sys/ioctl.h> |
| 10 | 10 |
| 11 #include "base/files/scoped_file.h" |
| 11 #include "base/logging.h" | 12 #include "base/logging.h" |
| 12 #include "base/posix/eintr_wrapper.h" | 13 #include "base/posix/eintr_wrapper.h" |
| 13 #include "base/threading/thread_restrictions.h" | 14 #include "base/threading/thread_restrictions.h" |
| 15 #include "net/base/net_util_linux.h" |
| 14 | 16 |
| 15 namespace net { | 17 namespace net { |
| 16 namespace internal { | 18 namespace internal { |
| 17 | 19 |
| 18 namespace { | 20 namespace { |
| 19 | 21 |
| 20 // Retrieves address from NETLINK address message. | 22 // Retrieves address from NETLINK address message. |
| 21 // Sets |really_deprecated| for IPv6 addresses with preferred lifetimes of 0. | 23 // Sets |really_deprecated| for IPv6 addresses with preferred lifetimes of 0. |
| 22 bool GetAddress(const struct nlmsghdr* header, | 24 bool GetAddress(const struct nlmsghdr* header, |
| 23 IPAddressNumber* out, | 25 IPAddressNumber* out, |
| (...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 69 } | 71 } |
| 70 } | 72 } |
| 71 if (local) | 73 if (local) |
| 72 address = local; | 74 address = local; |
| 73 if (!address) | 75 if (!address) |
| 74 return false; | 76 return false; |
| 75 out->assign(address, address + address_length); | 77 out->assign(address, address + address_length); |
| 76 return true; | 78 return true; |
| 77 } | 79 } |
| 78 | 80 |
| 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; | |
| 96 } | |
| 97 | |
| 98 } // namespace | 81 } // namespace |
| 99 | 82 |
| 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; |
| 89 |
| 90 struct ifreq ifr = {}; |
| 91 ifr.ifr_ifindex = interface_index; |
| 92 |
| 93 if (ioctl(ioctl_socket.get(), SIOCGIFNAME, &ifr) == 0) |
| 94 strncpy(buf, ifr.ifr_name, IFNAMSIZ - 1); |
| 95 return buf; |
| 96 } |
| 97 |
| 100 AddressTrackerLinux::AddressTrackerLinux() | 98 AddressTrackerLinux::AddressTrackerLinux() |
| 101 : get_interface_name_(GetInterfaceName), | 99 : get_interface_name_(GetInterfaceName), |
| 102 address_callback_(base::Bind(&base::DoNothing)), | 100 address_callback_(base::Bind(&base::DoNothing)), |
| 103 link_callback_(base::Bind(&base::DoNothing)), | 101 link_callback_(base::Bind(&base::DoNothing)), |
| 104 tunnel_callback_(base::Bind(&base::DoNothing)), | 102 tunnel_callback_(base::Bind(&base::DoNothing)), |
| 105 netlink_fd_(-1), | 103 netlink_fd_(-1), |
| 106 is_offline_(true), | 104 connection_type_initialized_(false), |
| 107 is_offline_initialized_(false), | 105 connection_type_initialized_cv_(&connection_type_lock_), |
| 108 is_offline_initialized_cv_(&is_offline_lock_), | 106 current_connection_type_(NetworkChangeNotifier::CONNECTION_NONE), |
| 109 tracking_(false) { | 107 tracking_(false) { |
| 110 } | 108 } |
| 111 | 109 |
| 112 AddressTrackerLinux::AddressTrackerLinux(const base::Closure& address_callback, | 110 AddressTrackerLinux::AddressTrackerLinux(const base::Closure& address_callback, |
| 113 const base::Closure& link_callback, | 111 const base::Closure& link_callback, |
| 114 const base::Closure& tunnel_callback) | 112 const base::Closure& tunnel_callback) |
| 115 : get_interface_name_(GetInterfaceName), | 113 : get_interface_name_(GetInterfaceName), |
| 116 address_callback_(address_callback), | 114 address_callback_(address_callback), |
| 117 link_callback_(link_callback), | 115 link_callback_(link_callback), |
| 118 tunnel_callback_(tunnel_callback), | 116 tunnel_callback_(tunnel_callback), |
| 119 netlink_fd_(-1), | 117 netlink_fd_(-1), |
| 120 is_offline_(true), | 118 connection_type_initialized_(false), |
| 121 is_offline_initialized_(false), | 119 connection_type_initialized_cv_(&connection_type_lock_), |
| 122 is_offline_initialized_cv_(&is_offline_lock_), | 120 current_connection_type_(NetworkChangeNotifier::CONNECTION_NONE), |
| 123 tracking_(true) { | 121 tracking_(true) { |
| 124 DCHECK(!address_callback.is_null()); | 122 DCHECK(!address_callback.is_null()); |
| 125 DCHECK(!link_callback.is_null()); | 123 DCHECK(!link_callback.is_null()); |
| 126 } | 124 } |
| 127 | 125 |
| 128 AddressTrackerLinux::~AddressTrackerLinux() { | 126 AddressTrackerLinux::~AddressTrackerLinux() { |
| 129 CloseSocket(); | 127 CloseSocket(); |
| 130 } | 128 } |
| 131 | 129 |
| 132 void AddressTrackerLinux::Init() { | 130 void AddressTrackerLinux::Init() { |
| (...skipping 63 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 196 sizeof(peer))); | 194 sizeof(peer))); |
| 197 if (rv < 0) { | 195 if (rv < 0) { |
| 198 PLOG(ERROR) << "Could not send NETLINK request"; | 196 PLOG(ERROR) << "Could not send NETLINK request"; |
| 199 AbortAndForceOnline(); | 197 AbortAndForceOnline(); |
| 200 return; | 198 return; |
| 201 } | 199 } |
| 202 | 200 |
| 203 // Consume pending message to populate links_online_, but don't notify. | 201 // Consume pending message to populate links_online_, but don't notify. |
| 204 ReadMessages(&address_changed, &link_changed, &tunnel_changed); | 202 ReadMessages(&address_changed, &link_changed, &tunnel_changed); |
| 205 { | 203 { |
| 206 AddressTrackerAutoLock lock(*this, is_offline_lock_); | 204 AddressTrackerAutoLock lock(*this, connection_type_lock_); |
| 207 is_offline_initialized_ = true; | 205 connection_type_initialized_ = true; |
| 208 is_offline_initialized_cv_.Signal(); | 206 connection_type_initialized_cv_.Signal(); |
| 209 } | 207 } |
| 210 | 208 |
| 211 if (tracking_) { | 209 if (tracking_) { |
| 212 rv = base::MessageLoopForIO::current()->WatchFileDescriptor( | 210 rv = base::MessageLoopForIO::current()->WatchFileDescriptor( |
| 213 netlink_fd_, true, base::MessageLoopForIO::WATCH_READ, &watcher_, this); | 211 netlink_fd_, true, base::MessageLoopForIO::WATCH_READ, &watcher_, this); |
| 214 if (rv < 0) { | 212 if (rv < 0) { |
| 215 PLOG(ERROR) << "Could not watch NETLINK socket"; | 213 PLOG(ERROR) << "Could not watch NETLINK socket"; |
| 216 AbortAndForceOnline(); | 214 AbortAndForceOnline(); |
| 217 return; | 215 return; |
| 218 } | 216 } |
| 219 } | 217 } |
| 220 } | 218 } |
| 221 | 219 |
| 222 void AddressTrackerLinux::AbortAndForceOnline() { | 220 void AddressTrackerLinux::AbortAndForceOnline() { |
| 223 CloseSocket(); | 221 CloseSocket(); |
| 224 AddressTrackerAutoLock lock(*this, is_offline_lock_); | 222 AddressTrackerAutoLock lock(*this, connection_type_lock_); |
| 225 is_offline_ = false; | 223 current_connection_type_ = NetworkChangeNotifier::CONNECTION_UNKNOWN; |
| 226 is_offline_initialized_ = true; | 224 connection_type_initialized_ = true; |
| 227 is_offline_initialized_cv_.Signal(); | 225 connection_type_initialized_cv_.Signal(); |
| 228 } | 226 } |
| 229 | 227 |
| 230 AddressTrackerLinux::AddressMap AddressTrackerLinux::GetAddressMap() const { | 228 AddressTrackerLinux::AddressMap AddressTrackerLinux::GetAddressMap() const { |
| 231 AddressTrackerAutoLock lock(*this, address_map_lock_); | 229 AddressTrackerAutoLock lock(*this, address_map_lock_); |
| 232 return address_map_; | 230 return address_map_; |
| 233 } | 231 } |
| 234 | 232 |
| 235 base::hash_set<int> AddressTrackerLinux::GetOnlineLinks() const { | 233 base::hash_set<int> AddressTrackerLinux::GetOnlineLinks() const { |
| 236 AddressTrackerAutoLock lock(*this, online_links_lock_); | 234 AddressTrackerAutoLock lock(*this, online_links_lock_); |
| 237 return online_links_; | 235 return online_links_; |
| 238 } | 236 } |
| 239 | 237 |
| 240 NetworkChangeNotifier::ConnectionType | 238 NetworkChangeNotifier::ConnectionType |
| 241 AddressTrackerLinux::GetCurrentConnectionType() { | 239 AddressTrackerLinux::GetCurrentConnectionType() { |
| 242 // http://crbug.com/125097 | 240 // http://crbug.com/125097 |
| 243 base::ThreadRestrictions::ScopedAllowWait allow_wait; | 241 base::ThreadRestrictions::ScopedAllowWait allow_wait; |
| 244 AddressTrackerAutoLock lock(*this, is_offline_lock_); | 242 AddressTrackerAutoLock lock(*this, connection_type_lock_); |
| 245 // Make sure the initial offline state is set before returning. | 243 // Make sure the initial connection type is set before returning. |
| 246 while (!is_offline_initialized_) { | 244 while (!connection_type_initialized_) { |
| 247 is_offline_initialized_cv_.Wait(); | 245 connection_type_initialized_cv_.Wait(); |
| 248 } | 246 } |
| 249 // TODO(droger): Return something more detailed than CONNECTION_UNKNOWN. | 247 return current_connection_type_; |
| 250 // http://crbug.com/160537 | |
| 251 return is_offline_ ? NetworkChangeNotifier::CONNECTION_NONE : | |
| 252 NetworkChangeNotifier::CONNECTION_UNKNOWN; | |
| 253 } | 248 } |
| 254 | 249 |
| 255 void AddressTrackerLinux::ReadMessages(bool* address_changed, | 250 void AddressTrackerLinux::ReadMessages(bool* address_changed, |
| 256 bool* link_changed, | 251 bool* link_changed, |
| 257 bool* tunnel_changed) { | 252 bool* tunnel_changed) { |
| 258 *address_changed = false; | 253 *address_changed = false; |
| 259 *link_changed = false; | 254 *link_changed = false; |
| 260 *tunnel_changed = false; | 255 *tunnel_changed = false; |
| 261 char buffer[4096]; | 256 char buffer[4096]; |
| 262 bool first_loop = true; | 257 bool first_loop = true; |
| 263 for (;;) { | 258 for (;;) { |
| 264 int rv = HANDLE_EINTR(recv(netlink_fd_, | 259 int rv = HANDLE_EINTR(recv(netlink_fd_, |
| 265 buffer, | 260 buffer, |
| 266 sizeof(buffer), | 261 sizeof(buffer), |
| 267 // Block the first time through loop. | 262 // Block the first time through loop. |
| 268 first_loop ? 0 : MSG_DONTWAIT)); | 263 first_loop ? 0 : MSG_DONTWAIT)); |
| 269 first_loop = false; | 264 first_loop = false; |
| 270 if (rv == 0) { | 265 if (rv == 0) { |
| 271 LOG(ERROR) << "Unexpected shutdown of NETLINK socket."; | 266 LOG(ERROR) << "Unexpected shutdown of NETLINK socket."; |
| 272 return; | 267 return; |
| 273 } | 268 } |
| 274 if (rv < 0) { | 269 if (rv < 0) { |
| 275 if ((errno == EAGAIN) || (errno == EWOULDBLOCK)) | 270 if ((errno == EAGAIN) || (errno == EWOULDBLOCK)) |
| 276 break; | 271 break; |
| 277 PLOG(ERROR) << "Failed to recv from netlink socket"; | 272 PLOG(ERROR) << "Failed to recv from netlink socket"; |
| 278 return; | 273 return; |
| 279 } | 274 } |
| 280 HandleMessage(buffer, rv, address_changed, link_changed, tunnel_changed); | 275 HandleMessage(buffer, rv, address_changed, link_changed, tunnel_changed); |
| 281 } | 276 } |
| 282 if (*link_changed) { | 277 if (*link_changed || *address_changed) |
| 283 bool is_offline; | 278 UpdateCurrentConnectionType(); |
| 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 } | 279 } |
| 292 | 280 |
| 293 void AddressTrackerLinux::HandleMessage(char* buffer, | 281 void AddressTrackerLinux::HandleMessage(char* buffer, |
| 294 size_t length, | 282 size_t length, |
| 295 bool* address_changed, | 283 bool* address_changed, |
| 296 bool* link_changed, | 284 bool* link_changed, |
| 297 bool* tunnel_changed) { | 285 bool* tunnel_changed) { |
| 298 DCHECK(buffer); | 286 DCHECK(buffer); |
| 299 for (struct nlmsghdr* header = reinterpret_cast<struct nlmsghdr*>(buffer); | 287 for (struct nlmsghdr* header = reinterpret_cast<struct nlmsghdr*>(buffer); |
| 300 NLMSG_OK(header, length); | 288 NLMSG_OK(header, length); |
| (...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 344 } | 332 } |
| 345 } break; | 333 } break; |
| 346 case RTM_NEWLINK: { | 334 case RTM_NEWLINK: { |
| 347 const struct ifinfomsg* msg = | 335 const struct ifinfomsg* msg = |
| 348 reinterpret_cast<struct ifinfomsg*>(NLMSG_DATA(header)); | 336 reinterpret_cast<struct ifinfomsg*>(NLMSG_DATA(header)); |
| 349 if (!(msg->ifi_flags & IFF_LOOPBACK) && (msg->ifi_flags & IFF_UP) && | 337 if (!(msg->ifi_flags & IFF_LOOPBACK) && (msg->ifi_flags & IFF_UP) && |
| 350 (msg->ifi_flags & IFF_LOWER_UP) && (msg->ifi_flags & IFF_RUNNING)) { | 338 (msg->ifi_flags & IFF_LOWER_UP) && (msg->ifi_flags & IFF_RUNNING)) { |
| 351 AddressTrackerAutoLock lock(*this, online_links_lock_); | 339 AddressTrackerAutoLock lock(*this, online_links_lock_); |
| 352 if (online_links_.insert(msg->ifi_index).second) { | 340 if (online_links_.insert(msg->ifi_index).second) { |
| 353 *link_changed = true; | 341 *link_changed = true; |
| 354 if (IsTunnelInterface(msg)) | 342 if (IsTunnelInterface(msg->ifi_index)) |
| 355 *tunnel_changed = true; | 343 *tunnel_changed = true; |
| 356 } | 344 } |
| 357 } else { | 345 } else { |
| 358 AddressTrackerAutoLock lock(*this, online_links_lock_); | 346 AddressTrackerAutoLock lock(*this, online_links_lock_); |
| 359 if (online_links_.erase(msg->ifi_index)) { | 347 if (online_links_.erase(msg->ifi_index)) { |
| 360 *link_changed = true; | 348 *link_changed = true; |
| 361 if (IsTunnelInterface(msg)) | 349 if (IsTunnelInterface(msg->ifi_index)) |
| 362 *tunnel_changed = true; | 350 *tunnel_changed = true; |
| 363 } | 351 } |
| 364 } | 352 } |
| 365 } break; | 353 } break; |
| 366 case RTM_DELLINK: { | 354 case RTM_DELLINK: { |
| 367 const struct ifinfomsg* msg = | 355 const struct ifinfomsg* msg = |
| 368 reinterpret_cast<struct ifinfomsg*>(NLMSG_DATA(header)); | 356 reinterpret_cast<struct ifinfomsg*>(NLMSG_DATA(header)); |
| 369 AddressTrackerAutoLock lock(*this, online_links_lock_); | 357 AddressTrackerAutoLock lock(*this, online_links_lock_); |
| 370 if (online_links_.erase(msg->ifi_index)) { | 358 if (online_links_.erase(msg->ifi_index)) { |
| 371 *link_changed = true; | 359 *link_changed = true; |
| 372 if (IsTunnelInterface(msg)) | 360 if (IsTunnelInterface(msg->ifi_index)) |
| 373 *tunnel_changed = true; | 361 *tunnel_changed = true; |
| 374 } | 362 } |
| 375 } break; | 363 } break; |
| 376 default: | 364 default: |
| 377 break; | 365 break; |
| 378 } | 366 } |
| 379 } | 367 } |
| 380 } | 368 } |
| 381 | 369 |
| 382 void AddressTrackerLinux::OnFileCanReadWithoutBlocking(int fd) { | 370 void AddressTrackerLinux::OnFileCanReadWithoutBlocking(int fd) { |
| (...skipping 11 matching lines...) Expand all Loading... |
| 394 } | 382 } |
| 395 | 383 |
| 396 void AddressTrackerLinux::OnFileCanWriteWithoutBlocking(int /* fd */) {} | 384 void AddressTrackerLinux::OnFileCanWriteWithoutBlocking(int /* fd */) {} |
| 397 | 385 |
| 398 void AddressTrackerLinux::CloseSocket() { | 386 void AddressTrackerLinux::CloseSocket() { |
| 399 if (netlink_fd_ >= 0 && IGNORE_EINTR(close(netlink_fd_)) < 0) | 387 if (netlink_fd_ >= 0 && IGNORE_EINTR(close(netlink_fd_)) < 0) |
| 400 PLOG(ERROR) << "Could not close NETLINK socket."; | 388 PLOG(ERROR) << "Could not close NETLINK socket."; |
| 401 netlink_fd_ = -1; | 389 netlink_fd_ = -1; |
| 402 } | 390 } |
| 403 | 391 |
| 404 bool AddressTrackerLinux::IsTunnelInterface(const struct ifinfomsg* msg) const { | 392 bool AddressTrackerLinux::IsTunnelInterface(int interface_index) const { |
| 405 // Linux kernel drivers/net/tun.c uses "tun" name prefix. | 393 // Linux kernel drivers/net/tun.c uses "tun" name prefix. |
| 406 return strncmp(get_interface_name_(msg->ifi_index), "tun", 3) == 0; | 394 char buf[IFNAMSIZ] = {0}; |
| 395 return strncmp(get_interface_name_(interface_index, buf), "tun", 3) == 0; |
| 396 } |
| 397 |
| 398 void AddressTrackerLinux::UpdateCurrentConnectionType() { |
| 399 AddressTrackerLinux::AddressMap address_map = GetAddressMap(); |
| 400 base::hash_set<int> online_links = GetOnlineLinks(); |
| 401 |
| 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; |
| 411 } |
| 412 } |
| 413 |
| 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; |
| 423 } |
| 424 |
| 425 AddressTrackerAutoLock lock(*this, connection_type_lock_); |
| 426 current_connection_type_ = type; |
| 407 } | 427 } |
| 408 | 428 |
| 409 AddressTrackerLinux::AddressTrackerAutoLock::AddressTrackerAutoLock( | 429 AddressTrackerLinux::AddressTrackerAutoLock::AddressTrackerAutoLock( |
| 410 const AddressTrackerLinux& tracker, | 430 const AddressTrackerLinux& tracker, |
| 411 base::Lock& lock) | 431 base::Lock& lock) |
| 412 : tracker_(tracker), lock_(lock) { | 432 : tracker_(tracker), lock_(lock) { |
| 413 if (tracker_.tracking_) { | 433 if (tracker_.tracking_) { |
| 414 lock_.Acquire(); | 434 lock_.Acquire(); |
| 415 } else { | 435 } else { |
| 416 DCHECK(tracker_.thread_checker_.CalledOnValidThread()); | 436 DCHECK(tracker_.thread_checker_.CalledOnValidThread()); |
| 417 } | 437 } |
| 418 } | 438 } |
| 419 | 439 |
| 420 AddressTrackerLinux::AddressTrackerAutoLock::~AddressTrackerAutoLock() { | 440 AddressTrackerLinux::AddressTrackerAutoLock::~AddressTrackerAutoLock() { |
| 421 if (tracker_.tracking_) { | 441 if (tracker_.tracking_) { |
| 422 lock_.AssertAcquired(); | 442 lock_.AssertAcquired(); |
| 423 lock_.Release(); | 443 lock_.Release(); |
| 424 } | 444 } |
| 425 } | 445 } |
| 426 | 446 |
| 427 } // namespace internal | 447 } // namespace internal |
| 428 } // namespace net | 448 } // namespace net |
| OLD | NEW |