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|. | 81 } // namespace |
80 // The return value points to a function-scoped static so it may be changed by | 82 |
81 // subsequent calls. This function could be replaced with if_indextoname() but | 83 // static |
82 // net/if.h cannot be mixed with linux/if.h so we'll stick with exclusively | 84 char* AddressTrackerLinux::GetInterfaceName(int interface_index, char* buf) { |
83 // talking to the kernel and not the C library. | 85 memset(buf, 0, IFNAMSIZ); |
84 const char* GetInterfaceName(int interface_index) { | 86 base::ScopedFD ioctl_socket(socket(AF_INET, SOCK_DGRAM, 0)); |
85 int ioctl_socket = socket(AF_INET, SOCK_DGRAM, 0); | 87 if (!ioctl_socket.is_valid()) |
86 if (ioctl_socket < 0) | 88 return buf; |
87 return ""; | 89 |
88 static struct ifreq ifr; | 90 struct ifreq ifr = {.ifr_ifindex = interface_index}; |
derekjchow1
2015/01/30 04:46:06
I built net_watcher for Chromecast to test this ch
| |
89 memset(&ifr, 0, sizeof(ifr)); | 91 |
90 ifr.ifr_ifindex = interface_index; | 92 if (ioctl(ioctl_socket.get(), SIOCGIFNAME, &ifr) == 0) |
91 int rv = ioctl(ioctl_socket, SIOCGIFNAME, &ifr); | 93 strncpy(buf, ifr.ifr_name, IFNAMSIZ - 1); |
92 close(ioctl_socket); | 94 return buf; |
93 if (rv != 0) | |
94 return ""; | |
95 return ifr.ifr_name; | |
96 } | 95 } |
97 | 96 |
98 } // namespace | |
99 | |
100 AddressTrackerLinux::AddressTrackerLinux() | 97 AddressTrackerLinux::AddressTrackerLinux() |
101 : get_interface_name_(GetInterfaceName), | 98 : get_interface_name_(GetInterfaceName), |
102 address_callback_(base::Bind(&base::DoNothing)), | 99 address_callback_(base::Bind(&base::DoNothing)), |
103 link_callback_(base::Bind(&base::DoNothing)), | 100 link_callback_(base::Bind(&base::DoNothing)), |
104 tunnel_callback_(base::Bind(&base::DoNothing)), | 101 tunnel_callback_(base::Bind(&base::DoNothing)), |
105 netlink_fd_(-1), | 102 netlink_fd_(-1), |
106 is_offline_(true), | 103 connection_type_initialized_(false), |
107 is_offline_initialized_(false), | 104 connection_type_initialized_cv_(&connection_type_lock_), |
108 is_offline_initialized_cv_(&is_offline_lock_), | 105 current_connection_type_(NetworkChangeNotifier::CONNECTION_NONE), |
109 tracking_(false) { | 106 tracking_(false) { |
110 } | 107 } |
111 | 108 |
112 AddressTrackerLinux::AddressTrackerLinux(const base::Closure& address_callback, | 109 AddressTrackerLinux::AddressTrackerLinux(const base::Closure& address_callback, |
113 const base::Closure& link_callback, | 110 const base::Closure& link_callback, |
114 const base::Closure& tunnel_callback) | 111 const base::Closure& tunnel_callback) |
115 : get_interface_name_(GetInterfaceName), | 112 : get_interface_name_(GetInterfaceName), |
116 address_callback_(address_callback), | 113 address_callback_(address_callback), |
117 link_callback_(link_callback), | 114 link_callback_(link_callback), |
118 tunnel_callback_(tunnel_callback), | 115 tunnel_callback_(tunnel_callback), |
119 netlink_fd_(-1), | 116 netlink_fd_(-1), |
120 is_offline_(true), | 117 connection_type_initialized_(false), |
121 is_offline_initialized_(false), | 118 connection_type_initialized_cv_(&connection_type_lock_), |
122 is_offline_initialized_cv_(&is_offline_lock_), | 119 current_connection_type_(NetworkChangeNotifier::CONNECTION_NONE), |
123 tracking_(true) { | 120 tracking_(true) { |
124 DCHECK(!address_callback.is_null()); | 121 DCHECK(!address_callback.is_null()); |
125 DCHECK(!link_callback.is_null()); | 122 DCHECK(!link_callback.is_null()); |
126 } | 123 } |
127 | 124 |
128 AddressTrackerLinux::~AddressTrackerLinux() { | 125 AddressTrackerLinux::~AddressTrackerLinux() { |
129 CloseSocket(); | 126 CloseSocket(); |
130 } | 127 } |
131 | 128 |
132 void AddressTrackerLinux::Init() { | 129 void AddressTrackerLinux::Init() { |
(...skipping 63 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
196 sizeof(peer))); | 193 sizeof(peer))); |
197 if (rv < 0) { | 194 if (rv < 0) { |
198 PLOG(ERROR) << "Could not send NETLINK request"; | 195 PLOG(ERROR) << "Could not send NETLINK request"; |
199 AbortAndForceOnline(); | 196 AbortAndForceOnline(); |
200 return; | 197 return; |
201 } | 198 } |
202 | 199 |
203 // Consume pending message to populate links_online_, but don't notify. | 200 // Consume pending message to populate links_online_, but don't notify. |
204 ReadMessages(&address_changed, &link_changed, &tunnel_changed); | 201 ReadMessages(&address_changed, &link_changed, &tunnel_changed); |
205 { | 202 { |
206 AddressTrackerAutoLock lock(*this, is_offline_lock_); | 203 AddressTrackerAutoLock lock(*this, connection_type_lock_); |
207 is_offline_initialized_ = true; | 204 connection_type_initialized_ = true; |
208 is_offline_initialized_cv_.Signal(); | 205 connection_type_initialized_cv_.Signal(); |
209 } | 206 } |
210 | 207 |
211 if (tracking_) { | 208 if (tracking_) { |
212 rv = base::MessageLoopForIO::current()->WatchFileDescriptor( | 209 rv = base::MessageLoopForIO::current()->WatchFileDescriptor( |
213 netlink_fd_, true, base::MessageLoopForIO::WATCH_READ, &watcher_, this); | 210 netlink_fd_, true, base::MessageLoopForIO::WATCH_READ, &watcher_, this); |
214 if (rv < 0) { | 211 if (rv < 0) { |
215 PLOG(ERROR) << "Could not watch NETLINK socket"; | 212 PLOG(ERROR) << "Could not watch NETLINK socket"; |
216 AbortAndForceOnline(); | 213 AbortAndForceOnline(); |
217 return; | 214 return; |
218 } | 215 } |
219 } | 216 } |
220 } | 217 } |
221 | 218 |
222 void AddressTrackerLinux::AbortAndForceOnline() { | 219 void AddressTrackerLinux::AbortAndForceOnline() { |
223 CloseSocket(); | 220 CloseSocket(); |
224 AddressTrackerAutoLock lock(*this, is_offline_lock_); | 221 AddressTrackerAutoLock lock(*this, connection_type_lock_); |
225 is_offline_ = false; | 222 current_connection_type_ = NetworkChangeNotifier::CONNECTION_UNKNOWN; |
226 is_offline_initialized_ = true; | 223 connection_type_initialized_ = true; |
227 is_offline_initialized_cv_.Signal(); | 224 connection_type_initialized_cv_.Signal(); |
228 } | 225 } |
229 | 226 |
230 AddressTrackerLinux::AddressMap AddressTrackerLinux::GetAddressMap() const { | 227 AddressTrackerLinux::AddressMap AddressTrackerLinux::GetAddressMap() const { |
231 AddressTrackerAutoLock lock(*this, address_map_lock_); | 228 AddressTrackerAutoLock lock(*this, address_map_lock_); |
232 return address_map_; | 229 return address_map_; |
233 } | 230 } |
234 | 231 |
235 base::hash_set<int> AddressTrackerLinux::GetOnlineLinks() const { | 232 base::hash_set<int> AddressTrackerLinux::GetOnlineLinks() const { |
236 AddressTrackerAutoLock lock(*this, online_links_lock_); | 233 AddressTrackerAutoLock lock(*this, online_links_lock_); |
237 return online_links_; | 234 return online_links_; |
238 } | 235 } |
239 | 236 |
240 NetworkChangeNotifier::ConnectionType | 237 NetworkChangeNotifier::ConnectionType |
241 AddressTrackerLinux::GetCurrentConnectionType() { | 238 AddressTrackerLinux::GetCurrentConnectionType() { |
242 // http://crbug.com/125097 | 239 // http://crbug.com/125097 |
243 base::ThreadRestrictions::ScopedAllowWait allow_wait; | 240 base::ThreadRestrictions::ScopedAllowWait allow_wait; |
244 AddressTrackerAutoLock lock(*this, is_offline_lock_); | 241 AddressTrackerAutoLock lock(*this, connection_type_lock_); |
245 // Make sure the initial offline state is set before returning. | 242 // Make sure the initial connection type is set before returning. |
246 while (!is_offline_initialized_) { | 243 while (!connection_type_initialized_) { |
247 is_offline_initialized_cv_.Wait(); | 244 connection_type_initialized_cv_.Wait(); |
248 } | 245 } |
249 // TODO(droger): Return something more detailed than CONNECTION_UNKNOWN. | 246 return current_connection_type_; |
250 // http://crbug.com/160537 | |
251 return is_offline_ ? NetworkChangeNotifier::CONNECTION_NONE : | |
252 NetworkChangeNotifier::CONNECTION_UNKNOWN; | |
253 } | 247 } |
254 | 248 |
255 void AddressTrackerLinux::ReadMessages(bool* address_changed, | 249 void AddressTrackerLinux::ReadMessages(bool* address_changed, |
256 bool* link_changed, | 250 bool* link_changed, |
257 bool* tunnel_changed) { | 251 bool* tunnel_changed) { |
258 *address_changed = false; | 252 *address_changed = false; |
259 *link_changed = false; | 253 *link_changed = false; |
260 *tunnel_changed = false; | 254 *tunnel_changed = false; |
261 char buffer[4096]; | 255 char buffer[4096]; |
262 bool first_loop = true; | 256 bool first_loop = true; |
263 for (;;) { | 257 for (;;) { |
264 int rv = HANDLE_EINTR(recv(netlink_fd_, | 258 int rv = HANDLE_EINTR(recv(netlink_fd_, |
265 buffer, | 259 buffer, |
266 sizeof(buffer), | 260 sizeof(buffer), |
267 // Block the first time through loop. | 261 // Block the first time through loop. |
268 first_loop ? 0 : MSG_DONTWAIT)); | 262 first_loop ? 0 : MSG_DONTWAIT)); |
269 first_loop = false; | 263 first_loop = false; |
270 if (rv == 0) { | 264 if (rv == 0) { |
271 LOG(ERROR) << "Unexpected shutdown of NETLINK socket."; | 265 LOG(ERROR) << "Unexpected shutdown of NETLINK socket."; |
272 return; | 266 return; |
273 } | 267 } |
274 if (rv < 0) { | 268 if (rv < 0) { |
275 if ((errno == EAGAIN) || (errno == EWOULDBLOCK)) | 269 if ((errno == EAGAIN) || (errno == EWOULDBLOCK)) |
276 break; | 270 break; |
277 PLOG(ERROR) << "Failed to recv from netlink socket"; | 271 PLOG(ERROR) << "Failed to recv from netlink socket"; |
278 return; | 272 return; |
279 } | 273 } |
280 HandleMessage(buffer, rv, address_changed, link_changed, tunnel_changed); | 274 HandleMessage(buffer, rv, address_changed, link_changed, tunnel_changed); |
281 } | 275 } |
282 if (*link_changed) { | 276 if (*link_changed || *address_changed) |
283 bool is_offline; | 277 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 } | 278 } |
292 | 279 |
293 void AddressTrackerLinux::HandleMessage(char* buffer, | 280 void AddressTrackerLinux::HandleMessage(char* buffer, |
294 size_t length, | 281 size_t length, |
295 bool* address_changed, | 282 bool* address_changed, |
296 bool* link_changed, | 283 bool* link_changed, |
297 bool* tunnel_changed) { | 284 bool* tunnel_changed) { |
298 DCHECK(buffer); | 285 DCHECK(buffer); |
299 for (struct nlmsghdr* header = reinterpret_cast<struct nlmsghdr*>(buffer); | 286 for (struct nlmsghdr* header = reinterpret_cast<struct nlmsghdr*>(buffer); |
300 NLMSG_OK(header, length); | 287 NLMSG_OK(header, length); |
(...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
344 } | 331 } |
345 } break; | 332 } break; |
346 case RTM_NEWLINK: { | 333 case RTM_NEWLINK: { |
347 const struct ifinfomsg* msg = | 334 const struct ifinfomsg* msg = |
348 reinterpret_cast<struct ifinfomsg*>(NLMSG_DATA(header)); | 335 reinterpret_cast<struct ifinfomsg*>(NLMSG_DATA(header)); |
349 if (!(msg->ifi_flags & IFF_LOOPBACK) && (msg->ifi_flags & IFF_UP) && | 336 if (!(msg->ifi_flags & IFF_LOOPBACK) && (msg->ifi_flags & IFF_UP) && |
350 (msg->ifi_flags & IFF_LOWER_UP) && (msg->ifi_flags & IFF_RUNNING)) { | 337 (msg->ifi_flags & IFF_LOWER_UP) && (msg->ifi_flags & IFF_RUNNING)) { |
351 AddressTrackerAutoLock lock(*this, online_links_lock_); | 338 AddressTrackerAutoLock lock(*this, online_links_lock_); |
352 if (online_links_.insert(msg->ifi_index).second) { | 339 if (online_links_.insert(msg->ifi_index).second) { |
353 *link_changed = true; | 340 *link_changed = true; |
354 if (IsTunnelInterface(msg)) | 341 if (IsTunnelInterface(msg->ifi_index)) |
355 *tunnel_changed = true; | 342 *tunnel_changed = true; |
356 } | 343 } |
357 } else { | 344 } else { |
358 AddressTrackerAutoLock lock(*this, online_links_lock_); | 345 AddressTrackerAutoLock lock(*this, online_links_lock_); |
359 if (online_links_.erase(msg->ifi_index)) { | 346 if (online_links_.erase(msg->ifi_index)) { |
360 *link_changed = true; | 347 *link_changed = true; |
361 if (IsTunnelInterface(msg)) | 348 if (IsTunnelInterface(msg->ifi_index)) |
362 *tunnel_changed = true; | 349 *tunnel_changed = true; |
363 } | 350 } |
364 } | 351 } |
365 } break; | 352 } break; |
366 case RTM_DELLINK: { | 353 case RTM_DELLINK: { |
367 const struct ifinfomsg* msg = | 354 const struct ifinfomsg* msg = |
368 reinterpret_cast<struct ifinfomsg*>(NLMSG_DATA(header)); | 355 reinterpret_cast<struct ifinfomsg*>(NLMSG_DATA(header)); |
369 AddressTrackerAutoLock lock(*this, online_links_lock_); | 356 AddressTrackerAutoLock lock(*this, online_links_lock_); |
370 if (online_links_.erase(msg->ifi_index)) { | 357 if (online_links_.erase(msg->ifi_index)) { |
371 *link_changed = true; | 358 *link_changed = true; |
372 if (IsTunnelInterface(msg)) | 359 if (IsTunnelInterface(msg->ifi_index)) |
373 *tunnel_changed = true; | 360 *tunnel_changed = true; |
374 } | 361 } |
375 } break; | 362 } break; |
376 default: | 363 default: |
377 break; | 364 break; |
378 } | 365 } |
379 } | 366 } |
380 } | 367 } |
381 | 368 |
382 void AddressTrackerLinux::OnFileCanReadWithoutBlocking(int fd) { | 369 void AddressTrackerLinux::OnFileCanReadWithoutBlocking(int fd) { |
(...skipping 11 matching lines...) Expand all Loading... | |
394 } | 381 } |
395 | 382 |
396 void AddressTrackerLinux::OnFileCanWriteWithoutBlocking(int /* fd */) {} | 383 void AddressTrackerLinux::OnFileCanWriteWithoutBlocking(int /* fd */) {} |
397 | 384 |
398 void AddressTrackerLinux::CloseSocket() { | 385 void AddressTrackerLinux::CloseSocket() { |
399 if (netlink_fd_ >= 0 && IGNORE_EINTR(close(netlink_fd_)) < 0) | 386 if (netlink_fd_ >= 0 && IGNORE_EINTR(close(netlink_fd_)) < 0) |
400 PLOG(ERROR) << "Could not close NETLINK socket."; | 387 PLOG(ERROR) << "Could not close NETLINK socket."; |
401 netlink_fd_ = -1; | 388 netlink_fd_ = -1; |
402 } | 389 } |
403 | 390 |
404 bool AddressTrackerLinux::IsTunnelInterface(const struct ifinfomsg* msg) const { | 391 bool AddressTrackerLinux::IsTunnelInterface(int interface_index) const { |
405 // Linux kernel drivers/net/tun.c uses "tun" name prefix. | 392 // Linux kernel drivers/net/tun.c uses "tun" name prefix. |
406 return strncmp(get_interface_name_(msg->ifi_index), "tun", 3) == 0; | 393 char buf[IFNAMSIZ] = {0}; |
394 return strncmp(get_interface_name_(interface_index, buf), "tun", 3) == 0; | |
395 } | |
396 | |
397 void AddressTrackerLinux::UpdateCurrentConnectionType() { | |
398 NetworkChangeNotifier::ConnectionType type = | |
pauljensen
2015/01/29 19:40:12
nit: move |type| declaration down next to |network
derekjchow1
2015/01/30 04:46:06
Done.
| |
399 NetworkChangeNotifier::CONNECTION_NONE; | |
400 AddressTrackerLinux::AddressMap address_map = GetAddressMap(); | |
401 base::hash_set<int> online_links = GetOnlineLinks(); | |
402 | |
403 // Strip out tunnel interfaces from online_links | |
404 for (base::hash_set<int>::const_iterator it = online_links.begin(); | |
405 it != online_links.end();) { | |
406 if (IsTunnelInterface(*it)) { | |
407 base::hash_set<int>::const_iterator tunnel_it = it; | |
408 ++it; | |
409 online_links.erase(tunnel_it); | |
410 } else { | |
411 ++it; | |
412 } | |
413 } | |
414 | |
415 NetworkInterfaceList networks; | |
416 if (GetNetworkListImpl(&networks, 0, online_links, address_map, | |
417 get_interface_name_)) { | |
418 type = NetworkChangeNotifier::ConnectionTypeFromInterfaceList(networks); | |
419 } else { | |
420 type = online_links.empty() ? NetworkChangeNotifier::CONNECTION_NONE | |
421 : NetworkChangeNotifier::CONNECTION_UNKNOWN; | |
422 } | |
423 | |
424 AddressTrackerAutoLock lock(*this, connection_type_lock_); | |
425 current_connection_type_ = type; | |
407 } | 426 } |
408 | 427 |
409 AddressTrackerLinux::AddressTrackerAutoLock::AddressTrackerAutoLock( | 428 AddressTrackerLinux::AddressTrackerAutoLock::AddressTrackerAutoLock( |
410 const AddressTrackerLinux& tracker, | 429 const AddressTrackerLinux& tracker, |
411 base::Lock& lock) | 430 base::Lock& lock) |
412 : tracker_(tracker), lock_(lock) { | 431 : tracker_(tracker), lock_(lock) { |
413 if (tracker_.tracking_) { | 432 if (tracker_.tracking_) { |
414 lock_.Acquire(); | 433 lock_.Acquire(); |
415 } else { | 434 } else { |
416 DCHECK(tracker_.thread_checker_.CalledOnValidThread()); | 435 DCHECK(tracker_.thread_checker_.CalledOnValidThread()); |
417 } | 436 } |
418 } | 437 } |
419 | 438 |
420 AddressTrackerLinux::AddressTrackerAutoLock::~AddressTrackerAutoLock() { | 439 AddressTrackerLinux::AddressTrackerAutoLock::~AddressTrackerAutoLock() { |
421 if (tracker_.tracking_) { | 440 if (tracker_.tracking_) { |
422 lock_.AssertAcquired(); | 441 lock_.AssertAcquired(); |
423 lock_.Release(); | 442 lock_.Release(); |
424 } | 443 } |
425 } | 444 } |
426 | 445 |
427 } // namespace internal | 446 } // namespace internal |
428 } // namespace net | 447 } // namespace net |
OLD | NEW |