• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
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"
14 
15 namespace net {
16 namespace internal {
17 
18 namespace {
19 
20 // Retrieves address from NETLINK address message.
GetAddress(const struct nlmsghdr * header,IPAddressNumber * out)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) {
26     case AF_INET:
27       address_length = kIPv4AddressSize;
28       break;
29     case AF_INET6:
30       address_length = kIPv6AddressSize;
31       break;
32     default:
33       // Unknown family.
34       return false;
35   }
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));
45        RTA_OK(attr, length);
46        attr = RTA_NEXT(attr, length)) {
47     switch (attr->rta_type) {
48       case IFA_ADDRESS:
49         DCHECK_GE(RTA_PAYLOAD(attr), address_length);
50         address = reinterpret_cast<unsigned char*>(RTA_DATA(attr));
51         break;
52       case IFA_LOCAL:
53         DCHECK_GE(RTA_PAYLOAD(attr), address_length);
54         local = reinterpret_cast<unsigned char*>(RTA_DATA(attr));
55         break;
56       default:
57         break;
58     }
59   }
60   if (local)
61     address = local;
62   if (!address)
63     return false;
64   out->assign(address, address + address_length);
65   return true;
66 }
67 
68 }  // namespace
69 
AddressTrackerLinux(const base::Closure & address_callback,const base::Closure & link_callback)70 AddressTrackerLinux::AddressTrackerLinux(const base::Closure& address_callback,
71                                          const base::Closure& link_callback)
72     : address_callback_(address_callback),
73       link_callback_(link_callback),
74       netlink_fd_(-1),
75       is_offline_(true),
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());
80 }
81 
~AddressTrackerLinux()82 AddressTrackerLinux::~AddressTrackerLinux() {
83   CloseSocket();
84 }
85 
Init()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();
91     return;
92   }
93 
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 |
100       RTMGRP_LINK;
101   int rv = bind(netlink_fd_,
102                 reinterpret_cast<struct sockaddr*>(&addr),
103                 sizeof(addr));
104   if (rv < 0) {
105     PLOG(ERROR) << "Could not bind NETLINK socket";
106     AbortAndForceOnline();
107     return;
108   }
109 
110   // Request dump of addresses.
111   struct sockaddr_nl peer = {};
112   peer.nl_family = AF_NETLINK;
113 
114   struct {
115     struct nlmsghdr header;
116     struct rtgenmsg msg;
117   } request = {};
118 
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;
124 
125   rv = HANDLE_EINTR(sendto(netlink_fd_, &request, request.header.nlmsg_len,
126                            0, reinterpret_cast<struct sockaddr*>(&peer),
127                            sizeof(peer)));
128   if (rv < 0) {
129     PLOG(ERROR) << "Could not send NETLINK request";
130     AbortAndForceOnline();
131     return;
132   }
133 
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;
137   bool link_changed;
138   ReadMessages(&address_changed, &link_changed);
139 
140   // Request dump of link state
141   request.header.nlmsg_type = RTM_GETLINK;
142 
143   rv = HANDLE_EINTR(sendto(netlink_fd_, &request, request.header.nlmsg_len, 0,
144                            reinterpret_cast<struct sockaddr*>(&peer),
145                            sizeof(peer)));
146   if (rv < 0) {
147     PLOG(ERROR) << "Could not send NETLINK request";
148     AbortAndForceOnline();
149     return;
150   }
151 
152   // Consume pending message to populate links_online_, but don't notify.
153   ReadMessages(&address_changed, &link_changed);
154   {
155     base::AutoLock lock(is_offline_lock_);
156     is_offline_initialized_ = true;
157     is_offline_initialized_cv_.Signal();
158   }
159 
160   rv = base::MessageLoopForIO::current()->WatchFileDescriptor(
161       netlink_fd_, true, base::MessageLoopForIO::WATCH_READ, &watcher_, this);
162   if (rv < 0) {
163     PLOG(ERROR) << "Could not watch NETLINK socket";
164     AbortAndForceOnline();
165     return;
166   }
167 }
168 
AbortAndForceOnline()169 void AddressTrackerLinux::AbortAndForceOnline() {
170   CloseSocket();
171   base::AutoLock lock(is_offline_lock_);
172   is_offline_ = false;
173   is_offline_initialized_ = true;
174   is_offline_initialized_cv_.Signal();
175 }
176 
GetAddressMap() const177 AddressTrackerLinux::AddressMap AddressTrackerLinux::GetAddressMap() const {
178   base::AutoLock lock(address_map_lock_);
179   return address_map_;
180 }
181 
182 NetworkChangeNotifier::ConnectionType
GetCurrentConnectionType()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();
190   }
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;
195 }
196 
ReadMessages(bool * address_changed,bool * link_changed)197 void AddressTrackerLinux::ReadMessages(bool* address_changed,
198                                        bool* link_changed) {
199   *address_changed = false;
200   *link_changed = false;
201   char buffer[4096];
202   bool first_loop = true;
203   for (;;) {
204     int rv = HANDLE_EINTR(recv(netlink_fd_,
205                                buffer,
206                                sizeof(buffer),
207                                // Block the first time through loop.
208                                first_loop ? 0 : MSG_DONTWAIT));
209     first_loop = false;
210     if (rv == 0) {
211       LOG(ERROR) << "Unexpected shutdown of NETLINK socket.";
212       return;
213     }
214     if (rv < 0) {
215       if ((errno == EAGAIN) || (errno == EWOULDBLOCK))
216         break;
217       PLOG(ERROR) << "Failed to recv from netlink socket";
218       return;
219     }
220     HandleMessage(buffer, rv, address_changed, link_changed);
221   };
222   if (*link_changed) {
223     base::AutoLock lock(is_offline_lock_);
224     is_offline_ = online_links_.empty();
225   }
226 }
227 
HandleMessage(const char * buffer,size_t length,bool * address_changed,bool * link_changed)228 void AddressTrackerLinux::HandleMessage(const char* buffer,
229                                         size_t length,
230                                         bool* address_changed,
231                                         bool* link_changed) {
232   DCHECK(buffer);
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) {
238       case NLMSG_DONE:
239         return;
240       case NLMSG_ERROR: {
241         const struct nlmsgerr* msg =
242             reinterpret_cast<struct nlmsgerr*>(NLMSG_DATA(header));
243         LOG(ERROR) << "Unexpected netlink error " << msg->error << ".";
244       } return;
245       case RTM_NEWADDR: {
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
252           // changed.
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))) {
258             it->second = *msg;
259             *address_changed = true;
260           }
261         }
262       } break;
263       case RTM_DELADDR: {
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;
269         }
270       } break;
271       case RTM_NEWLINK: {
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;
278         } else {
279           if (online_links_.erase(msg->ifi_index))
280             *link_changed = true;
281         }
282       } break;
283       case RTM_DELLINK: {
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;
288       } break;
289       default:
290         break;
291     }
292   }
293 }
294 
OnFileCanReadWithoutBlocking(int fd)295 void AddressTrackerLinux::OnFileCanReadWithoutBlocking(int fd) {
296   DCHECK_EQ(netlink_fd_, fd);
297   bool address_changed;
298   bool link_changed;
299   ReadMessages(&address_changed, &link_changed);
300   if (address_changed)
301     address_callback_.Run();
302   if (link_changed)
303     link_callback_.Run();
304 }
305 
OnFileCanWriteWithoutBlocking(int)306 void AddressTrackerLinux::OnFileCanWriteWithoutBlocking(int /* fd */) {}
307 
CloseSocket()308 void AddressTrackerLinux::CloseSocket() {
309   if (netlink_fd_ >= 0 && IGNORE_EINTR(close(netlink_fd_)) < 0)
310     PLOG(ERROR) << "Could not close NETLINK socket.";
311   netlink_fd_ = -1;
312 }
313 
314 }  // namespace internal
315 }  // namespace net
316