Upstream version 10.39.225.0
[platform/framework/web/crosswalk.git] / src / net / base / address_tracker_linux.cc
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 #include <linux/if.h>
9 #include <sys/ioctl.h>
10
11 #include "base/logging.h"
12 #include "base/posix/eintr_wrapper.h"
13 #include "base/threading/thread_restrictions.h"
14
15 namespace net {
16 namespace internal {
17
18 namespace {
19
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,
23                 IPAddressNumber* out,
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) {
31     case AF_INET:
32       address_length = kIPv4AddressSize;
33       break;
34     case AF_INET6:
35       address_length = kIPv6AddressSize;
36       break;
37     default:
38       // Unknown family.
39       return false;
40   }
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));
50        RTA_OK(attr, length);
51        attr = RTA_NEXT(attr, length)) {
52     switch (attr->rta_type) {
53       case IFA_ADDRESS:
54         DCHECK_GE(RTA_PAYLOAD(attr), address_length);
55         address = reinterpret_cast<unsigned char*>(RTA_DATA(attr));
56         break;
57       case IFA_LOCAL:
58         DCHECK_GE(RTA_PAYLOAD(attr), address_length);
59         local = reinterpret_cast<unsigned char*>(RTA_DATA(attr));
60         break;
61       case IFA_CACHEINFO: {
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);
66       } break;
67       default:
68         break;
69     }
70   }
71   if (local)
72     address = local;
73   if (!address)
74     return false;
75   out->assign(address, address + address_length);
76   return true;
77 }
78
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);
86   if (ioctl_socket < 0)
87     return "";
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);
92   close(ioctl_socket);
93   if (rv != 0)
94     return "";
95   return ifr.ifr_name;
96 }
97
98 }  // namespace
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
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),
119       netlink_fd_(-1),
120       is_offline_(true),
121       is_offline_initialized_(false),
122       is_offline_initialized_cv_(&is_offline_lock_),
123       tracking_(true) {
124   DCHECK(!address_callback.is_null());
125   DCHECK(!link_callback.is_null());
126 }
127
128 AddressTrackerLinux::~AddressTrackerLinux() {
129   CloseSocket();
130 }
131
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();
137     return;
138   }
139
140   int rv;
141
142   if (tracking_) {
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
149     addr.nl_groups =
150         RTMGRP_IPV4_IFADDR | RTMGRP_IPV6_IFADDR | RTMGRP_NOTIFY | RTMGRP_LINK;
151     rv = bind(
152         netlink_fd_, reinterpret_cast<struct sockaddr*>(&addr), sizeof(addr));
153     if (rv < 0) {
154       PLOG(ERROR) << "Could not bind NETLINK socket";
155       AbortAndForceOnline();
156       return;
157     }
158   }
159
160   // Request dump of addresses.
161   struct sockaddr_nl peer = {};
162   peer.nl_family = AF_NETLINK;
163
164   struct {
165     struct nlmsghdr header;
166     struct rtgenmsg msg;
167   } request = {};
168
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;
174
175   rv = HANDLE_EINTR(sendto(netlink_fd_, &request, request.header.nlmsg_len,
176                            0, reinterpret_cast<struct sockaddr*>(&peer),
177                            sizeof(peer)));
178   if (rv < 0) {
179     PLOG(ERROR) << "Could not send NETLINK request";
180     AbortAndForceOnline();
181     return;
182   }
183
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;
187   bool link_changed;
188   bool tunnel_changed;
189   ReadMessages(&address_changed, &link_changed, &tunnel_changed);
190
191   // Request dump of link state
192   request.header.nlmsg_type = RTM_GETLINK;
193
194   rv = HANDLE_EINTR(sendto(netlink_fd_, &request, request.header.nlmsg_len, 0,
195                            reinterpret_cast<struct sockaddr*>(&peer),
196                            sizeof(peer)));
197   if (rv < 0) {
198     PLOG(ERROR) << "Could not send NETLINK request";
199     AbortAndForceOnline();
200     return;
201   }
202
203   // Consume pending message to populate links_online_, but don't notify.
204   ReadMessages(&address_changed, &link_changed, &tunnel_changed);
205   {
206     AddressTrackerAutoLock lock(*this, is_offline_lock_);
207     is_offline_initialized_ = true;
208     is_offline_initialized_cv_.Signal();
209   }
210
211   if (tracking_) {
212     rv = base::MessageLoopForIO::current()->WatchFileDescriptor(
213         netlink_fd_, true, base::MessageLoopForIO::WATCH_READ, &watcher_, this);
214     if (rv < 0) {
215       PLOG(ERROR) << "Could not watch NETLINK socket";
216       AbortAndForceOnline();
217       return;
218     }
219   }
220 }
221
222 void AddressTrackerLinux::AbortAndForceOnline() {
223   CloseSocket();
224   AddressTrackerAutoLock lock(*this, is_offline_lock_);
225   is_offline_ = false;
226   is_offline_initialized_ = true;
227   is_offline_initialized_cv_.Signal();
228 }
229
230 AddressTrackerLinux::AddressMap AddressTrackerLinux::GetAddressMap() const {
231   AddressTrackerAutoLock lock(*this, address_map_lock_);
232   return address_map_;
233 }
234
235 base::hash_set<int> AddressTrackerLinux::GetOnlineLinks() const {
236   AddressTrackerAutoLock lock(*this, online_links_lock_);
237   return online_links_;
238 }
239
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();
248   }
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;
253 }
254
255 void AddressTrackerLinux::ReadMessages(bool* address_changed,
256                                        bool* link_changed,
257                                        bool* tunnel_changed) {
258   *address_changed = false;
259   *link_changed = false;
260   *tunnel_changed = false;
261   char buffer[4096];
262   bool first_loop = true;
263   for (;;) {
264     int rv = HANDLE_EINTR(recv(netlink_fd_,
265                                buffer,
266                                sizeof(buffer),
267                                // Block the first time through loop.
268                                first_loop ? 0 : MSG_DONTWAIT));
269     first_loop = false;
270     if (rv == 0) {
271       LOG(ERROR) << "Unexpected shutdown of NETLINK socket.";
272       return;
273     }
274     if (rv < 0) {
275       if ((errno == EAGAIN) || (errno == EWOULDBLOCK))
276         break;
277       PLOG(ERROR) << "Failed to recv from netlink socket";
278       return;
279     }
280     HandleMessage(buffer, rv, address_changed, link_changed, tunnel_changed);
281   }
282   if (*link_changed) {
283     bool is_offline;
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;
290   }
291 }
292
293 void AddressTrackerLinux::HandleMessage(char* buffer,
294                                         size_t length,
295                                         bool* address_changed,
296                                         bool* link_changed,
297                                         bool* tunnel_changed) {
298   DCHECK(buffer);
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) {
303       case NLMSG_DONE:
304         return;
305       case NLMSG_ERROR: {
306         const struct nlmsgerr* msg =
307             reinterpret_cast<struct nlmsgerr*>(NLMSG_DATA(header));
308         LOG(ERROR) << "Unexpected netlink error " << msg->error << ".";
309       } return;
310       case RTM_NEWADDR: {
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
327           // changed.
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))) {
333             it->second = *msg;
334             *address_changed = true;
335           }
336         }
337       } break;
338       case RTM_DELADDR: {
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;
344         }
345       } break;
346       case RTM_NEWLINK: {
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;
356           }
357         } else {
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;
363           }
364         }
365       } break;
366       case RTM_DELLINK: {
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;
374         }
375       } break;
376       default:
377         break;
378     }
379   }
380 }
381
382 void AddressTrackerLinux::OnFileCanReadWithoutBlocking(int fd) {
383   DCHECK_EQ(netlink_fd_, fd);
384   bool address_changed;
385   bool link_changed;
386   bool tunnel_changed;
387   ReadMessages(&address_changed, &link_changed, &tunnel_changed);
388   if (address_changed)
389     address_callback_.Run();
390   if (link_changed)
391     link_callback_.Run();
392   if (tunnel_changed)
393     tunnel_callback_.Run();
394 }
395
396 void AddressTrackerLinux::OnFileCanWriteWithoutBlocking(int /* fd */) {}
397
398 void AddressTrackerLinux::CloseSocket() {
399   if (netlink_fd_ >= 0 && IGNORE_EINTR(close(netlink_fd_)) < 0)
400     PLOG(ERROR) << "Could not close NETLINK socket.";
401   netlink_fd_ = -1;
402 }
403
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;
407 }
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
427 }  // namespace internal
428 }  // namespace net