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 #if !defined(OS_BSD)
9 #include <linux/if.h>
10 #endif
11 #include <stdint.h>
12 #include <sys/ioctl.h>
13 #include <utility>
14
15 #include "base/bind_helpers.h"
16 #include "base/files/scoped_file.h"
17 #include "base/logging.h"
18 #include "base/message_loop/message_loop_current.h"
19 #include "base/optional.h"
20 #include "base/posix/eintr_wrapper.h"
21 #include "base/threading/scoped_blocking_call.h"
22 #include "net/base/network_interfaces_linux.h"
23
24 namespace net {
25 namespace internal {
26
27 namespace {
28
29 // Some kernel functions such as wireless_send_event and rtnetlink_ifinfo_prep
30 // may send spurious messages over rtnetlink. RTM_NEWLINK messages where
31 // ifi_change == 0 and rta_type == IFLA_WIRELESS should be ignored.
IgnoreWirelessChange(const struct ifinfomsg * msg,int length)32 bool IgnoreWirelessChange(const struct ifinfomsg* msg, int length) {
33 for (const struct rtattr* attr = IFLA_RTA(msg); RTA_OK(attr, length);
34 attr = RTA_NEXT(attr, length)) {
35 if (attr->rta_type == IFLA_WIRELESS && msg->ifi_change == 0)
36 return true;
37 }
38 return false;
39 }
40
41 // Retrieves address from NETLINK address message.
42 // Sets |really_deprecated| for IPv6 addresses with preferred lifetimes of 0.
43 // Precondition: |header| must already be validated with NLMSG_OK.
GetAddress(const struct nlmsghdr * header,int header_length,IPAddress * out,bool * really_deprecated)44 bool GetAddress(const struct nlmsghdr* header,
45 int header_length,
46 IPAddress* out,
47 bool* really_deprecated) {
48 if (really_deprecated)
49 *really_deprecated = false;
50
51 // Extract the message and update |header_length| to be the number of
52 // remaining bytes.
53 const struct ifaddrmsg* msg =
54 reinterpret_cast<const struct ifaddrmsg*>(NLMSG_DATA(header));
55 header_length -= NLMSG_HDRLEN;
56
57 size_t address_length = 0;
58 switch (msg->ifa_family) {
59 case AF_INET:
60 address_length = IPAddress::kIPv4AddressSize;
61 break;
62 case AF_INET6:
63 address_length = IPAddress::kIPv6AddressSize;
64 break;
65 default:
66 // Unknown family.
67 return false;
68 }
69 // Use IFA_ADDRESS unless IFA_LOCAL is present. This behavior here is based on
70 // getaddrinfo in glibc (check_pf.c). Judging from kernel implementation of
71 // NETLINK, IPv4 addresses have only the IFA_ADDRESS attribute, while IPv6
72 // have the IFA_LOCAL attribute.
73 uint8_t* address = NULL;
74 uint8_t* local = NULL;
75 int length = IFA_PAYLOAD(header);
76 if (length > header_length) {
77 LOG(ERROR) << "ifaddrmsg length exceeds bounds";
78 return false;
79 }
80 for (const struct rtattr* attr =
81 reinterpret_cast<const struct rtattr*>(IFA_RTA(msg));
82 RTA_OK(attr, length); attr = RTA_NEXT(attr, length)) {
83 switch (attr->rta_type) {
84 case IFA_ADDRESS:
85 if (RTA_PAYLOAD(attr) < address_length) {
86 LOG(ERROR) << "attr does not have enough bytes to read an address";
87 return false;
88 }
89 address = reinterpret_cast<uint8_t*>(RTA_DATA(attr));
90 break;
91 case IFA_LOCAL:
92 if (RTA_PAYLOAD(attr) < address_length) {
93 LOG(ERROR) << "attr does not have enough bytes to read an address";
94 return false;
95 }
96 local = reinterpret_cast<uint8_t*>(RTA_DATA(attr));
97 break;
98 case IFA_CACHEINFO: {
99 if (RTA_PAYLOAD(attr) < sizeof(struct ifa_cacheinfo)) {
100 LOG(ERROR)
101 << "attr does not have enough bytes to read an ifa_cacheinfo";
102 return false;
103 }
104 const struct ifa_cacheinfo* cache_info =
105 reinterpret_cast<const struct ifa_cacheinfo*>(RTA_DATA(attr));
106 if (really_deprecated)
107 *really_deprecated = (cache_info->ifa_prefered == 0);
108 } break;
109 default:
110 break;
111 }
112 }
113 if (local)
114 address = local;
115 if (!address)
116 return false;
117 *out = IPAddress(address, address_length);
118 return true;
119 }
120
121 // SafelyCastNetlinkMsgData<T> performs a bounds check before casting |header|'s
122 // data to a |T*|. When the bounds check fails, returns nullptr.
123 template <typename T>
SafelyCastNetlinkMsgData(const struct nlmsghdr * header,int length)124 T* SafelyCastNetlinkMsgData(const struct nlmsghdr* header, int length) {
125 DCHECK(NLMSG_OK(header, static_cast<__u32>(length)));
126 if (length <= 0 || static_cast<size_t>(length) < NLMSG_HDRLEN + sizeof(T))
127 return nullptr;
128 return reinterpret_cast<const T*>(NLMSG_DATA(header));
129 }
130
131 } // namespace
132
133 // static
GetInterfaceName(int interface_index,char * buf)134 char* AddressTrackerLinux::GetInterfaceName(int interface_index, char* buf) {
135 memset(buf, 0, IFNAMSIZ);
136 base::ScopedFD ioctl_socket = GetSocketForIoctl();
137 if (!ioctl_socket.is_valid())
138 return buf;
139
140 struct ifreq ifr = {};
141 ifr.ifr_ifindex = interface_index;
142
143 if (ioctl(ioctl_socket.get(), SIOCGIFNAME, &ifr) == 0)
144 strncpy(buf, ifr.ifr_name, IFNAMSIZ - 1);
145 return buf;
146 }
147
AddressTrackerLinux()148 AddressTrackerLinux::AddressTrackerLinux()
149 : get_interface_name_(GetInterfaceName),
150 address_callback_(base::DoNothing()),
151 link_callback_(base::DoNothing()),
152 tunnel_callback_(base::DoNothing()),
153 ignored_interfaces_(),
154 connection_type_initialized_(false),
155 connection_type_initialized_cv_(&connection_type_lock_),
156 current_connection_type_(NetworkChangeNotifier::CONNECTION_NONE),
157 tracking_(false),
158 threads_waiting_for_connection_type_initialization_(0) {}
159
AddressTrackerLinux(const base::RepeatingClosure & address_callback,const base::RepeatingClosure & link_callback,const base::RepeatingClosure & tunnel_callback,const std::unordered_set<std::string> & ignored_interfaces)160 AddressTrackerLinux::AddressTrackerLinux(
161 const base::RepeatingClosure& address_callback,
162 const base::RepeatingClosure& link_callback,
163 const base::RepeatingClosure& tunnel_callback,
164 const std::unordered_set<std::string>& ignored_interfaces)
165 : get_interface_name_(GetInterfaceName),
166 address_callback_(address_callback),
167 link_callback_(link_callback),
168 tunnel_callback_(tunnel_callback),
169 ignored_interfaces_(ignored_interfaces),
170 connection_type_initialized_(false),
171 connection_type_initialized_cv_(&connection_type_lock_),
172 current_connection_type_(NetworkChangeNotifier::CONNECTION_NONE),
173 tracking_(true),
174 threads_waiting_for_connection_type_initialization_(0) {
175 DCHECK(!address_callback.is_null());
176 DCHECK(!link_callback.is_null());
177 }
178
179 AddressTrackerLinux::~AddressTrackerLinux() = default;
180
Init()181 void AddressTrackerLinux::Init() {
182 #if !defined(OS_FREEBSD)
183 netlink_fd_.reset(socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE));
184 if (!netlink_fd_.is_valid()) {
185 PLOG(ERROR) << "Could not create NETLINK socket";
186 AbortAndForceOnline();
187 return;
188 }
189
190 int rv;
191
192 if (tracking_) {
193 // Request notifications.
194 struct sockaddr_nl addr = {};
195 addr.nl_family = AF_NETLINK;
196 addr.nl_pid = getpid();
197 // TODO(szym): Track RTMGRP_LINK as well for ifi_type,
198 // http://crbug.com/113993
199 addr.nl_groups =
200 RTMGRP_IPV4_IFADDR | RTMGRP_IPV6_IFADDR | RTMGRP_NOTIFY | RTMGRP_LINK;
201 rv = bind(netlink_fd_.get(), reinterpret_cast<struct sockaddr*>(&addr),
202 sizeof(addr));
203 if (rv < 0) {
204 PLOG(ERROR) << "Could not bind NETLINK socket";
205 AbortAndForceOnline();
206 return;
207 }
208 }
209
210 // Request dump of addresses.
211 struct sockaddr_nl peer = {};
212 peer.nl_family = AF_NETLINK;
213
214 struct {
215 struct nlmsghdr header;
216 struct rtgenmsg msg;
217 } request = {};
218
219 request.header.nlmsg_len = NLMSG_LENGTH(sizeof(request.msg));
220 request.header.nlmsg_type = RTM_GETADDR;
221 request.header.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
222 request.header.nlmsg_pid = getpid();
223 request.msg.rtgen_family = AF_UNSPEC;
224
225 rv = HANDLE_EINTR(
226 sendto(netlink_fd_.get(), &request, request.header.nlmsg_len, 0,
227 reinterpret_cast<struct sockaddr*>(&peer), sizeof(peer)));
228 if (rv < 0) {
229 PLOG(ERROR) << "Could not send NETLINK request";
230 AbortAndForceOnline();
231 return;
232 }
233
234 // Consume pending message to populate the AddressMap, but don't notify.
235 // Sending another request without first reading responses results in EBUSY.
236 bool address_changed;
237 bool link_changed;
238 bool tunnel_changed;
239 ReadMessages(&address_changed, &link_changed, &tunnel_changed);
240
241 // Request dump of link state
242 request.header.nlmsg_type = RTM_GETLINK;
243
244 rv = HANDLE_EINTR(
245 sendto(netlink_fd_.get(), &request, request.header.nlmsg_len, 0,
246 reinterpret_cast<struct sockaddr*>(&peer), sizeof(peer)));
247 if (rv < 0) {
248 PLOG(ERROR) << "Could not send NETLINK request";
249 AbortAndForceOnline();
250 return;
251 }
252
253 // Consume pending message to populate links_online_, but don't notify.
254 ReadMessages(&address_changed, &link_changed, &tunnel_changed);
255 {
256 AddressTrackerAutoLock lock(*this, connection_type_lock_);
257 connection_type_initialized_ = true;
258 connection_type_initialized_cv_.Broadcast();
259 }
260
261 if (tracking_) {
262 watcher_ = base::FileDescriptorWatcher::WatchReadable(
263 netlink_fd_.get(),
264 base::BindRepeating(&AddressTrackerLinux::OnFileCanReadWithoutBlocking,
265 base::Unretained(this)));
266 }
267 }
268
AbortAndForceOnline()269 void AddressTrackerLinux::AbortAndForceOnline() {
270 watcher_.reset();
271 netlink_fd_.reset();
272 AddressTrackerAutoLock lock(*this, connection_type_lock_);
273 current_connection_type_ = NetworkChangeNotifier::CONNECTION_UNKNOWN;
274 connection_type_initialized_ = true;
275 connection_type_initialized_cv_.Broadcast();
276 }
277
278 #if !defined(OS_BSD)
GetAddressMap() const279 AddressTrackerLinux::AddressMap AddressTrackerLinux::GetAddressMap() const {
280 AddressTrackerAutoLock lock(*this, address_map_lock_);
281 return address_map_;
282 }
283
GetOnlineLinks() const284 std::unordered_set<int> AddressTrackerLinux::GetOnlineLinks() const {
285 AddressTrackerAutoLock lock(*this, online_links_lock_);
286 return online_links_;
287 }
288
IsInterfaceIgnored(int interface_index) const289 bool AddressTrackerLinux::IsInterfaceIgnored(int interface_index) const {
290 if (ignored_interfaces_.empty())
291 return false;
292
293 char buf[IFNAMSIZ] = {0};
294 const char* interface_name = get_interface_name_(interface_index, buf);
295 return ignored_interfaces_.find(interface_name) != ignored_interfaces_.end();
296 }
297 #endif // !OS_BSD
298
299 NetworkChangeNotifier::ConnectionType
GetCurrentConnectionType()300 AddressTrackerLinux::GetCurrentConnectionType() {
301 // http://crbug.com/125097
302 base::ScopedAllowBaseSyncPrimitivesOutsideBlockingScope allow_wait;
303 AddressTrackerAutoLock lock(*this, connection_type_lock_);
304 // Make sure the initial connection type is set before returning.
305 threads_waiting_for_connection_type_initialization_++;
306 while (!connection_type_initialized_) {
307 connection_type_initialized_cv_.Wait();
308 }
309 threads_waiting_for_connection_type_initialization_--;
310 return current_connection_type_;
311 }
312
ReadMessages(bool * address_changed,bool * link_changed,bool * tunnel_changed)313 void AddressTrackerLinux::ReadMessages(bool* address_changed,
314 bool* link_changed,
315 bool* tunnel_changed) {
316 *address_changed = false;
317 *link_changed = false;
318 *tunnel_changed = false;
319 char buffer[4096];
320 bool first_loop = true;
321 {
322 base::Optional<base::ScopedBlockingCall> blocking_call;
323 if (tracking_) {
324 // If the loop below takes a long time to run, a new thread should added
325 // to the current thread pool to ensure forward progress of all tasks.
326 blocking_call.emplace(FROM_HERE, base::BlockingType::MAY_BLOCK);
327 }
328
329 for (;;) {
330 int rv = HANDLE_EINTR(recv(netlink_fd_.get(), buffer, sizeof(buffer),
331 // Block the first time through loop.
332 first_loop ? 0 : MSG_DONTWAIT));
333 first_loop = false;
334 if (rv == 0) {
335 LOG(ERROR) << "Unexpected shutdown of NETLINK socket.";
336 return;
337 }
338 if (rv < 0) {
339 if ((errno == EAGAIN) || (errno == EWOULDBLOCK))
340 break;
341 PLOG(ERROR) << "Failed to recv from netlink socket";
342 return;
343 }
344 HandleMessage(buffer, rv, address_changed, link_changed, tunnel_changed);
345 }
346 }
347 if (*link_changed || *address_changed)
348 UpdateCurrentConnectionType();
349 }
350
HandleMessage(const char * buffer,int length,bool * address_changed,bool * link_changed,bool * tunnel_changed)351 void AddressTrackerLinux::HandleMessage(const char* buffer,
352 int length,
353 bool* address_changed,
354 bool* link_changed,
355 bool* tunnel_changed) {
356 #if !defined(OS_FREEBSD)
357 DCHECK(buffer);
358 // Note that NLMSG_NEXT decrements |length| to reflect the number of bytes
359 // remaining in |buffer|.
360 for (const struct nlmsghdr* header =
361 reinterpret_cast<const struct nlmsghdr*>(buffer);
362 length >= 0 && NLMSG_OK(header, static_cast<__u32>(length));
363 header = NLMSG_NEXT(header, length)) {
364 // The |header| pointer should never precede |buffer|.
365 DCHECK_LE(buffer, reinterpret_cast<const char*>(header));
366 switch (header->nlmsg_type) {
367 case NLMSG_DONE:
368 return;
369 case NLMSG_ERROR: {
370 const struct nlmsgerr* msg =
371 SafelyCastNetlinkMsgData<const struct nlmsgerr>(header, length);
372 if (msg == nullptr)
373 return;
374 LOG(ERROR) << "Unexpected netlink error " << msg->error << ".";
375 } return;
376 case RTM_NEWADDR: {
377 IPAddress address;
378 bool really_deprecated;
379 const struct ifaddrmsg* msg =
380 SafelyCastNetlinkMsgData<const struct ifaddrmsg>(header, length);
381 if (msg == nullptr)
382 return;
383 if (IsInterfaceIgnored(msg->ifa_index))
384 break;
385 if (GetAddress(header, length, &address, &really_deprecated)) {
386 struct ifaddrmsg msg_copy = *msg;
387 AddressTrackerAutoLock lock(*this, address_map_lock_);
388 // Routers may frequently (every few seconds) output the IPv6 ULA
389 // prefix which can cause the linux kernel to frequently output two
390 // back-to-back messages, one without the deprecated flag and one with
391 // the deprecated flag but both with preferred lifetimes of 0. Avoid
392 // interpretting this as an actual change by canonicalizing the two
393 // messages by setting the deprecated flag based on the preferred
394 // lifetime also. http://crbug.com/268042
395 if (really_deprecated)
396 msg_copy.ifa_flags |= IFA_F_DEPRECATED;
397 // Only indicate change if the address is new or ifaddrmsg info has
398 // changed.
399 auto it = address_map_.find(address);
400 if (it == address_map_.end()) {
401 address_map_.insert(it, std::make_pair(address, msg_copy));
402 *address_changed = true;
403 } else if (memcmp(&it->second, &msg_copy, sizeof(msg_copy))) {
404 it->second = msg_copy;
405 *address_changed = true;
406 }
407 }
408 } break;
409 case RTM_DELADDR: {
410 IPAddress address;
411 const struct ifaddrmsg* msg =
412 SafelyCastNetlinkMsgData<const struct ifaddrmsg>(header, length);
413 if (msg == nullptr)
414 return;
415 if (IsInterfaceIgnored(msg->ifa_index))
416 break;
417 if (GetAddress(header, length, &address, nullptr)) {
418 AddressTrackerAutoLock lock(*this, address_map_lock_);
419 if (address_map_.erase(address))
420 *address_changed = true;
421 }
422 } break;
423 case RTM_NEWLINK: {
424 const struct ifinfomsg* msg =
425 SafelyCastNetlinkMsgData<const struct ifinfomsg>(header, length);
426 if (msg == nullptr)
427 return;
428 if (IsInterfaceIgnored(msg->ifi_index))
429 break;
430 if (IgnoreWirelessChange(msg, IFLA_PAYLOAD(header))) {
431 VLOG(2) << "Ignoring RTM_NEWLINK message";
432 break;
433 }
434 if (!(msg->ifi_flags & IFF_LOOPBACK) && (msg->ifi_flags & IFF_UP) &&
435 (msg->ifi_flags & IFF_LOWER_UP) && (msg->ifi_flags & IFF_RUNNING)) {
436 AddressTrackerAutoLock lock(*this, online_links_lock_);
437 if (online_links_.insert(msg->ifi_index).second) {
438 *link_changed = true;
439 if (IsTunnelInterface(msg->ifi_index))
440 *tunnel_changed = true;
441 }
442 } else {
443 AddressTrackerAutoLock lock(*this, online_links_lock_);
444 if (online_links_.erase(msg->ifi_index)) {
445 *link_changed = true;
446 if (IsTunnelInterface(msg->ifi_index))
447 *tunnel_changed = true;
448 }
449 }
450 } break;
451 case RTM_DELLINK: {
452 const struct ifinfomsg* msg =
453 SafelyCastNetlinkMsgData<const struct ifinfomsg>(header, length);
454 if (msg == nullptr)
455 return;
456 if (IsInterfaceIgnored(msg->ifi_index))
457 break;
458 AddressTrackerAutoLock lock(*this, online_links_lock_);
459 if (online_links_.erase(msg->ifi_index)) {
460 *link_changed = true;
461 if (IsTunnelInterface(msg->ifi_index))
462 *tunnel_changed = true;
463 }
464 } break;
465 default:
466 break;
467 }
468 }
469 #else // !OS_FREEBSD
470 NOTIMPLEMENTED();
471 AbortAndForceOnline();
472 #endif // !OS_FREEBSD
473 }
474
OnFileCanReadWithoutBlocking()475 void AddressTrackerLinux::OnFileCanReadWithoutBlocking() {
476 bool address_changed;
477 bool link_changed;
478 bool tunnel_changed;
479 ReadMessages(&address_changed, &link_changed, &tunnel_changed);
480 if (address_changed)
481 address_callback_.Run();
482 if (link_changed)
483 link_callback_.Run();
484 if (tunnel_changed)
485 tunnel_callback_.Run();
486 }
487
IsTunnelInterface(int interface_index) const488 bool AddressTrackerLinux::IsTunnelInterface(int interface_index) const {
489 char buf[IFNAMSIZ] = {0};
490 return IsTunnelInterfaceName(get_interface_name_(interface_index, buf));
491 }
492
493 // static
IsTunnelInterfaceName(const char * name)494 bool AddressTrackerLinux::IsTunnelInterfaceName(const char* name) {
495 // Linux kernel drivers/net/tun.c uses "tun" name prefix.
496 return strncmp(name, "tun", 3) == 0;
497 }
498
UpdateCurrentConnectionType()499 void AddressTrackerLinux::UpdateCurrentConnectionType() {
500 NOTIMPLEMENTED();
501 }
502
GetThreadsWaitingForConnectionTypeInitForTesting()503 int AddressTrackerLinux::GetThreadsWaitingForConnectionTypeInitForTesting() {
504 AddressTrackerAutoLock lock(*this, connection_type_lock_);
505 return threads_waiting_for_connection_type_initialization_;
506 }
507
AddressTrackerAutoLock(const AddressTrackerLinux & tracker,base::Lock & lock)508 AddressTrackerLinux::AddressTrackerAutoLock::AddressTrackerAutoLock(
509 const AddressTrackerLinux& tracker,
510 base::Lock& lock)
511 : tracker_(tracker), lock_(lock) {
512 if (tracker_.tracking_) {
513 lock_.Acquire();
514 } else {
515 DCHECK(tracker_.thread_checker_.CalledOnValidThread());
516 }
517 }
518
~AddressTrackerAutoLock()519 AddressTrackerLinux::AddressTrackerAutoLock::~AddressTrackerAutoLock() {
520 if (tracker_.tracking_) {
521 lock_.AssertAcquired();
522 lock_.Release();
523 }
524 }
525
526 } // namespace internal
527 } // namespace net
528