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. 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 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 82 AddressTrackerLinux::~AddressTrackerLinux() { 83 CloseSocket(); 84 } 85 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 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 177 AddressTrackerLinux::AddressMap AddressTrackerLinux::GetAddressMap() const { 178 base::AutoLock lock(address_map_lock_); 179 return address_map_; 180 } 181 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(); 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 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 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 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 306 void AddressTrackerLinux::OnFileCanWriteWithoutBlocking(int /* fd */) {} 307 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