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"
10 #include "base/logging.h"
11 #include "base/posix/eintr_wrapper.h"
12 #include "base/threading/thread_restrictions.h"
13 #include "net/base/network_change_notifier_linux.h"
20 // Retrieves address from NETLINK address message.
21 bool GetAddress(const struct nlmsghdr
* header
, IPAddressNumber
* out
) {
22 const struct ifaddrmsg
* msg
=
23 reinterpret_cast<struct ifaddrmsg
*>(NLMSG_DATA(header
));
24 size_t address_length
= 0;
25 switch (msg
->ifa_family
) {
27 address_length
= kIPv4AddressSize
;
30 address_length
= kIPv6AddressSize
;
36 // Use IFA_ADDRESS unless IFA_LOCAL is present. This behavior here is based on
37 // getaddrinfo in glibc (check_pf.c). Judging from kernel implementation of
38 // NETLINK, IPv4 addresses have only the IFA_ADDRESS attribute, while IPv6
39 // have the IFA_LOCAL attribute.
40 unsigned char* address
= NULL
;
41 unsigned char* local
= NULL
;
42 size_t length
= IFA_PAYLOAD(header
);
43 for (const struct rtattr
* attr
=
44 reinterpret_cast<const struct rtattr
*>(IFA_RTA(msg
));
46 attr
= RTA_NEXT(attr
, length
)) {
47 switch (attr
->rta_type
) {
49 DCHECK_GE(RTA_PAYLOAD(attr
), address_length
);
50 address
= reinterpret_cast<unsigned char*>(RTA_DATA(attr
));
53 DCHECK_GE(RTA_PAYLOAD(attr
), address_length
);
54 local
= reinterpret_cast<unsigned char*>(RTA_DATA(attr
));
64 out
->assign(address
, address
+ address_length
);
70 AddressTrackerLinux::AddressTrackerLinux(const base::Closure
& address_callback
,
71 const base::Closure
& link_callback
)
72 : address_callback_(address_callback
),
73 link_callback_(link_callback
),
76 is_offline_initialized_(false),
77 is_offline_initialized_cv_(&is_offline_lock_
) {
78 DCHECK(!address_callback
.is_null());
79 DCHECK(!link_callback
.is_null());
82 AddressTrackerLinux::~AddressTrackerLinux() {
86 void AddressTrackerLinux::Init() {
87 netlink_fd_
= socket(AF_NETLINK
, SOCK_RAW
, NETLINK_ROUTE
);
88 if (netlink_fd_
< 0) {
89 PLOG(ERROR
) << "Could not create NETLINK socket";
90 AbortAndForceOnline();
94 // Request notifications.
95 struct sockaddr_nl addr
= {};
96 addr
.nl_family
= AF_NETLINK
;
97 addr
.nl_pid
= getpid();
98 // TODO(szym): Track RTMGRP_LINK as well for ifi_type, http://crbug.com/113993
99 addr
.nl_groups
= RTMGRP_IPV4_IFADDR
| RTMGRP_IPV6_IFADDR
| RTMGRP_NOTIFY
|
101 int rv
= bind(netlink_fd_
,
102 reinterpret_cast<struct sockaddr
*>(&addr
),
105 PLOG(ERROR
) << "Could not bind NETLINK socket";
106 AbortAndForceOnline();
110 // Request dump of addresses.
111 struct sockaddr_nl peer
= {};
112 peer
.nl_family
= AF_NETLINK
;
115 struct nlmsghdr header
;
119 request
.header
.nlmsg_len
= NLMSG_LENGTH(sizeof(request
.msg
));
120 request
.header
.nlmsg_type
= RTM_GETADDR
;
121 request
.header
.nlmsg_flags
= NLM_F_REQUEST
| NLM_F_DUMP
;
122 request
.header
.nlmsg_pid
= getpid();
123 request
.msg
.rtgen_family
= AF_UNSPEC
;
125 rv
= HANDLE_EINTR(sendto(netlink_fd_
, &request
, request
.header
.nlmsg_len
,
126 0, reinterpret_cast<struct sockaddr
*>(&peer
),
129 PLOG(ERROR
) << "Could not send NETLINK request";
130 AbortAndForceOnline();
134 // Consume pending message to populate the AddressMap, but don't notify.
135 // Sending another request without first reading responses results in EBUSY.
136 bool address_changed
;
138 ReadMessages(&address_changed
, &link_changed
);
140 // Request dump of link state
141 request
.header
.nlmsg_type
= RTM_GETLINK
;
143 rv
= HANDLE_EINTR(sendto(netlink_fd_
, &request
, request
.header
.nlmsg_len
, 0,
144 reinterpret_cast<struct sockaddr
*>(&peer
),
147 PLOG(ERROR
) << "Could not send NETLINK request";
148 AbortAndForceOnline();
152 // Consume pending message to populate links_online_, but don't notify.
153 ReadMessages(&address_changed
, &link_changed
);
155 base::AutoLock
lock(is_offline_lock_
);
156 is_offline_initialized_
= true;
157 is_offline_initialized_cv_
.Signal();
160 rv
= MessageLoopForIO::current()->WatchFileDescriptor(
161 netlink_fd_
, true, MessageLoopForIO::WATCH_READ
, &watcher_
, this);
163 PLOG(ERROR
) << "Could not watch NETLINK socket";
164 AbortAndForceOnline();
169 void AddressTrackerLinux::AbortAndForceOnline() {
171 base::AutoLock
lock(is_offline_lock_
);
173 is_offline_initialized_
= true;
174 is_offline_initialized_cv_
.Signal();
177 AddressTrackerLinux::AddressMap
AddressTrackerLinux::GetAddressMap() const {
178 base::AutoLock
lock(address_map_lock_
);
182 NetworkChangeNotifier::ConnectionType
183 AddressTrackerLinux::GetCurrentConnectionType() {
184 // http://crbug.com/125097
185 base::ThreadRestrictions::ScopedAllowWait allow_wait
;
186 base::AutoLock
lock(is_offline_lock_
);
187 // Make sure the initial offline state is set before returning.
188 while (!is_offline_initialized_
) {
189 is_offline_initialized_cv_
.Wait();
191 // TODO(droger): Return something more detailed than CONNECTION_UNKNOWN.
192 // http://crbug.com/160537
193 return is_offline_
? NetworkChangeNotifier::CONNECTION_NONE
:
194 NetworkChangeNotifier::CONNECTION_UNKNOWN
;
197 void AddressTrackerLinux::ReadMessages(bool* address_changed
,
198 bool* link_changed
) {
199 *address_changed
= false;
200 *link_changed
= false;
202 bool first_loop
= true;
204 int rv
= HANDLE_EINTR(recv(netlink_fd_
,
207 // Block the first time through loop.
208 first_loop
? 0 : MSG_DONTWAIT
));
211 LOG(ERROR
) << "Unexpected shutdown of NETLINK socket.";
215 if ((errno
== EAGAIN
) || (errno
== EWOULDBLOCK
))
217 PLOG(ERROR
) << "Failed to recv from netlink socket";
220 HandleMessage(buffer
, rv
, address_changed
, link_changed
);
223 base::AutoLock
lock(is_offline_lock_
);
224 is_offline_
= online_links_
.empty();
228 void AddressTrackerLinux::HandleMessage(const char* buffer
,
230 bool* address_changed
,
231 bool* link_changed
) {
233 for (const struct nlmsghdr
* header
=
234 reinterpret_cast<const struct nlmsghdr
*>(buffer
);
235 NLMSG_OK(header
, length
);
236 header
= NLMSG_NEXT(header
, length
)) {
237 switch (header
->nlmsg_type
) {
241 const struct nlmsgerr
* msg
=
242 reinterpret_cast<struct nlmsgerr
*>(NLMSG_DATA(header
));
243 LOG(ERROR
) << "Unexpected netlink error " << msg
->error
<< ".";
246 IPAddressNumber address
;
247 if (GetAddress(header
, &address
)) {
248 base::AutoLock
lock(address_map_lock_
);
249 const struct ifaddrmsg
* msg
=
250 reinterpret_cast<struct ifaddrmsg
*>(NLMSG_DATA(header
));
251 // Only indicate change if the address is new or ifaddrmsg info has
253 AddressMap::iterator it
= address_map_
.find(address
);
254 if (it
== address_map_
.end()) {
255 address_map_
.insert(it
, std::make_pair(address
, *msg
));
256 *address_changed
= true;
257 } else if (memcmp(&it
->second
, msg
, sizeof(*msg
))) {
259 *address_changed
= true;
264 IPAddressNumber address
;
265 if (GetAddress(header
, &address
)) {
266 base::AutoLock
lock(address_map_lock_
);
267 if (address_map_
.erase(address
))
268 *address_changed
= true;
272 const struct ifinfomsg
* msg
=
273 reinterpret_cast<struct ifinfomsg
*>(NLMSG_DATA(header
));
274 if (!(msg
->ifi_flags
& IFF_LOOPBACK
) && (msg
->ifi_flags
& IFF_UP
) &&
275 (msg
->ifi_flags
& IFF_LOWER_UP
) && (msg
->ifi_flags
& IFF_RUNNING
)) {
276 if (online_links_
.insert(msg
->ifi_index
).second
)
277 *link_changed
= true;
279 if (online_links_
.erase(msg
->ifi_index
))
280 *link_changed
= true;
284 const struct ifinfomsg
* msg
=
285 reinterpret_cast<struct ifinfomsg
*>(NLMSG_DATA(header
));
286 if (online_links_
.erase(msg
->ifi_index
))
287 *link_changed
= true;
295 void AddressTrackerLinux::OnFileCanReadWithoutBlocking(int fd
) {
296 DCHECK_EQ(netlink_fd_
, fd
);
297 bool address_changed
;
299 ReadMessages(&address_changed
, &link_changed
);
301 address_callback_
.Run();
303 link_callback_
.Run();
306 void AddressTrackerLinux::OnFileCanWriteWithoutBlocking(int /* fd */) {}
308 void AddressTrackerLinux::CloseSocket() {
309 if (netlink_fd_
>= 0 && HANDLE_EINTR(close(netlink_fd_
)) < 0)
310 PLOG(ERROR
) << "Could not close NETLINK socket.";
314 } // namespace internal