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 |
(...skipping 79 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
90 ifr.ifr_ifindex = interface_index; | 90 ifr.ifr_ifindex = interface_index; |
91 int rv = ioctl(ioctl_socket, SIOCGIFNAME, &ifr); | 91 int rv = ioctl(ioctl_socket, SIOCGIFNAME, &ifr); |
92 close(ioctl_socket); | 92 close(ioctl_socket); |
93 if (rv != 0) | 93 if (rv != 0) |
94 return ""; | 94 return ""; |
95 return ifr.ifr_name; | 95 return ifr.ifr_name; |
96 } | 96 } |
97 | 97 |
98 } // namespace | 98 } // namespace |
99 | 99 |
| 100 AddressTrackerLinux::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 |
100 AddressTrackerLinux::AddressTrackerLinux(const base::Closure& address_callback, | 112 AddressTrackerLinux::AddressTrackerLinux(const base::Closure& address_callback, |
101 const base::Closure& link_callback, | 113 const base::Closure& link_callback, |
102 const base::Closure& tunnel_callback) | 114 const base::Closure& tunnel_callback) |
103 : get_interface_name_(GetInterfaceName), | 115 : get_interface_name_(GetInterfaceName), |
104 address_callback_(address_callback), | 116 address_callback_(address_callback), |
105 link_callback_(link_callback), | 117 link_callback_(link_callback), |
106 tunnel_callback_(tunnel_callback), | 118 tunnel_callback_(tunnel_callback), |
107 netlink_fd_(-1), | 119 netlink_fd_(-1), |
108 is_offline_(true), | 120 is_offline_(true), |
109 is_offline_initialized_(false), | 121 is_offline_initialized_(false), |
110 is_offline_initialized_cv_(&is_offline_lock_) { | 122 is_offline_initialized_cv_(&is_offline_lock_), |
| 123 tracking_(true) { |
111 DCHECK(!address_callback.is_null()); | 124 DCHECK(!address_callback.is_null()); |
112 DCHECK(!link_callback.is_null()); | 125 DCHECK(!link_callback.is_null()); |
113 } | 126 } |
114 | 127 |
115 AddressTrackerLinux::~AddressTrackerLinux() { | 128 AddressTrackerLinux::~AddressTrackerLinux() { |
116 CloseSocket(); | 129 CloseSocket(); |
117 } | 130 } |
118 | 131 |
119 void AddressTrackerLinux::Init() { | 132 void AddressTrackerLinux::Init() { |
120 netlink_fd_ = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE); | 133 netlink_fd_ = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE); |
121 if (netlink_fd_ < 0) { | 134 if (netlink_fd_ < 0) { |
122 PLOG(ERROR) << "Could not create NETLINK socket"; | 135 PLOG(ERROR) << "Could not create NETLINK socket"; |
123 AbortAndForceOnline(); | 136 AbortAndForceOnline(); |
124 return; | 137 return; |
125 } | 138 } |
126 | 139 |
127 // Request notifications. | 140 int rv; |
128 struct sockaddr_nl addr = {}; | 141 |
129 addr.nl_family = AF_NETLINK; | 142 if (tracking_) { |
130 addr.nl_pid = getpid(); | 143 // Request notifications. |
131 // TODO(szym): Track RTMGRP_LINK as well for ifi_type, http://crbug.com/113993 | 144 struct sockaddr_nl addr = {}; |
132 addr.nl_groups = RTMGRP_IPV4_IFADDR | RTMGRP_IPV6_IFADDR | RTMGRP_NOTIFY | | 145 addr.nl_family = AF_NETLINK; |
133 RTMGRP_LINK; | 146 addr.nl_pid = getpid(); |
134 int rv = bind(netlink_fd_, | 147 // TODO(szym): Track RTMGRP_LINK as well for ifi_type, |
135 reinterpret_cast<struct sockaddr*>(&addr), | 148 // http://crbug.com/113993 |
136 sizeof(addr)); | 149 addr.nl_groups = |
137 if (rv < 0) { | 150 RTMGRP_IPV4_IFADDR | RTMGRP_IPV6_IFADDR | RTMGRP_NOTIFY | RTMGRP_LINK; |
138 PLOG(ERROR) << "Could not bind NETLINK socket"; | 151 rv = bind( |
139 AbortAndForceOnline(); | 152 netlink_fd_, reinterpret_cast<struct sockaddr*>(&addr), sizeof(addr)); |
140 return; | 153 if (rv < 0) { |
| 154 PLOG(ERROR) << "Could not bind NETLINK socket"; |
| 155 AbortAndForceOnline(); |
| 156 return; |
| 157 } |
141 } | 158 } |
142 | 159 |
143 // Request dump of addresses. | 160 // Request dump of addresses. |
144 struct sockaddr_nl peer = {}; | 161 struct sockaddr_nl peer = {}; |
145 peer.nl_family = AF_NETLINK; | 162 peer.nl_family = AF_NETLINK; |
146 | 163 |
147 struct { | 164 struct { |
148 struct nlmsghdr header; | 165 struct nlmsghdr header; |
149 struct rtgenmsg msg; | 166 struct rtgenmsg msg; |
150 } request = {}; | 167 } request = {}; |
(...skipping 28 matching lines...) Expand all Loading... |
179 sizeof(peer))); | 196 sizeof(peer))); |
180 if (rv < 0) { | 197 if (rv < 0) { |
181 PLOG(ERROR) << "Could not send NETLINK request"; | 198 PLOG(ERROR) << "Could not send NETLINK request"; |
182 AbortAndForceOnline(); | 199 AbortAndForceOnline(); |
183 return; | 200 return; |
184 } | 201 } |
185 | 202 |
186 // Consume pending message to populate links_online_, but don't notify. | 203 // Consume pending message to populate links_online_, but don't notify. |
187 ReadMessages(&address_changed, &link_changed, &tunnel_changed); | 204 ReadMessages(&address_changed, &link_changed, &tunnel_changed); |
188 { | 205 { |
189 base::AutoLock lock(is_offline_lock_); | 206 AddressTrackerAutoLock lock(*this, is_offline_lock_); |
190 is_offline_initialized_ = true; | 207 is_offline_initialized_ = true; |
191 is_offline_initialized_cv_.Signal(); | 208 is_offline_initialized_cv_.Signal(); |
192 } | 209 } |
193 | 210 |
194 rv = base::MessageLoopForIO::current()->WatchFileDescriptor( | 211 if (tracking_) { |
195 netlink_fd_, true, base::MessageLoopForIO::WATCH_READ, &watcher_, this); | 212 rv = base::MessageLoopForIO::current()->WatchFileDescriptor( |
196 if (rv < 0) { | 213 netlink_fd_, true, base::MessageLoopForIO::WATCH_READ, &watcher_, this); |
197 PLOG(ERROR) << "Could not watch NETLINK socket"; | 214 if (rv < 0) { |
198 AbortAndForceOnline(); | 215 PLOG(ERROR) << "Could not watch NETLINK socket"; |
199 return; | 216 AbortAndForceOnline(); |
| 217 return; |
| 218 } |
200 } | 219 } |
201 } | 220 } |
202 | 221 |
203 void AddressTrackerLinux::AbortAndForceOnline() { | 222 void AddressTrackerLinux::AbortAndForceOnline() { |
204 CloseSocket(); | 223 CloseSocket(); |
205 base::AutoLock lock(is_offline_lock_); | 224 AddressTrackerAutoLock lock(*this, is_offline_lock_); |
206 is_offline_ = false; | 225 is_offline_ = false; |
207 is_offline_initialized_ = true; | 226 is_offline_initialized_ = true; |
208 is_offline_initialized_cv_.Signal(); | 227 is_offline_initialized_cv_.Signal(); |
209 } | 228 } |
210 | 229 |
211 AddressTrackerLinux::AddressMap AddressTrackerLinux::GetAddressMap() const { | 230 AddressTrackerLinux::AddressMap AddressTrackerLinux::GetAddressMap() const { |
212 base::AutoLock lock(address_map_lock_); | 231 AddressTrackerAutoLock lock(*this, address_map_lock_); |
213 return address_map_; | 232 return address_map_; |
214 } | 233 } |
215 | 234 |
| 235 base::hash_set<int> AddressTrackerLinux::GetOnlineLinks() const { |
| 236 AddressTrackerAutoLock lock(*this, online_links_lock_); |
| 237 return online_links_; |
| 238 } |
| 239 |
216 NetworkChangeNotifier::ConnectionType | 240 NetworkChangeNotifier::ConnectionType |
217 AddressTrackerLinux::GetCurrentConnectionType() { | 241 AddressTrackerLinux::GetCurrentConnectionType() { |
218 // http://crbug.com/125097 | 242 // http://crbug.com/125097 |
219 base::ThreadRestrictions::ScopedAllowWait allow_wait; | 243 base::ThreadRestrictions::ScopedAllowWait allow_wait; |
220 base::AutoLock lock(is_offline_lock_); | 244 AddressTrackerAutoLock lock(*this, is_offline_lock_); |
221 // Make sure the initial offline state is set before returning. | 245 // Make sure the initial offline state is set before returning. |
222 while (!is_offline_initialized_) { | 246 while (!is_offline_initialized_) { |
223 is_offline_initialized_cv_.Wait(); | 247 is_offline_initialized_cv_.Wait(); |
224 } | 248 } |
225 // TODO(droger): Return something more detailed than CONNECTION_UNKNOWN. | 249 // TODO(droger): Return something more detailed than CONNECTION_UNKNOWN. |
226 // http://crbug.com/160537 | 250 // http://crbug.com/160537 |
227 return is_offline_ ? NetworkChangeNotifier::CONNECTION_NONE : | 251 return is_offline_ ? NetworkChangeNotifier::CONNECTION_NONE : |
228 NetworkChangeNotifier::CONNECTION_UNKNOWN; | 252 NetworkChangeNotifier::CONNECTION_UNKNOWN; |
229 } | 253 } |
230 | 254 |
(...skipping 16 matching lines...) Expand all Loading... |
247 LOG(ERROR) << "Unexpected shutdown of NETLINK socket."; | 271 LOG(ERROR) << "Unexpected shutdown of NETLINK socket."; |
248 return; | 272 return; |
249 } | 273 } |
250 if (rv < 0) { | 274 if (rv < 0) { |
251 if ((errno == EAGAIN) || (errno == EWOULDBLOCK)) | 275 if ((errno == EAGAIN) || (errno == EWOULDBLOCK)) |
252 break; | 276 break; |
253 PLOG(ERROR) << "Failed to recv from netlink socket"; | 277 PLOG(ERROR) << "Failed to recv from netlink socket"; |
254 return; | 278 return; |
255 } | 279 } |
256 HandleMessage(buffer, rv, address_changed, link_changed, tunnel_changed); | 280 HandleMessage(buffer, rv, address_changed, link_changed, tunnel_changed); |
257 }; | 281 } |
258 if (*link_changed) { | 282 if (*link_changed) { |
259 base::AutoLock lock(is_offline_lock_); | 283 bool is_offline; |
260 is_offline_ = online_links_.empty(); | 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; |
261 } | 290 } |
262 } | 291 } |
263 | 292 |
264 void AddressTrackerLinux::HandleMessage(char* buffer, | 293 void AddressTrackerLinux::HandleMessage(char* buffer, |
265 size_t length, | 294 size_t length, |
266 bool* address_changed, | 295 bool* address_changed, |
267 bool* link_changed, | 296 bool* link_changed, |
268 bool* tunnel_changed) { | 297 bool* tunnel_changed) { |
269 DCHECK(buffer); | 298 DCHECK(buffer); |
270 for (struct nlmsghdr* header = reinterpret_cast<struct nlmsghdr*>(buffer); | 299 for (struct nlmsghdr* header = reinterpret_cast<struct nlmsghdr*>(buffer); |
271 NLMSG_OK(header, length); | 300 NLMSG_OK(header, length); |
272 header = NLMSG_NEXT(header, length)) { | 301 header = NLMSG_NEXT(header, length)) { |
273 switch (header->nlmsg_type) { | 302 switch (header->nlmsg_type) { |
274 case NLMSG_DONE: | 303 case NLMSG_DONE: |
275 return; | 304 return; |
276 case NLMSG_ERROR: { | 305 case NLMSG_ERROR: { |
277 const struct nlmsgerr* msg = | 306 const struct nlmsgerr* msg = |
278 reinterpret_cast<struct nlmsgerr*>(NLMSG_DATA(header)); | 307 reinterpret_cast<struct nlmsgerr*>(NLMSG_DATA(header)); |
279 LOG(ERROR) << "Unexpected netlink error " << msg->error << "."; | 308 LOG(ERROR) << "Unexpected netlink error " << msg->error << "."; |
280 } return; | 309 } return; |
281 case RTM_NEWADDR: { | 310 case RTM_NEWADDR: { |
282 IPAddressNumber address; | 311 IPAddressNumber address; |
283 bool really_deprecated; | 312 bool really_deprecated; |
284 if (GetAddress(header, &address, &really_deprecated)) { | 313 if (GetAddress(header, &address, &really_deprecated)) { |
285 base::AutoLock lock(address_map_lock_); | 314 AddressTrackerAutoLock lock(*this, address_map_lock_); |
286 struct ifaddrmsg* msg = | 315 struct ifaddrmsg* msg = |
287 reinterpret_cast<struct ifaddrmsg*>(NLMSG_DATA(header)); | 316 reinterpret_cast<struct ifaddrmsg*>(NLMSG_DATA(header)); |
288 // Routers may frequently (every few seconds) output the IPv6 ULA | 317 // Routers may frequently (every few seconds) output the IPv6 ULA |
289 // prefix which can cause the linux kernel to frequently output two | 318 // prefix which can cause the linux kernel to frequently output two |
290 // back-to-back messages, one without the deprecated flag and one with | 319 // back-to-back messages, one without the deprecated flag and one with |
291 // the deprecated flag but both with preferred lifetimes of 0. Avoid | 320 // the deprecated flag but both with preferred lifetimes of 0. Avoid |
292 // interpretting this as an actual change by canonicalizing the two | 321 // interpretting this as an actual change by canonicalizing the two |
293 // messages by setting the deprecated flag based on the preferred | 322 // messages by setting the deprecated flag based on the preferred |
294 // lifetime also. http://crbug.com/268042 | 323 // lifetime also. http://crbug.com/268042 |
295 if (really_deprecated) | 324 if (really_deprecated) |
296 msg->ifa_flags |= IFA_F_DEPRECATED; | 325 msg->ifa_flags |= IFA_F_DEPRECATED; |
297 // Only indicate change if the address is new or ifaddrmsg info has | 326 // Only indicate change if the address is new or ifaddrmsg info has |
298 // changed. | 327 // changed. |
299 AddressMap::iterator it = address_map_.find(address); | 328 AddressMap::iterator it = address_map_.find(address); |
300 if (it == address_map_.end()) { | 329 if (it == address_map_.end()) { |
301 address_map_.insert(it, std::make_pair(address, *msg)); | 330 address_map_.insert(it, std::make_pair(address, *msg)); |
302 *address_changed = true; | 331 *address_changed = true; |
303 } else if (memcmp(&it->second, msg, sizeof(*msg))) { | 332 } else if (memcmp(&it->second, msg, sizeof(*msg))) { |
304 it->second = *msg; | 333 it->second = *msg; |
305 *address_changed = true; | 334 *address_changed = true; |
306 } | 335 } |
307 } | 336 } |
308 } break; | 337 } break; |
309 case RTM_DELADDR: { | 338 case RTM_DELADDR: { |
310 IPAddressNumber address; | 339 IPAddressNumber address; |
311 if (GetAddress(header, &address, NULL)) { | 340 if (GetAddress(header, &address, NULL)) { |
312 base::AutoLock lock(address_map_lock_); | 341 AddressTrackerAutoLock lock(*this, address_map_lock_); |
313 if (address_map_.erase(address)) | 342 if (address_map_.erase(address)) |
314 *address_changed = true; | 343 *address_changed = true; |
315 } | 344 } |
316 } break; | 345 } break; |
317 case RTM_NEWLINK: { | 346 case RTM_NEWLINK: { |
318 const struct ifinfomsg* msg = | 347 const struct ifinfomsg* msg = |
319 reinterpret_cast<struct ifinfomsg*>(NLMSG_DATA(header)); | 348 reinterpret_cast<struct ifinfomsg*>(NLMSG_DATA(header)); |
320 if (!(msg->ifi_flags & IFF_LOOPBACK) && (msg->ifi_flags & IFF_UP) && | 349 if (!(msg->ifi_flags & IFF_LOOPBACK) && (msg->ifi_flags & IFF_UP) && |
321 (msg->ifi_flags & IFF_LOWER_UP) && (msg->ifi_flags & IFF_RUNNING)) { | 350 (msg->ifi_flags & IFF_LOWER_UP) && (msg->ifi_flags & IFF_RUNNING)) { |
| 351 AddressTrackerAutoLock lock(*this, online_links_lock_); |
322 if (online_links_.insert(msg->ifi_index).second) { | 352 if (online_links_.insert(msg->ifi_index).second) { |
323 *link_changed = true; | 353 *link_changed = true; |
324 if (IsTunnelInterface(msg)) | 354 if (IsTunnelInterface(msg)) |
325 *tunnel_changed = true; | 355 *tunnel_changed = true; |
326 } | 356 } |
327 } else { | 357 } else { |
| 358 AddressTrackerAutoLock lock(*this, online_links_lock_); |
328 if (online_links_.erase(msg->ifi_index)) { | 359 if (online_links_.erase(msg->ifi_index)) { |
329 *link_changed = true; | 360 *link_changed = true; |
330 if (IsTunnelInterface(msg)) | 361 if (IsTunnelInterface(msg)) |
331 *tunnel_changed = true; | 362 *tunnel_changed = true; |
332 } | 363 } |
333 } | 364 } |
334 } break; | 365 } break; |
335 case RTM_DELLINK: { | 366 case RTM_DELLINK: { |
336 const struct ifinfomsg* msg = | 367 const struct ifinfomsg* msg = |
337 reinterpret_cast<struct ifinfomsg*>(NLMSG_DATA(header)); | 368 reinterpret_cast<struct ifinfomsg*>(NLMSG_DATA(header)); |
| 369 AddressTrackerAutoLock lock(*this, online_links_lock_); |
338 if (online_links_.erase(msg->ifi_index)) { | 370 if (online_links_.erase(msg->ifi_index)) { |
339 *link_changed = true; | 371 *link_changed = true; |
340 if (IsTunnelInterface(msg)) | 372 if (IsTunnelInterface(msg)) |
341 *tunnel_changed = true; | 373 *tunnel_changed = true; |
342 } | 374 } |
343 } break; | 375 } break; |
344 default: | 376 default: |
345 break; | 377 break; |
346 } | 378 } |
347 } | 379 } |
(...skipping 19 matching lines...) Expand all Loading... |
367 if (netlink_fd_ >= 0 && IGNORE_EINTR(close(netlink_fd_)) < 0) | 399 if (netlink_fd_ >= 0 && IGNORE_EINTR(close(netlink_fd_)) < 0) |
368 PLOG(ERROR) << "Could not close NETLINK socket."; | 400 PLOG(ERROR) << "Could not close NETLINK socket."; |
369 netlink_fd_ = -1; | 401 netlink_fd_ = -1; |
370 } | 402 } |
371 | 403 |
372 bool AddressTrackerLinux::IsTunnelInterface(const struct ifinfomsg* msg) const { | 404 bool AddressTrackerLinux::IsTunnelInterface(const struct ifinfomsg* msg) const { |
373 // Linux kernel drivers/net/tun.c uses "tun" name prefix. | 405 // Linux kernel drivers/net/tun.c uses "tun" name prefix. |
374 return strncmp(get_interface_name_(msg->ifi_index), "tun", 3) == 0; | 406 return strncmp(get_interface_name_(msg->ifi_index), "tun", 3) == 0; |
375 } | 407 } |
376 | 408 |
| 409 AddressTrackerLinux::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 |
| 420 AddressTrackerLinux::AddressTrackerAutoLock::~AddressTrackerAutoLock() { |
| 421 if (tracker_.tracking_) { |
| 422 lock_.AssertAcquired(); |
| 423 lock_.Release(); |
| 424 } |
| 425 } |
| 426 |
377 } // namespace internal | 427 } // namespace internal |
378 } // namespace net | 428 } // namespace net |
OLD | NEW |