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/logging.h"
12 #include "base/posix/eintr_wrapper.h"
13 #include "base/threading/thread_restrictions.h"
20 // Retrieves address from NETLINK address message.
21 // Sets |really_deprecated| for IPv6 addresses with preferred lifetimes of 0.
22 bool GetAddress(const struct nlmsghdr
* header
,
24 bool* really_deprecated
) {
25 if (really_deprecated
)
26 *really_deprecated
= false;
27 const struct ifaddrmsg
* msg
=
28 reinterpret_cast<struct ifaddrmsg
*>(NLMSG_DATA(header
));
29 size_t address_length
= 0;
30 switch (msg
->ifa_family
) {
32 address_length
= kIPv4AddressSize
;
35 address_length
= kIPv6AddressSize
;
41 // Use IFA_ADDRESS unless IFA_LOCAL is present. This behavior here is based on
42 // getaddrinfo in glibc (check_pf.c). Judging from kernel implementation of
43 // NETLINK, IPv4 addresses have only the IFA_ADDRESS attribute, while IPv6
44 // have the IFA_LOCAL attribute.
45 unsigned char* address
= NULL
;
46 unsigned char* local
= NULL
;
47 size_t length
= IFA_PAYLOAD(header
);
48 for (const struct rtattr
* attr
=
49 reinterpret_cast<const struct rtattr
*>(IFA_RTA(msg
));
51 attr
= RTA_NEXT(attr
, length
)) {
52 switch (attr
->rta_type
) {
54 DCHECK_GE(RTA_PAYLOAD(attr
), address_length
);
55 address
= reinterpret_cast<unsigned char*>(RTA_DATA(attr
));
58 DCHECK_GE(RTA_PAYLOAD(attr
), address_length
);
59 local
= reinterpret_cast<unsigned char*>(RTA_DATA(attr
));
62 const struct ifa_cacheinfo
*cache_info
=
63 reinterpret_cast<const struct ifa_cacheinfo
*>(RTA_DATA(attr
));
64 if (really_deprecated
)
65 *really_deprecated
= (cache_info
->ifa_prefered
== 0);
75 out
->assign(address
, address
+ address_length
);
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);
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
);
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
)),
107 is_offline_initialized_(false),
108 is_offline_initialized_cv_(&is_offline_lock_
),
112 AddressTrackerLinux::AddressTrackerLinux(const base::Closure
& address_callback
,
113 const base::Closure
& link_callback
,
114 const base::Closure
& tunnel_callback
)
115 : get_interface_name_(GetInterfaceName
),
116 address_callback_(address_callback
),
117 link_callback_(link_callback
),
118 tunnel_callback_(tunnel_callback
),
121 is_offline_initialized_(false),
122 is_offline_initialized_cv_(&is_offline_lock_
),
124 DCHECK(!address_callback
.is_null());
125 DCHECK(!link_callback
.is_null());
128 AddressTrackerLinux::~AddressTrackerLinux() {
132 void AddressTrackerLinux::Init() {
133 netlink_fd_
= socket(AF_NETLINK
, SOCK_RAW
, NETLINK_ROUTE
);
134 if (netlink_fd_
< 0) {
135 PLOG(ERROR
) << "Could not create NETLINK socket";
136 AbortAndForceOnline();
143 // Request notifications.
144 struct sockaddr_nl addr
= {};
145 addr
.nl_family
= AF_NETLINK
;
146 addr
.nl_pid
= getpid();
147 // TODO(szym): Track RTMGRP_LINK as well for ifi_type,
148 // http://crbug.com/113993
150 RTMGRP_IPV4_IFADDR
| RTMGRP_IPV6_IFADDR
| RTMGRP_NOTIFY
| RTMGRP_LINK
;
152 netlink_fd_
, reinterpret_cast<struct sockaddr
*>(&addr
), sizeof(addr
));
154 PLOG(ERROR
) << "Could not bind NETLINK socket";
155 AbortAndForceOnline();
160 // Request dump of addresses.
161 struct sockaddr_nl peer
= {};
162 peer
.nl_family
= AF_NETLINK
;
165 struct nlmsghdr header
;
169 request
.header
.nlmsg_len
= NLMSG_LENGTH(sizeof(request
.msg
));
170 request
.header
.nlmsg_type
= RTM_GETADDR
;
171 request
.header
.nlmsg_flags
= NLM_F_REQUEST
| NLM_F_DUMP
;
172 request
.header
.nlmsg_pid
= getpid();
173 request
.msg
.rtgen_family
= AF_UNSPEC
;
175 rv
= HANDLE_EINTR(sendto(netlink_fd_
, &request
, request
.header
.nlmsg_len
,
176 0, reinterpret_cast<struct sockaddr
*>(&peer
),
179 PLOG(ERROR
) << "Could not send NETLINK request";
180 AbortAndForceOnline();
184 // Consume pending message to populate the AddressMap, but don't notify.
185 // Sending another request without first reading responses results in EBUSY.
186 bool address_changed
;
189 ReadMessages(&address_changed
, &link_changed
, &tunnel_changed
);
191 // Request dump of link state
192 request
.header
.nlmsg_type
= RTM_GETLINK
;
194 rv
= HANDLE_EINTR(sendto(netlink_fd_
, &request
, request
.header
.nlmsg_len
, 0,
195 reinterpret_cast<struct sockaddr
*>(&peer
),
198 PLOG(ERROR
) << "Could not send NETLINK request";
199 AbortAndForceOnline();
203 // Consume pending message to populate links_online_, but don't notify.
204 ReadMessages(&address_changed
, &link_changed
, &tunnel_changed
);
206 AddressTrackerAutoLock
lock(*this, is_offline_lock_
);
207 is_offline_initialized_
= true;
208 is_offline_initialized_cv_
.Signal();
212 rv
= base::MessageLoopForIO::current()->WatchFileDescriptor(
213 netlink_fd_
, true, base::MessageLoopForIO::WATCH_READ
, &watcher_
, this);
215 PLOG(ERROR
) << "Could not watch NETLINK socket";
216 AbortAndForceOnline();
222 void AddressTrackerLinux::AbortAndForceOnline() {
224 AddressTrackerAutoLock
lock(*this, is_offline_lock_
);
226 is_offline_initialized_
= true;
227 is_offline_initialized_cv_
.Signal();
230 AddressTrackerLinux::AddressMap
AddressTrackerLinux::GetAddressMap() const {
231 AddressTrackerAutoLock
lock(*this, address_map_lock_
);
235 base::hash_set
<int> AddressTrackerLinux::GetOnlineLinks() const {
236 AddressTrackerAutoLock
lock(*this, online_links_lock_
);
237 return online_links_
;
240 NetworkChangeNotifier::ConnectionType
241 AddressTrackerLinux::GetCurrentConnectionType() {
242 // http://crbug.com/125097
243 base::ThreadRestrictions::ScopedAllowWait allow_wait
;
244 AddressTrackerAutoLock
lock(*this, is_offline_lock_
);
245 // Make sure the initial offline state is set before returning.
246 while (!is_offline_initialized_
) {
247 is_offline_initialized_cv_
.Wait();
249 // TODO(droger): Return something more detailed than CONNECTION_UNKNOWN.
250 // http://crbug.com/160537
251 return is_offline_
? NetworkChangeNotifier::CONNECTION_NONE
:
252 NetworkChangeNotifier::CONNECTION_UNKNOWN
;
255 void AddressTrackerLinux::ReadMessages(bool* address_changed
,
257 bool* tunnel_changed
) {
258 *address_changed
= false;
259 *link_changed
= false;
260 *tunnel_changed
= false;
262 bool first_loop
= true;
264 int rv
= HANDLE_EINTR(recv(netlink_fd_
,
267 // Block the first time through loop.
268 first_loop
? 0 : MSG_DONTWAIT
));
271 LOG(ERROR
) << "Unexpected shutdown of NETLINK socket.";
275 if ((errno
== EAGAIN
) || (errno
== EWOULDBLOCK
))
277 PLOG(ERROR
) << "Failed to recv from netlink socket";
280 HandleMessage(buffer
, rv
, address_changed
, link_changed
, tunnel_changed
);
285 AddressTrackerAutoLock
lock(*this, online_links_lock_
);
286 is_offline
= online_links_
.empty();
288 AddressTrackerAutoLock
lock(*this, is_offline_lock_
);
289 is_offline_
= is_offline
;
293 void AddressTrackerLinux::HandleMessage(char* buffer
,
295 bool* address_changed
,
297 bool* tunnel_changed
) {
299 for (struct nlmsghdr
* header
= reinterpret_cast<struct nlmsghdr
*>(buffer
);
300 NLMSG_OK(header
, length
);
301 header
= NLMSG_NEXT(header
, length
)) {
302 switch (header
->nlmsg_type
) {
306 const struct nlmsgerr
* msg
=
307 reinterpret_cast<struct nlmsgerr
*>(NLMSG_DATA(header
));
308 LOG(ERROR
) << "Unexpected netlink error " << msg
->error
<< ".";
311 IPAddressNumber address
;
312 bool really_deprecated
;
313 if (GetAddress(header
, &address
, &really_deprecated
)) {
314 AddressTrackerAutoLock
lock(*this, address_map_lock_
);
315 struct ifaddrmsg
* msg
=
316 reinterpret_cast<struct ifaddrmsg
*>(NLMSG_DATA(header
));
317 // Routers may frequently (every few seconds) output the IPv6 ULA
318 // prefix which can cause the linux kernel to frequently output two
319 // back-to-back messages, one without the deprecated flag and one with
320 // the deprecated flag but both with preferred lifetimes of 0. Avoid
321 // interpretting this as an actual change by canonicalizing the two
322 // messages by setting the deprecated flag based on the preferred
323 // lifetime also. http://crbug.com/268042
324 if (really_deprecated
)
325 msg
->ifa_flags
|= IFA_F_DEPRECATED
;
326 // Only indicate change if the address is new or ifaddrmsg info has
328 AddressMap::iterator it
= address_map_
.find(address
);
329 if (it
== address_map_
.end()) {
330 address_map_
.insert(it
, std::make_pair(address
, *msg
));
331 *address_changed
= true;
332 } else if (memcmp(&it
->second
, msg
, sizeof(*msg
))) {
334 *address_changed
= true;
339 IPAddressNumber address
;
340 if (GetAddress(header
, &address
, NULL
)) {
341 AddressTrackerAutoLock
lock(*this, address_map_lock_
);
342 if (address_map_
.erase(address
))
343 *address_changed
= true;
347 const struct ifinfomsg
* msg
=
348 reinterpret_cast<struct ifinfomsg
*>(NLMSG_DATA(header
));
349 if (!(msg
->ifi_flags
& IFF_LOOPBACK
) && (msg
->ifi_flags
& IFF_UP
) &&
350 (msg
->ifi_flags
& IFF_LOWER_UP
) && (msg
->ifi_flags
& IFF_RUNNING
)) {
351 AddressTrackerAutoLock
lock(*this, online_links_lock_
);
352 if (online_links_
.insert(msg
->ifi_index
).second
) {
353 *link_changed
= true;
354 if (IsTunnelInterface(msg
))
355 *tunnel_changed
= true;
358 AddressTrackerAutoLock
lock(*this, online_links_lock_
);
359 if (online_links_
.erase(msg
->ifi_index
)) {
360 *link_changed
= true;
361 if (IsTunnelInterface(msg
))
362 *tunnel_changed
= true;
367 const struct ifinfomsg
* msg
=
368 reinterpret_cast<struct ifinfomsg
*>(NLMSG_DATA(header
));
369 AddressTrackerAutoLock
lock(*this, online_links_lock_
);
370 if (online_links_
.erase(msg
->ifi_index
)) {
371 *link_changed
= true;
372 if (IsTunnelInterface(msg
))
373 *tunnel_changed
= true;
382 void AddressTrackerLinux::OnFileCanReadWithoutBlocking(int fd
) {
383 DCHECK_EQ(netlink_fd_
, fd
);
384 bool address_changed
;
387 ReadMessages(&address_changed
, &link_changed
, &tunnel_changed
);
389 address_callback_
.Run();
391 link_callback_
.Run();
393 tunnel_callback_
.Run();
396 void AddressTrackerLinux::OnFileCanWriteWithoutBlocking(int /* fd */) {}
398 void AddressTrackerLinux::CloseSocket() {
399 if (netlink_fd_
>= 0 && IGNORE_EINTR(close(netlink_fd_
)) < 0)
400 PLOG(ERROR
) << "Could not close NETLINK socket.";
404 bool AddressTrackerLinux::IsTunnelInterface(const struct ifinfomsg
* msg
) const {
405 // Linux kernel drivers/net/tun.c uses "tun" name prefix.
406 return strncmp(get_interface_name_(msg
->ifi_index
), "tun", 3) == 0;
409 AddressTrackerLinux::AddressTrackerAutoLock::AddressTrackerAutoLock(
410 const AddressTrackerLinux
& tracker
,
412 : tracker_(tracker
), lock_(lock
) {
413 if (tracker_
.tracking_
) {
416 DCHECK(tracker_
.thread_checker_
.CalledOnValidThread());
420 AddressTrackerLinux::AddressTrackerAutoLock::~AddressTrackerAutoLock() {
421 if (tracker_
.tracking_
) {
422 lock_
.AssertAcquired();
427 } // namespace internal