1 /* 2 * Copyright (C) 2015 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package android.net.ip; 18 19 import com.android.internal.annotations.GuardedBy; 20 21 import android.content.Context; 22 import android.net.LinkAddress; 23 import android.net.LinkProperties; 24 import android.net.LinkProperties.ProvisioningChange; 25 import android.net.ProxyInfo; 26 import android.net.RouteInfo; 27 import android.net.metrics.IpReachabilityEvent; 28 import android.net.netlink.NetlinkConstants; 29 import android.net.netlink.NetlinkErrorMessage; 30 import android.net.netlink.NetlinkMessage; 31 import android.net.netlink.NetlinkSocket; 32 import android.net.netlink.RtNetlinkNeighborMessage; 33 import android.net.netlink.StructNdaCacheInfo; 34 import android.net.netlink.StructNdMsg; 35 import android.net.netlink.StructNlMsgHdr; 36 import android.os.PowerManager; 37 import android.os.SystemClock; 38 import android.system.ErrnoException; 39 import android.system.NetlinkSocketAddress; 40 import android.system.OsConstants; 41 import android.util.Log; 42 43 import java.io.InterruptedIOException; 44 import java.net.InetAddress; 45 import java.net.InetSocketAddress; 46 import java.net.NetworkInterface; 47 import java.net.SocketAddress; 48 import java.net.SocketException; 49 import java.nio.ByteBuffer; 50 import java.util.Arrays; 51 import java.util.HashMap; 52 import java.util.HashSet; 53 import java.util.List; 54 import java.util.Map; 55 import java.util.Set; 56 57 58 /** 59 * IpReachabilityMonitor. 60 * 61 * Monitors on-link IP reachability and notifies callers whenever any on-link 62 * addresses of interest appear to have become unresponsive. 63 * 64 * This code does not concern itself with "why" a neighbour might have become 65 * unreachable. Instead, it primarily reacts to the kernel's notion of IP 66 * reachability for each of the neighbours we know to be critically important 67 * to normal network connectivity. As such, it is often "just the messenger": 68 * the neighbours about which it warns are already deemed by the kernel to have 69 * become unreachable. 70 * 71 * 72 * How it works: 73 * 74 * 1. The "on-link neighbours of interest" found in a given LinkProperties 75 * instance are added to a "watch list" via #updateLinkProperties(). 76 * This usually means all default gateways and any on-link DNS servers. 77 * 78 * 2. We listen continuously for netlink neighbour messages (RTM_NEWNEIGH, 79 * RTM_DELNEIGH), watching only for neighbours in the watch list. 80 * 81 * - A neighbour going into NUD_REACHABLE, NUD_STALE, NUD_DELAY, and 82 * even NUD_PROBE is perfectly normal; we merely record the new state. 83 * 84 * - A neighbour's entry may be deleted (RTM_DELNEIGH), for example due 85 * to garbage collection. This is not necessarily of immediate 86 * concern; we record the neighbour as moving to NUD_NONE. 87 * 88 * - A neighbour transitioning to NUD_FAILED (for any reason) is 89 * critically important and is handled as described below in #4. 90 * 91 * 3. All on-link neighbours in the watch list can be forcibly "probed" by 92 * calling #probeAll(). This should be called whenever it is important to 93 * verify that critical neighbours on the link are still reachable, e.g. 94 * when roaming between BSSIDs. 95 * 96 * - The kernel will send unicast ARP requests for IPv4 neighbours and 97 * unicast NS packets for IPv6 neighbours. The expected replies will 98 * likely be unicast. 99 * 100 * - The forced probing is done holding a wakelock. The kernel may, 101 * however, initiate probing of a neighbor on its own, i.e. whenever 102 * a neighbour has expired from NUD_DELAY. 103 * 104 * - The kernel sends: 105 * 106 * /proc/sys/net/ipv{4,6}/neigh/<ifname>/ucast_solicit 107 * 108 * number of probes (usually 3) every: 109 * 110 * /proc/sys/net/ipv{4,6}/neigh/<ifname>/retrans_time_ms 111 * 112 * number of milliseconds (usually 1000ms). This normally results in 113 * 3 unicast packets, 1 per second. 114 * 115 * - If no response is received to any of the probe packets, the kernel 116 * marks the neighbour as being in state NUD_FAILED, and the listening 117 * process in #2 will learn of it. 118 * 119 * 4. We call the supplied Callback#notifyLost() function if the loss of a 120 * neighbour in NUD_FAILED would cause IPv4 or IPv6 configuration to 121 * become incomplete (a loss of provisioning). 122 * 123 * - For example, losing all our IPv4 on-link DNS servers (or losing 124 * our only IPv6 default gateway) constitutes a loss of IPv4 (IPv6) 125 * provisioning; Callback#notifyLost() would be called. 126 * 127 * - Since it can be non-trivial to reacquire certain IP provisioning 128 * state it may be best for the link to disconnect completely and 129 * reconnect afresh. 130 * 131 * 132 * @hide 133 */ 134 public class IpReachabilityMonitor { 135 private static final String TAG = "IpReachabilityMonitor"; 136 private static final boolean DBG = false; 137 private static final boolean VDBG = false; 138 139 public interface Callback { 140 // This callback function must execute as quickly as possible as it is 141 // run on the same thread that listens to kernel neighbor updates. 142 // 143 // TODO: refactor to something like notifyProvisioningLost(String msg). notifyLost(InetAddress ip, String logMsg)144 public void notifyLost(InetAddress ip, String logMsg); 145 } 146 147 private final Object mLock = new Object(); 148 private final PowerManager.WakeLock mWakeLock; 149 private final String mInterfaceName; 150 private final int mInterfaceIndex; 151 private final Callback mCallback; 152 private final NetlinkSocketObserver mNetlinkSocketObserver; 153 private final Thread mObserverThread; 154 @GuardedBy("mLock") 155 private LinkProperties mLinkProperties = new LinkProperties(); 156 // TODO: consider a map to a private NeighborState class holding more 157 // information than a single NUD state entry. 158 @GuardedBy("mLock") 159 private Map<InetAddress, Short> mIpWatchList = new HashMap<>(); 160 @GuardedBy("mLock") 161 private int mIpWatchListVersion; 162 @GuardedBy("mLock") 163 private boolean mRunning; 164 165 /** 166 * Make the kernel perform neighbor reachability detection (IPv4 ARP or IPv6 ND) 167 * for the given IP address on the specified interface index. 168 * 169 * @return 0 if the request was successfully passed to the kernel; otherwise return 170 * a non-zero error code. 171 */ probeNeighbor(int ifIndex, InetAddress ip)172 private static int probeNeighbor(int ifIndex, InetAddress ip) { 173 final String msgSnippet = "probing ip=" + ip.getHostAddress() + "%" + ifIndex; 174 if (DBG) { Log.d(TAG, msgSnippet); } 175 176 final byte[] msg = RtNetlinkNeighborMessage.newNewNeighborMessage( 177 1, ip, StructNdMsg.NUD_PROBE, ifIndex, null); 178 179 int errno = -OsConstants.EPROTO; 180 try (NetlinkSocket nlSocket = new NetlinkSocket(OsConstants.NETLINK_ROUTE)) { 181 final long IO_TIMEOUT = 300L; 182 nlSocket.connectToKernel(); 183 nlSocket.sendMessage(msg, 0, msg.length, IO_TIMEOUT); 184 final ByteBuffer bytes = nlSocket.recvMessage(IO_TIMEOUT); 185 // recvMessage() guaranteed to not return null if it did not throw. 186 final NetlinkMessage response = NetlinkMessage.parse(bytes); 187 if (response != null && response instanceof NetlinkErrorMessage && 188 (((NetlinkErrorMessage) response).getNlMsgError() != null)) { 189 errno = ((NetlinkErrorMessage) response).getNlMsgError().error; 190 if (errno != 0) { 191 // TODO: consider ignoring EINVAL (-22), which appears to be 192 // normal when probing a neighbor for which the kernel does 193 // not already have / no longer has a link layer address. 194 Log.e(TAG, "Error " + msgSnippet + ", errmsg=" + response.toString()); 195 } 196 } else { 197 String errmsg; 198 if (response == null) { 199 bytes.position(0); 200 errmsg = "raw bytes: " + NetlinkConstants.hexify(bytes); 201 } else { 202 errmsg = response.toString(); 203 } 204 Log.e(TAG, "Error " + msgSnippet + ", errmsg=" + errmsg); 205 } 206 } catch (ErrnoException e) { 207 Log.e(TAG, "Error " + msgSnippet, e); 208 errno = -e.errno; 209 } catch (InterruptedIOException e) { 210 Log.e(TAG, "Error " + msgSnippet, e); 211 errno = -OsConstants.ETIMEDOUT; 212 } catch (SocketException e) { 213 Log.e(TAG, "Error " + msgSnippet, e); 214 errno = -OsConstants.EIO; 215 } 216 return errno; 217 } 218 IpReachabilityMonitor(Context context, String ifName, Callback callback)219 public IpReachabilityMonitor(Context context, String ifName, Callback callback) 220 throws IllegalArgumentException { 221 mInterfaceName = ifName; 222 int ifIndex = -1; 223 try { 224 NetworkInterface netIf = NetworkInterface.getByName(ifName); 225 mInterfaceIndex = netIf.getIndex(); 226 } catch (SocketException | NullPointerException e) { 227 throw new IllegalArgumentException("invalid interface '" + ifName + "': ", e); 228 } 229 mWakeLock = ((PowerManager) context.getSystemService(Context.POWER_SERVICE)).newWakeLock( 230 PowerManager.PARTIAL_WAKE_LOCK, TAG + "." + mInterfaceName); 231 mCallback = callback; 232 mNetlinkSocketObserver = new NetlinkSocketObserver(); 233 mObserverThread = new Thread(mNetlinkSocketObserver); 234 mObserverThread.start(); 235 } 236 stop()237 public void stop() { 238 synchronized (mLock) { mRunning = false; } 239 clearLinkProperties(); 240 mNetlinkSocketObserver.clearNetlinkSocket(); 241 } 242 243 // TODO: add a public dump() method that can be called during a bug report. 244 describeWatchList()245 private String describeWatchList() { 246 final String delimiter = ", "; 247 StringBuilder sb = new StringBuilder(); 248 synchronized (mLock) { 249 sb.append("iface{" + mInterfaceName + "/" + mInterfaceIndex + "}, "); 250 sb.append("v{" + mIpWatchListVersion + "}, "); 251 sb.append("ntable=["); 252 boolean firstTime = true; 253 for (Map.Entry<InetAddress, Short> entry : mIpWatchList.entrySet()) { 254 if (firstTime) { 255 firstTime = false; 256 } else { 257 sb.append(delimiter); 258 } 259 sb.append(entry.getKey().getHostAddress() + "/" + 260 StructNdMsg.stringForNudState(entry.getValue())); 261 } 262 sb.append("]"); 263 } 264 return sb.toString(); 265 } 266 isWatching(InetAddress ip)267 private boolean isWatching(InetAddress ip) { 268 synchronized (mLock) { 269 return mRunning && mIpWatchList.containsKey(ip); 270 } 271 } 272 stillRunning()273 private boolean stillRunning() { 274 synchronized (mLock) { 275 return mRunning; 276 } 277 } 278 isOnLink(List<RouteInfo> routes, InetAddress ip)279 private static boolean isOnLink(List<RouteInfo> routes, InetAddress ip) { 280 for (RouteInfo route : routes) { 281 if (!route.hasGateway() && route.matches(ip)) { 282 return true; 283 } 284 } 285 return false; 286 } 287 getNeighborStateLocked(InetAddress ip)288 private short getNeighborStateLocked(InetAddress ip) { 289 if (mIpWatchList.containsKey(ip)) { 290 return mIpWatchList.get(ip); 291 } 292 return StructNdMsg.NUD_NONE; 293 } 294 updateLinkProperties(LinkProperties lp)295 public void updateLinkProperties(LinkProperties lp) { 296 if (!mInterfaceName.equals(lp.getInterfaceName())) { 297 // TODO: figure out whether / how to cope with interface changes. 298 Log.wtf(TAG, "requested LinkProperties interface '" + lp.getInterfaceName() + 299 "' does not match: " + mInterfaceName); 300 return; 301 } 302 303 synchronized (mLock) { 304 mLinkProperties = new LinkProperties(lp); 305 Map<InetAddress, Short> newIpWatchList = new HashMap<>(); 306 307 final List<RouteInfo> routes = mLinkProperties.getRoutes(); 308 for (RouteInfo route : routes) { 309 if (route.hasGateway()) { 310 InetAddress gw = route.getGateway(); 311 if (isOnLink(routes, gw)) { 312 newIpWatchList.put(gw, getNeighborStateLocked(gw)); 313 } 314 } 315 } 316 317 for (InetAddress nameserver : lp.getDnsServers()) { 318 if (isOnLink(routes, nameserver)) { 319 newIpWatchList.put(nameserver, getNeighborStateLocked(nameserver)); 320 } 321 } 322 323 mIpWatchList = newIpWatchList; 324 mIpWatchListVersion++; 325 } 326 if (DBG) { Log.d(TAG, "watch: " + describeWatchList()); } 327 } 328 clearLinkProperties()329 public void clearLinkProperties() { 330 synchronized (mLock) { 331 mLinkProperties.clear(); 332 mIpWatchList.clear(); 333 mIpWatchListVersion++; 334 } 335 if (DBG) { Log.d(TAG, "clear: " + describeWatchList()); } 336 } 337 handleNeighborLost(String msg)338 private void handleNeighborLost(String msg) { 339 InetAddress ip = null; 340 ProvisioningChange delta; 341 synchronized (mLock) { 342 LinkProperties whatIfLp = new LinkProperties(mLinkProperties); 343 344 for (Map.Entry<InetAddress, Short> entry : mIpWatchList.entrySet()) { 345 if (entry.getValue() != StructNdMsg.NUD_FAILED) { 346 continue; 347 } 348 349 ip = entry.getKey(); 350 for (RouteInfo route : mLinkProperties.getRoutes()) { 351 if (ip.equals(route.getGateway())) { 352 whatIfLp.removeRoute(route); 353 } 354 } 355 whatIfLp.removeDnsServer(ip); 356 } 357 358 delta = LinkProperties.compareProvisioning(mLinkProperties, whatIfLp); 359 } 360 361 if (delta == ProvisioningChange.LOST_PROVISIONING) { 362 IpReachabilityEvent.logProvisioningLost(mInterfaceName); 363 final String logMsg = "FAILURE: LOST_PROVISIONING, " + msg; 364 Log.w(TAG, logMsg); 365 if (mCallback != null) { 366 // TODO: remove |ip| when the callback signature no longer has 367 // an InetAddress argument. 368 mCallback.notifyLost(ip, logMsg); 369 } 370 } else { 371 IpReachabilityEvent.logNudFailed(mInterfaceName); 372 } 373 } 374 probeAll()375 public void probeAll() { 376 Set<InetAddress> ipProbeList = new HashSet<InetAddress>(); 377 synchronized (mLock) { 378 ipProbeList.addAll(mIpWatchList.keySet()); 379 } 380 381 if (!ipProbeList.isEmpty() && stillRunning()) { 382 // Keep the CPU awake long enough to allow all ARP/ND 383 // probes a reasonable chance at success. See b/23197666. 384 // 385 // The wakelock we use is (by default) refcounted, and this version 386 // of acquire(timeout) queues a release message to keep acquisitions 387 // and releases balanced. 388 mWakeLock.acquire(getProbeWakeLockDuration()); 389 } 390 391 for (InetAddress target : ipProbeList) { 392 if (!stillRunning()) { 393 break; 394 } 395 final int returnValue = probeNeighbor(mInterfaceIndex, target); 396 IpReachabilityEvent.logProbeEvent(mInterfaceName, returnValue); 397 } 398 } 399 getProbeWakeLockDuration()400 private long getProbeWakeLockDuration() { 401 // Ideally, this would be computed by examining the values of: 402 // 403 // /proc/sys/net/ipv[46]/neigh/<ifname>/ucast_solicit 404 // 405 // and: 406 // 407 // /proc/sys/net/ipv[46]/neigh/<ifname>/retrans_time_ms 408 // 409 // For now, just make some assumptions. 410 final long numUnicastProbes = 3; 411 final long retransTimeMs = 1000; 412 final long gracePeriodMs = 500; 413 return (numUnicastProbes * retransTimeMs) + gracePeriodMs; 414 } 415 416 // TODO: simplify the number of objects by making this extend Thread. 417 private final class NetlinkSocketObserver implements Runnable { 418 private NetlinkSocket mSocket; 419 420 @Override run()421 public void run() { 422 if (VDBG) { Log.d(TAG, "Starting observing thread."); } 423 synchronized (mLock) { mRunning = true; } 424 425 try { 426 setupNetlinkSocket(); 427 } catch (ErrnoException | SocketException e) { 428 Log.e(TAG, "Failed to suitably initialize a netlink socket", e); 429 synchronized (mLock) { mRunning = false; } 430 } 431 432 ByteBuffer byteBuffer; 433 while (stillRunning()) { 434 try { 435 byteBuffer = recvKernelReply(); 436 } catch (ErrnoException e) { 437 if (stillRunning()) { Log.w(TAG, "ErrnoException: ", e); } 438 break; 439 } 440 final long whenMs = SystemClock.elapsedRealtime(); 441 if (byteBuffer == null) { 442 continue; 443 } 444 parseNetlinkMessageBuffer(byteBuffer, whenMs); 445 } 446 447 clearNetlinkSocket(); 448 449 synchronized (mLock) { mRunning = false; } 450 if (VDBG) { Log.d(TAG, "Finishing observing thread."); } 451 } 452 clearNetlinkSocket()453 private void clearNetlinkSocket() { 454 if (mSocket != null) { 455 mSocket.close(); 456 } 457 } 458 459 // TODO: Refactor the main loop to recreate the socket upon recoverable errors. setupNetlinkSocket()460 private void setupNetlinkSocket() throws ErrnoException, SocketException { 461 clearNetlinkSocket(); 462 mSocket = new NetlinkSocket(OsConstants.NETLINK_ROUTE); 463 464 final NetlinkSocketAddress listenAddr = new NetlinkSocketAddress( 465 0, OsConstants.RTMGRP_NEIGH); 466 mSocket.bind(listenAddr); 467 468 if (VDBG) { 469 final NetlinkSocketAddress nlAddr = mSocket.getLocalAddress(); 470 Log.d(TAG, "bound to sockaddr_nl{" 471 + ((long) (nlAddr.getPortId() & 0xffffffff)) + ", " 472 + nlAddr.getGroupsMask() 473 + "}"); 474 } 475 } 476 recvKernelReply()477 private ByteBuffer recvKernelReply() throws ErrnoException { 478 try { 479 return mSocket.recvMessage(0); 480 } catch (InterruptedIOException e) { 481 // Interruption or other error, e.g. another thread closed our file descriptor. 482 } catch (ErrnoException e) { 483 if (e.errno != OsConstants.EAGAIN) { 484 throw e; 485 } 486 } 487 return null; 488 } 489 parseNetlinkMessageBuffer(ByteBuffer byteBuffer, long whenMs)490 private void parseNetlinkMessageBuffer(ByteBuffer byteBuffer, long whenMs) { 491 while (byteBuffer.remaining() > 0) { 492 final int position = byteBuffer.position(); 493 final NetlinkMessage nlMsg = NetlinkMessage.parse(byteBuffer); 494 if (nlMsg == null || nlMsg.getHeader() == null) { 495 byteBuffer.position(position); 496 Log.e(TAG, "unparsable netlink msg: " + NetlinkConstants.hexify(byteBuffer)); 497 break; 498 } 499 500 final int srcPortId = nlMsg.getHeader().nlmsg_pid; 501 if (srcPortId != 0) { 502 Log.e(TAG, "non-kernel source portId: " + ((long) (srcPortId & 0xffffffff))); 503 break; 504 } 505 506 if (nlMsg instanceof NetlinkErrorMessage) { 507 Log.e(TAG, "netlink error: " + nlMsg); 508 continue; 509 } else if (!(nlMsg instanceof RtNetlinkNeighborMessage)) { 510 if (DBG) { 511 Log.d(TAG, "non-rtnetlink neighbor msg: " + nlMsg); 512 } 513 continue; 514 } 515 516 evaluateRtNetlinkNeighborMessage((RtNetlinkNeighborMessage) nlMsg, whenMs); 517 } 518 } 519 evaluateRtNetlinkNeighborMessage( RtNetlinkNeighborMessage neighMsg, long whenMs)520 private void evaluateRtNetlinkNeighborMessage( 521 RtNetlinkNeighborMessage neighMsg, long whenMs) { 522 final StructNdMsg ndMsg = neighMsg.getNdHeader(); 523 if (ndMsg == null || ndMsg.ndm_ifindex != mInterfaceIndex) { 524 return; 525 } 526 527 final InetAddress destination = neighMsg.getDestination(); 528 if (!isWatching(destination)) { 529 return; 530 } 531 532 final short msgType = neighMsg.getHeader().nlmsg_type; 533 final short nudState = ndMsg.ndm_state; 534 final String eventMsg = "NeighborEvent{" 535 + "elapsedMs=" + whenMs + ", " 536 + destination.getHostAddress() + ", " 537 + "[" + NetlinkConstants.hexify(neighMsg.getLinkLayerAddress()) + "], " 538 + NetlinkConstants.stringForNlMsgType(msgType) + ", " 539 + StructNdMsg.stringForNudState(nudState) 540 + "}"; 541 542 if (VDBG) { 543 Log.d(TAG, neighMsg.toString()); 544 } else if (DBG) { 545 Log.d(TAG, eventMsg); 546 } 547 548 synchronized (mLock) { 549 if (mIpWatchList.containsKey(destination)) { 550 final short value = 551 (msgType == NetlinkConstants.RTM_DELNEIGH) 552 ? StructNdMsg.NUD_NONE 553 : nudState; 554 mIpWatchList.put(destination, value); 555 } 556 } 557 558 if (nudState == StructNdMsg.NUD_FAILED) { 559 Log.w(TAG, "ALERT: " + eventMsg); 560 handleNeighborLost(eventMsg); 561 } 562 } 563 } 564 } 565