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.
5 #include "net/base/address_tracker_linux.h"
11 #include "base/files/scoped_file.h"
12 #include "base/logging.h"
13 #include "base/posix/eintr_wrapper.h"
14 #include "base/threading/thread_restrictions.h"
15 #include "net/base/network_interfaces_linux.h"
16 #include "net/base/ip_address_number.h"
23 // Some kernel functions such as wireless_send_event and rtnetlink_ifinfo_prep
24 // may send spurious messages over rtnetlink. RTM_NEWLINK messages where
25 // ifi_change == 0 and rta_type == IFLA_WIRELESS should be ignored.
26 bool IgnoreWirelessChange(const struct nlmsghdr
* header
,
27 const struct ifinfomsg
* msg
) {
28 size_t length
= IFLA_PAYLOAD(header
);
29 for (const struct rtattr
* attr
= IFLA_RTA(msg
); RTA_OK(attr
, length
);
30 attr
= RTA_NEXT(attr
, length
)) {
31 if (attr
->rta_type
== IFLA_WIRELESS
&& msg
->ifi_change
== 0)
37 // Retrieves address from NETLINK address message.
38 // Sets |really_deprecated| for IPv6 addresses with preferred lifetimes of 0.
39 bool GetAddress(const struct nlmsghdr
* header
,
41 bool* really_deprecated
) {
42 if (really_deprecated
)
43 *really_deprecated
= false;
44 const struct ifaddrmsg
* msg
=
45 reinterpret_cast<struct ifaddrmsg
*>(NLMSG_DATA(header
));
46 size_t address_length
= 0;
47 switch (msg
->ifa_family
) {
49 address_length
= kIPv4AddressSize
;
52 address_length
= kIPv6AddressSize
;
58 // Use IFA_ADDRESS unless IFA_LOCAL is present. This behavior here is based on
59 // getaddrinfo in glibc (check_pf.c). Judging from kernel implementation of
60 // NETLINK, IPv4 addresses have only the IFA_ADDRESS attribute, while IPv6
61 // have the IFA_LOCAL attribute.
62 unsigned char* address
= NULL
;
63 unsigned char* local
= NULL
;
64 size_t length
= IFA_PAYLOAD(header
);
65 for (const struct rtattr
* attr
=
66 reinterpret_cast<const struct rtattr
*>(IFA_RTA(msg
));
68 attr
= RTA_NEXT(attr
, length
)) {
69 switch (attr
->rta_type
) {
71 DCHECK_GE(RTA_PAYLOAD(attr
), address_length
);
72 address
= reinterpret_cast<unsigned char*>(RTA_DATA(attr
));
75 DCHECK_GE(RTA_PAYLOAD(attr
), address_length
);
76 local
= reinterpret_cast<unsigned char*>(RTA_DATA(attr
));
79 const struct ifa_cacheinfo
*cache_info
=
80 reinterpret_cast<const struct ifa_cacheinfo
*>(RTA_DATA(attr
));
81 if (really_deprecated
)
82 *really_deprecated
= (cache_info
->ifa_prefered
== 0);
92 out
->assign(address
, address
+ address_length
);
99 char* AddressTrackerLinux::GetInterfaceName(int interface_index
, char* buf
) {
100 memset(buf
, 0, IFNAMSIZ
);
101 base::ScopedFD
ioctl_socket(socket(AF_INET
, SOCK_DGRAM
, 0));
102 if (!ioctl_socket
.is_valid())
105 struct ifreq ifr
= {};
106 ifr
.ifr_ifindex
= interface_index
;
108 if (ioctl(ioctl_socket
.get(), SIOCGIFNAME
, &ifr
) == 0)
109 strncpy(buf
, ifr
.ifr_name
, IFNAMSIZ
- 1);
113 AddressTrackerLinux::AddressTrackerLinux()
114 : get_interface_name_(GetInterfaceName
),
115 address_callback_(base::Bind(&base::DoNothing
)),
116 link_callback_(base::Bind(&base::DoNothing
)),
117 tunnel_callback_(base::Bind(&base::DoNothing
)),
119 connection_type_initialized_(false),
120 connection_type_initialized_cv_(&connection_type_lock_
),
121 current_connection_type_(NetworkChangeNotifier::CONNECTION_NONE
),
125 AddressTrackerLinux::AddressTrackerLinux(
126 const base::Closure
& address_callback
,
127 const base::Closure
& link_callback
,
128 const base::Closure
& tunnel_callback
,
129 const base::hash_set
<std::string
>& ignored_interfaces
)
130 : get_interface_name_(GetInterfaceName
),
131 address_callback_(address_callback
),
132 link_callback_(link_callback
),
133 tunnel_callback_(tunnel_callback
),
135 ignored_interfaces_(ignored_interfaces
),
136 connection_type_initialized_(false),
137 connection_type_initialized_cv_(&connection_type_lock_
),
138 current_connection_type_(NetworkChangeNotifier::CONNECTION_NONE
),
140 DCHECK(!address_callback
.is_null());
141 DCHECK(!link_callback
.is_null());
144 AddressTrackerLinux::~AddressTrackerLinux() {
148 void AddressTrackerLinux::Init() {
149 netlink_fd_
= socket(AF_NETLINK
, SOCK_RAW
, NETLINK_ROUTE
);
150 if (netlink_fd_
< 0) {
151 PLOG(ERROR
) << "Could not create NETLINK socket";
152 AbortAndForceOnline();
159 // Request notifications.
160 struct sockaddr_nl addr
= {};
161 addr
.nl_family
= AF_NETLINK
;
162 addr
.nl_pid
= getpid();
163 // TODO(szym): Track RTMGRP_LINK as well for ifi_type,
164 // http://crbug.com/113993
166 RTMGRP_IPV4_IFADDR
| RTMGRP_IPV6_IFADDR
| RTMGRP_NOTIFY
| RTMGRP_LINK
;
168 netlink_fd_
, reinterpret_cast<struct sockaddr
*>(&addr
), sizeof(addr
));
170 PLOG(ERROR
) << "Could not bind NETLINK socket";
171 AbortAndForceOnline();
176 // Request dump of addresses.
177 struct sockaddr_nl peer
= {};
178 peer
.nl_family
= AF_NETLINK
;
181 struct nlmsghdr header
;
185 request
.header
.nlmsg_len
= NLMSG_LENGTH(sizeof(request
.msg
));
186 request
.header
.nlmsg_type
= RTM_GETADDR
;
187 request
.header
.nlmsg_flags
= NLM_F_REQUEST
| NLM_F_DUMP
;
188 request
.header
.nlmsg_pid
= getpid();
189 request
.msg
.rtgen_family
= AF_UNSPEC
;
191 rv
= HANDLE_EINTR(sendto(netlink_fd_
, &request
, request
.header
.nlmsg_len
,
192 0, reinterpret_cast<struct sockaddr
*>(&peer
),
195 PLOG(ERROR
) << "Could not send NETLINK request";
196 AbortAndForceOnline();
200 // Consume pending message to populate the AddressMap, but don't notify.
201 // Sending another request without first reading responses results in EBUSY.
202 bool address_changed
;
205 ReadMessages(&address_changed
, &link_changed
, &tunnel_changed
);
207 // Request dump of link state
208 request
.header
.nlmsg_type
= RTM_GETLINK
;
210 rv
= HANDLE_EINTR(sendto(netlink_fd_
, &request
, request
.header
.nlmsg_len
, 0,
211 reinterpret_cast<struct sockaddr
*>(&peer
),
214 PLOG(ERROR
) << "Could not send NETLINK request";
215 AbortAndForceOnline();
219 // Consume pending message to populate links_online_, but don't notify.
220 ReadMessages(&address_changed
, &link_changed
, &tunnel_changed
);
222 AddressTrackerAutoLock
lock(*this, connection_type_lock_
);
223 connection_type_initialized_
= true;
224 connection_type_initialized_cv_
.Signal();
228 rv
= base::MessageLoopForIO::current()->WatchFileDescriptor(
229 netlink_fd_
, true, base::MessageLoopForIO::WATCH_READ
, &watcher_
, this);
231 PLOG(ERROR
) << "Could not watch NETLINK socket";
232 AbortAndForceOnline();
238 void AddressTrackerLinux::AbortAndForceOnline() {
240 AddressTrackerAutoLock
lock(*this, connection_type_lock_
);
241 current_connection_type_
= NetworkChangeNotifier::CONNECTION_UNKNOWN
;
242 connection_type_initialized_
= true;
243 connection_type_initialized_cv_
.Signal();
246 AddressTrackerLinux::AddressMap
AddressTrackerLinux::GetAddressMap() const {
247 AddressTrackerAutoLock
lock(*this, address_map_lock_
);
251 base::hash_set
<int> AddressTrackerLinux::GetOnlineLinks() const {
252 AddressTrackerAutoLock
lock(*this, online_links_lock_
);
253 return online_links_
;
256 bool AddressTrackerLinux::IsInterfaceIgnored(int interface_index
) const {
257 if (ignored_interfaces_
.empty())
260 char buf
[IFNAMSIZ
] = {0};
261 const char* interface_name
= get_interface_name_(interface_index
, buf
);
262 return ignored_interfaces_
.find(interface_name
) != ignored_interfaces_
.end();
265 NetworkChangeNotifier::ConnectionType
266 AddressTrackerLinux::GetCurrentConnectionType() {
267 // http://crbug.com/125097
268 base::ThreadRestrictions::ScopedAllowWait allow_wait
;
269 AddressTrackerAutoLock
lock(*this, connection_type_lock_
);
270 // Make sure the initial connection type is set before returning.
271 while (!connection_type_initialized_
) {
272 connection_type_initialized_cv_
.Wait();
274 return current_connection_type_
;
277 void AddressTrackerLinux::ReadMessages(bool* address_changed
,
279 bool* tunnel_changed
) {
280 *address_changed
= false;
281 *link_changed
= false;
282 *tunnel_changed
= false;
284 bool first_loop
= true;
286 int rv
= HANDLE_EINTR(recv(netlink_fd_
,
289 // Block the first time through loop.
290 first_loop
? 0 : MSG_DONTWAIT
));
293 LOG(ERROR
) << "Unexpected shutdown of NETLINK socket.";
297 if ((errno
== EAGAIN
) || (errno
== EWOULDBLOCK
))
299 PLOG(ERROR
) << "Failed to recv from netlink socket";
302 HandleMessage(buffer
, rv
, address_changed
, link_changed
, tunnel_changed
);
304 if (*link_changed
|| *address_changed
)
305 UpdateCurrentConnectionType();
308 void AddressTrackerLinux::HandleMessage(char* buffer
,
310 bool* address_changed
,
312 bool* tunnel_changed
) {
314 for (struct nlmsghdr
* header
= reinterpret_cast<struct nlmsghdr
*>(buffer
);
315 NLMSG_OK(header
, length
);
316 header
= NLMSG_NEXT(header
, length
)) {
317 switch (header
->nlmsg_type
) {
321 const struct nlmsgerr
* msg
=
322 reinterpret_cast<struct nlmsgerr
*>(NLMSG_DATA(header
));
323 LOG(ERROR
) << "Unexpected netlink error " << msg
->error
<< ".";
326 IPAddressNumber address
;
327 bool really_deprecated
;
328 struct ifaddrmsg
* msg
=
329 reinterpret_cast<struct ifaddrmsg
*>(NLMSG_DATA(header
));
330 if (IsInterfaceIgnored(msg
->ifa_index
))
332 if (GetAddress(header
, &address
, &really_deprecated
)) {
333 AddressTrackerAutoLock
lock(*this, address_map_lock_
);
334 // Routers may frequently (every few seconds) output the IPv6 ULA
335 // prefix which can cause the linux kernel to frequently output two
336 // back-to-back messages, one without the deprecated flag and one with
337 // the deprecated flag but both with preferred lifetimes of 0. Avoid
338 // interpretting this as an actual change by canonicalizing the two
339 // messages by setting the deprecated flag based on the preferred
340 // lifetime also. http://crbug.com/268042
341 if (really_deprecated
)
342 msg
->ifa_flags
|= IFA_F_DEPRECATED
;
343 // Only indicate change if the address is new or ifaddrmsg info has
345 AddressMap::iterator it
= address_map_
.find(address
);
346 if (it
== address_map_
.end()) {
347 address_map_
.insert(it
, std::make_pair(address
, *msg
));
348 *address_changed
= true;
349 } else if (memcmp(&it
->second
, msg
, sizeof(*msg
))) {
351 *address_changed
= true;
356 IPAddressNumber address
;
357 const struct ifaddrmsg
* msg
=
358 reinterpret_cast<struct ifaddrmsg
*>(NLMSG_DATA(header
));
359 if (IsInterfaceIgnored(msg
->ifa_index
))
361 if (GetAddress(header
, &address
, NULL
)) {
362 AddressTrackerAutoLock
lock(*this, address_map_lock_
);
363 if (address_map_
.erase(address
))
364 *address_changed
= true;
368 const struct ifinfomsg
* msg
=
369 reinterpret_cast<struct ifinfomsg
*>(NLMSG_DATA(header
));
370 if (IsInterfaceIgnored(msg
->ifi_index
))
372 if (IgnoreWirelessChange(header
, msg
)) {
373 VLOG(2) << "Ignoring RTM_NEWLINK message";
376 if (!(msg
->ifi_flags
& IFF_LOOPBACK
) && (msg
->ifi_flags
& IFF_UP
) &&
377 (msg
->ifi_flags
& IFF_LOWER_UP
) && (msg
->ifi_flags
& IFF_RUNNING
)) {
378 AddressTrackerAutoLock
lock(*this, online_links_lock_
);
379 if (online_links_
.insert(msg
->ifi_index
).second
) {
380 *link_changed
= true;
381 if (IsTunnelInterface(msg
->ifi_index
))
382 *tunnel_changed
= true;
385 AddressTrackerAutoLock
lock(*this, online_links_lock_
);
386 if (online_links_
.erase(msg
->ifi_index
)) {
387 *link_changed
= true;
388 if (IsTunnelInterface(msg
->ifi_index
))
389 *tunnel_changed
= true;
394 const struct ifinfomsg
* msg
=
395 reinterpret_cast<struct ifinfomsg
*>(NLMSG_DATA(header
));
396 if (IsInterfaceIgnored(msg
->ifi_index
))
398 AddressTrackerAutoLock
lock(*this, online_links_lock_
);
399 if (online_links_
.erase(msg
->ifi_index
)) {
400 *link_changed
= true;
401 if (IsTunnelInterface(msg
->ifi_index
))
402 *tunnel_changed
= true;
411 void AddressTrackerLinux::OnFileCanReadWithoutBlocking(int fd
) {
412 DCHECK_EQ(netlink_fd_
, fd
);
413 bool address_changed
;
416 ReadMessages(&address_changed
, &link_changed
, &tunnel_changed
);
418 address_callback_
.Run();
420 link_callback_
.Run();
422 tunnel_callback_
.Run();
425 void AddressTrackerLinux::OnFileCanWriteWithoutBlocking(int /* fd */) {}
427 void AddressTrackerLinux::CloseSocket() {
428 if (netlink_fd_
>= 0 && IGNORE_EINTR(close(netlink_fd_
)) < 0)
429 PLOG(ERROR
) << "Could not close NETLINK socket.";
433 bool AddressTrackerLinux::IsTunnelInterface(int interface_index
) const {
434 // Linux kernel drivers/net/tun.c uses "tun" name prefix.
435 char buf
[IFNAMSIZ
] = {0};
436 return strncmp(get_interface_name_(interface_index
, buf
), "tun", 3) == 0;
439 void AddressTrackerLinux::UpdateCurrentConnectionType() {
440 AddressTrackerLinux::AddressMap address_map
= GetAddressMap();
441 base::hash_set
<int> online_links
= GetOnlineLinks();
443 // Strip out tunnel interfaces from online_links
444 for (base::hash_set
<int>::const_iterator it
= online_links
.begin();
445 it
!= online_links
.end();) {
446 if (IsTunnelInterface(*it
)) {
447 base::hash_set
<int>::const_iterator tunnel_it
= it
;
449 online_links
.erase(*tunnel_it
);
455 NetworkInterfaceList networks
;
456 NetworkChangeNotifier::ConnectionType type
=
457 NetworkChangeNotifier::CONNECTION_NONE
;
458 if (GetNetworkListImpl(&networks
, 0, online_links
, address_map
,
459 get_interface_name_
)) {
460 type
= NetworkChangeNotifier::ConnectionTypeFromInterfaceList(networks
);
462 type
= online_links
.empty() ? NetworkChangeNotifier::CONNECTION_NONE
463 : NetworkChangeNotifier::CONNECTION_UNKNOWN
;
466 AddressTrackerAutoLock
lock(*this, connection_type_lock_
);
467 current_connection_type_
= type
;
470 AddressTrackerLinux::AddressTrackerAutoLock::AddressTrackerAutoLock(
471 const AddressTrackerLinux
& tracker
,
473 : tracker_(tracker
), lock_(lock
) {
474 if (tracker_
.tracking_
) {
477 DCHECK(tracker_
.thread_checker_
.CalledOnValidThread());
481 AddressTrackerLinux::AddressTrackerAutoLock::~AddressTrackerAutoLock() {
482 if (tracker_
.tracking_
) {
483 lock_
.AssertAcquired();
488 } // namespace internal