Upstream version 5.34.104.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(const base::Closure& address_callback,
101                                          const base::Closure& link_callback,
102                                          const base::Closure& tunnel_callback)
103     : get_interface_name_(GetInterfaceName),
104       address_callback_(address_callback),
105       link_callback_(link_callback),
106       tunnel_callback_(tunnel_callback),
107       netlink_fd_(-1),
108       is_offline_(true),
109       is_offline_initialized_(false),
110       is_offline_initialized_cv_(&is_offline_lock_) {
111   DCHECK(!address_callback.is_null());
112   DCHECK(!link_callback.is_null());
113 }
114
115 AddressTrackerLinux::~AddressTrackerLinux() {
116   CloseSocket();
117 }
118
119 void AddressTrackerLinux::Init() {
120   netlink_fd_ = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
121   if (netlink_fd_ < 0) {
122     PLOG(ERROR) << "Could not create NETLINK socket";
123     AbortAndForceOnline();
124     return;
125   }
126
127   // Request notifications.
128   struct sockaddr_nl addr = {};
129   addr.nl_family = AF_NETLINK;
130   addr.nl_pid = getpid();
131   // TODO(szym): Track RTMGRP_LINK as well for ifi_type, http://crbug.com/113993
132   addr.nl_groups = RTMGRP_IPV4_IFADDR | RTMGRP_IPV6_IFADDR | RTMGRP_NOTIFY |
133       RTMGRP_LINK;
134   int rv = bind(netlink_fd_,
135                 reinterpret_cast<struct sockaddr*>(&addr),
136                 sizeof(addr));
137   if (rv < 0) {
138     PLOG(ERROR) << "Could not bind NETLINK socket";
139     AbortAndForceOnline();
140     return;
141   }
142
143   // Request dump of addresses.
144   struct sockaddr_nl peer = {};
145   peer.nl_family = AF_NETLINK;
146
147   struct {
148     struct nlmsghdr header;
149     struct rtgenmsg msg;
150   } request = {};
151
152   request.header.nlmsg_len = NLMSG_LENGTH(sizeof(request.msg));
153   request.header.nlmsg_type = RTM_GETADDR;
154   request.header.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
155   request.header.nlmsg_pid = getpid();
156   request.msg.rtgen_family = AF_UNSPEC;
157
158   rv = HANDLE_EINTR(sendto(netlink_fd_, &request, request.header.nlmsg_len,
159                            0, reinterpret_cast<struct sockaddr*>(&peer),
160                            sizeof(peer)));
161   if (rv < 0) {
162     PLOG(ERROR) << "Could not send NETLINK request";
163     AbortAndForceOnline();
164     return;
165   }
166
167   // Consume pending message to populate the AddressMap, but don't notify.
168   // Sending another request without first reading responses results in EBUSY.
169   bool address_changed;
170   bool link_changed;
171   bool tunnel_changed;
172   ReadMessages(&address_changed, &link_changed, &tunnel_changed);
173
174   // Request dump of link state
175   request.header.nlmsg_type = RTM_GETLINK;
176
177   rv = HANDLE_EINTR(sendto(netlink_fd_, &request, request.header.nlmsg_len, 0,
178                            reinterpret_cast<struct sockaddr*>(&peer),
179                            sizeof(peer)));
180   if (rv < 0) {
181     PLOG(ERROR) << "Could not send NETLINK request";
182     AbortAndForceOnline();
183     return;
184   }
185
186   // Consume pending message to populate links_online_, but don't notify.
187   ReadMessages(&address_changed, &link_changed, &tunnel_changed);
188   {
189     base::AutoLock lock(is_offline_lock_);
190     is_offline_initialized_ = true;
191     is_offline_initialized_cv_.Signal();
192   }
193
194   rv = base::MessageLoopForIO::current()->WatchFileDescriptor(
195       netlink_fd_, true, base::MessageLoopForIO::WATCH_READ, &watcher_, this);
196   if (rv < 0) {
197     PLOG(ERROR) << "Could not watch NETLINK socket";
198     AbortAndForceOnline();
199     return;
200   }
201 }
202
203 void AddressTrackerLinux::AbortAndForceOnline() {
204   CloseSocket();
205   base::AutoLock lock(is_offline_lock_);
206   is_offline_ = false;
207   is_offline_initialized_ = true;
208   is_offline_initialized_cv_.Signal();
209 }
210
211 AddressTrackerLinux::AddressMap AddressTrackerLinux::GetAddressMap() const {
212   base::AutoLock lock(address_map_lock_);
213   return address_map_;
214 }
215
216 NetworkChangeNotifier::ConnectionType
217 AddressTrackerLinux::GetCurrentConnectionType() {
218   // http://crbug.com/125097
219   base::ThreadRestrictions::ScopedAllowWait allow_wait;
220   base::AutoLock lock(is_offline_lock_);
221   // Make sure the initial offline state is set before returning.
222   while (!is_offline_initialized_) {
223     is_offline_initialized_cv_.Wait();
224   }
225   // TODO(droger): Return something more detailed than CONNECTION_UNKNOWN.
226   // http://crbug.com/160537
227   return is_offline_ ? NetworkChangeNotifier::CONNECTION_NONE :
228                        NetworkChangeNotifier::CONNECTION_UNKNOWN;
229 }
230
231 void AddressTrackerLinux::ReadMessages(bool* address_changed,
232                                        bool* link_changed,
233                                        bool* tunnel_changed) {
234   *address_changed = false;
235   *link_changed = false;
236   *tunnel_changed = false;
237   char buffer[4096];
238   bool first_loop = true;
239   for (;;) {
240     int rv = HANDLE_EINTR(recv(netlink_fd_,
241                                buffer,
242                                sizeof(buffer),
243                                // Block the first time through loop.
244                                first_loop ? 0 : MSG_DONTWAIT));
245     first_loop = false;
246     if (rv == 0) {
247       LOG(ERROR) << "Unexpected shutdown of NETLINK socket.";
248       return;
249     }
250     if (rv < 0) {
251       if ((errno == EAGAIN) || (errno == EWOULDBLOCK))
252         break;
253       PLOG(ERROR) << "Failed to recv from netlink socket";
254       return;
255     }
256     HandleMessage(buffer, rv, address_changed, link_changed, tunnel_changed);
257   };
258   if (*link_changed) {
259     base::AutoLock lock(is_offline_lock_);
260     is_offline_ = online_links_.empty();
261   }
262 }
263
264 void AddressTrackerLinux::HandleMessage(char* buffer,
265                                         size_t length,
266                                         bool* address_changed,
267                                         bool* link_changed,
268                                         bool* tunnel_changed) {
269   DCHECK(buffer);
270   for (struct nlmsghdr* header = reinterpret_cast<struct nlmsghdr*>(buffer);
271        NLMSG_OK(header, length);
272        header = NLMSG_NEXT(header, length)) {
273     switch (header->nlmsg_type) {
274       case NLMSG_DONE:
275         return;
276       case NLMSG_ERROR: {
277         const struct nlmsgerr* msg =
278             reinterpret_cast<struct nlmsgerr*>(NLMSG_DATA(header));
279         LOG(ERROR) << "Unexpected netlink error " << msg->error << ".";
280       } return;
281       case RTM_NEWADDR: {
282         IPAddressNumber address;
283         bool really_deprecated;
284         if (GetAddress(header, &address, &really_deprecated)) {
285           base::AutoLock lock(address_map_lock_);
286           struct ifaddrmsg* msg =
287               reinterpret_cast<struct ifaddrmsg*>(NLMSG_DATA(header));
288           // Routers may frequently (every few seconds) output the IPv6 ULA
289           // prefix which can cause the linux kernel to frequently output two
290           // back-to-back messages, one without the deprecated flag and one with
291           // the deprecated flag but both with preferred lifetimes of 0. Avoid
292           // interpretting this as an actual change by canonicalizing the two
293           // messages by setting the deprecated flag based on the preferred
294           // lifetime also.  http://crbug.com/268042
295           if (really_deprecated)
296             msg->ifa_flags |= IFA_F_DEPRECATED;
297           // Only indicate change if the address is new or ifaddrmsg info has
298           // changed.
299           AddressMap::iterator it = address_map_.find(address);
300           if (it == address_map_.end()) {
301             address_map_.insert(it, std::make_pair(address, *msg));
302             *address_changed = true;
303           } else if (memcmp(&it->second, msg, sizeof(*msg))) {
304             it->second = *msg;
305             *address_changed = true;
306           }
307         }
308       } break;
309       case RTM_DELADDR: {
310         IPAddressNumber address;
311         if (GetAddress(header, &address, NULL)) {
312           base::AutoLock lock(address_map_lock_);
313           if (address_map_.erase(address))
314             *address_changed = true;
315         }
316       } break;
317       case RTM_NEWLINK: {
318         const struct ifinfomsg* msg =
319             reinterpret_cast<struct ifinfomsg*>(NLMSG_DATA(header));
320         if (!(msg->ifi_flags & IFF_LOOPBACK) && (msg->ifi_flags & IFF_UP) &&
321             (msg->ifi_flags & IFF_LOWER_UP) && (msg->ifi_flags & IFF_RUNNING)) {
322           if (online_links_.insert(msg->ifi_index).second) {
323             *link_changed = true;
324             if (IsTunnelInterface(msg))
325               *tunnel_changed = true;
326           }
327         } else {
328           if (online_links_.erase(msg->ifi_index)) {
329             *link_changed = true;
330             if (IsTunnelInterface(msg))
331               *tunnel_changed = true;
332           }
333         }
334       } break;
335       case RTM_DELLINK: {
336         const struct ifinfomsg* msg =
337             reinterpret_cast<struct ifinfomsg*>(NLMSG_DATA(header));
338         if (online_links_.erase(msg->ifi_index)) {
339           *link_changed = true;
340           if (IsTunnelInterface(msg))
341             *tunnel_changed = true;
342         }
343       } break;
344       default:
345         break;
346     }
347   }
348 }
349
350 void AddressTrackerLinux::OnFileCanReadWithoutBlocking(int fd) {
351   DCHECK_EQ(netlink_fd_, fd);
352   bool address_changed;
353   bool link_changed;
354   bool tunnel_changed;
355   ReadMessages(&address_changed, &link_changed, &tunnel_changed);
356   if (address_changed)
357     address_callback_.Run();
358   if (link_changed)
359     link_callback_.Run();
360   if (tunnel_changed)
361     tunnel_callback_.Run();
362 }
363
364 void AddressTrackerLinux::OnFileCanWriteWithoutBlocking(int /* fd */) {}
365
366 void AddressTrackerLinux::CloseSocket() {
367   if (netlink_fd_ >= 0 && IGNORE_EINTR(close(netlink_fd_)) < 0)
368     PLOG(ERROR) << "Could not close NETLINK socket.";
369   netlink_fd_ = -1;
370 }
371
372 bool AddressTrackerLinux::IsTunnelInterface(const struct ifinfomsg* msg) const {
373   // Linux kernel drivers/net/tun.c uses "tun" name prefix.
374   return strncmp(get_interface_name_(msg->ifi_index), "tun", 3) == 0;
375 }
376
377 }  // namespace internal
378 }  // namespace net