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 android.content.Context; 20 import android.net.LinkAddress; 21 import android.net.LinkProperties; 22 import android.net.LinkProperties.ProvisioningChange; 23 import android.net.ProxyInfo; 24 import android.net.RouteInfo; 25 import android.net.ip.IpNeighborMonitor.NeighborEvent; 26 import android.net.metrics.IpConnectivityLog; 27 import android.net.metrics.IpReachabilityEvent; 28 import android.net.netlink.StructNdMsg; 29 import android.net.util.InterfaceParams; 30 import android.net.util.MultinetworkPolicyTracker; 31 import android.net.util.SharedLog; 32 import android.os.Handler; 33 import android.os.PowerManager; 34 import android.os.PowerManager.WakeLock; 35 import android.os.SystemClock; 36 import android.system.ErrnoException; 37 import android.system.OsConstants; 38 import android.util.Log; 39 40 import com.android.internal.annotations.GuardedBy; 41 import com.android.internal.annotations.VisibleForTesting; 42 import com.android.internal.util.DumpUtils; 43 import com.android.internal.util.DumpUtils.Dump; 44 45 import java.io.InterruptedIOException; 46 import java.io.PrintWriter; 47 import java.net.Inet6Address; 48 import java.net.InetAddress; 49 import java.net.InetSocketAddress; 50 import java.net.SocketAddress; 51 import java.nio.ByteBuffer; 52 import java.util.ArrayList; 53 import java.util.Arrays; 54 import java.util.HashMap; 55 import java.util.List; 56 import java.util.Map; 57 import java.util.Set; 58 59 60 /** 61 * IpReachabilityMonitor. 62 * 63 * Monitors on-link IP reachability and notifies callers whenever any on-link 64 * addresses of interest appear to have become unresponsive. 65 * 66 * This code does not concern itself with "why" a neighbour might have become 67 * unreachable. Instead, it primarily reacts to the kernel's notion of IP 68 * reachability for each of the neighbours we know to be critically important 69 * to normal network connectivity. As such, it is often "just the messenger": 70 * the neighbours about which it warns are already deemed by the kernel to have 71 * become unreachable. 72 * 73 * 74 * How it works: 75 * 76 * 1. The "on-link neighbours of interest" found in a given LinkProperties 77 * instance are added to a "watch list" via #updateLinkProperties(). 78 * This usually means all default gateways and any on-link DNS servers. 79 * 80 * 2. We listen continuously for netlink neighbour messages (RTM_NEWNEIGH, 81 * RTM_DELNEIGH), watching only for neighbours in the watch list. 82 * 83 * - A neighbour going into NUD_REACHABLE, NUD_STALE, NUD_DELAY, and 84 * even NUD_PROBE is perfectly normal; we merely record the new state. 85 * 86 * - A neighbour's entry may be deleted (RTM_DELNEIGH), for example due 87 * to garbage collection. This is not necessarily of immediate 88 * concern; we record the neighbour as moving to NUD_NONE. 89 * 90 * - A neighbour transitioning to NUD_FAILED (for any reason) is 91 * critically important and is handled as described below in #4. 92 * 93 * 3. All on-link neighbours in the watch list can be forcibly "probed" by 94 * calling #probeAll(). This should be called whenever it is important to 95 * verify that critical neighbours on the link are still reachable, e.g. 96 * when roaming between BSSIDs. 97 * 98 * - The kernel will send unicast ARP requests for IPv4 neighbours and 99 * unicast NS packets for IPv6 neighbours. The expected replies will 100 * likely be unicast. 101 * 102 * - The forced probing is done holding a wakelock. The kernel may, 103 * however, initiate probing of a neighbor on its own, i.e. whenever 104 * a neighbour has expired from NUD_DELAY. 105 * 106 * - The kernel sends: 107 * 108 * /proc/sys/net/ipv{4,6}/neigh/<ifname>/ucast_solicit 109 * 110 * number of probes (usually 3) every: 111 * 112 * /proc/sys/net/ipv{4,6}/neigh/<ifname>/retrans_time_ms 113 * 114 * number of milliseconds (usually 1000ms). This normally results in 115 * 3 unicast packets, 1 per second. 116 * 117 * - If no response is received to any of the probe packets, the kernel 118 * marks the neighbour as being in state NUD_FAILED, and the listening 119 * process in #2 will learn of it. 120 * 121 * 4. We call the supplied Callback#notifyLost() function if the loss of a 122 * neighbour in NUD_FAILED would cause IPv4 or IPv6 configuration to 123 * become incomplete (a loss of provisioning). 124 * 125 * - For example, losing all our IPv4 on-link DNS servers (or losing 126 * our only IPv6 default gateway) constitutes a loss of IPv4 (IPv6) 127 * provisioning; Callback#notifyLost() would be called. 128 * 129 * - Since it can be non-trivial to reacquire certain IP provisioning 130 * state it may be best for the link to disconnect completely and 131 * reconnect afresh. 132 * 133 * Accessing an instance of this class from multiple threads is NOT safe. 134 * 135 * @hide 136 */ 137 public class IpReachabilityMonitor { 138 private static final String TAG = "IpReachabilityMonitor"; 139 private static final boolean DBG = false; 140 private static final boolean VDBG = false; 141 142 public interface Callback { 143 // This callback function must execute as quickly as possible as it is 144 // run on the same thread that listens to kernel neighbor updates. 145 // 146 // TODO: refactor to something like notifyProvisioningLost(String msg). 147 public void notifyLost(InetAddress ip, String logMsg); 148 } 149 150 /** 151 * Encapsulates IpReachabilityMonitor depencencies on systems that hinder unit testing. 152 * TODO: consider also wrapping MultinetworkPolicyTracker in this interface. 153 */ 154 interface Dependencies { 155 void acquireWakeLock(long durationMs); 156 157 static Dependencies makeDefault(Context context, String iface) { 158 final String lockName = TAG + "." + iface; 159 final PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE); 160 final WakeLock lock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, lockName); 161 162 return new Dependencies() { 163 public void acquireWakeLock(long durationMs) { 164 lock.acquire(durationMs); 165 } 166 }; 167 } 168 } 169 170 private final InterfaceParams mInterfaceParams; 171 private final IpNeighborMonitor mIpNeighborMonitor; 172 private final SharedLog mLog; 173 private final Callback mCallback; 174 private final Dependencies mDependencies; 175 private final MultinetworkPolicyTracker mMultinetworkPolicyTracker; 176 private final IpConnectivityLog mMetricsLog = new IpConnectivityLog(); 177 private LinkProperties mLinkProperties = new LinkProperties(); 178 private Map<InetAddress, NeighborEvent> mNeighborWatchList = new HashMap<>(); 179 // Time in milliseconds of the last forced probe request. 180 private volatile long mLastProbeTimeMs; 181 182 public IpReachabilityMonitor( 183 Context context, InterfaceParams ifParams, Handler h, SharedLog log, Callback callback, 184 MultinetworkPolicyTracker tracker) { 185 this(ifParams, h, log, callback, tracker, Dependencies.makeDefault(context, ifParams.name)); 186 } 187 188 @VisibleForTesting 189 IpReachabilityMonitor(InterfaceParams ifParams, Handler h, SharedLog log, Callback callback, 190 MultinetworkPolicyTracker tracker, Dependencies dependencies) { 191 if (ifParams == null) throw new IllegalArgumentException("null InterfaceParams"); 192 193 mInterfaceParams = ifParams; 194 mLog = log.forSubComponent(TAG); 195 mCallback = callback; 196 mMultinetworkPolicyTracker = tracker; 197 mDependencies = dependencies; 198 199 mIpNeighborMonitor = new IpNeighborMonitor(h, mLog, 200 (NeighborEvent event) -> { 201 if (mInterfaceParams.index != event.ifindex) return; 202 if (!mNeighborWatchList.containsKey(event.ip)) return; 203 204 final NeighborEvent prev = mNeighborWatchList.put(event.ip, event); 205 206 // TODO: Consider what to do with other states that are not within 207 // NeighborEvent#isValid() (i.e. NUD_NONE, NUD_INCOMPLETE). 208 if (event.nudState == StructNdMsg.NUD_FAILED) { 209 mLog.w("ALERT neighbor went from: " + prev + " to: " + event); 210 handleNeighborLost(event); 211 } 212 }); 213 mIpNeighborMonitor.start(); 214 } 215 216 public void stop() { 217 mIpNeighborMonitor.stop(); 218 clearLinkProperties(); 219 } 220 221 public void dump(PrintWriter pw) { 222 DumpUtils.dumpAsync( 223 mIpNeighborMonitor.getHandler(), 224 new Dump() { 225 @Override 226 public void dump(PrintWriter pw, String prefix) { 227 pw.println(describeWatchList("\n")); 228 } 229 }, 230 pw, "", 1000); 231 } 232 233 private String describeWatchList() { return describeWatchList(" "); } 234 235 private String describeWatchList(String sep) { 236 final StringBuilder sb = new StringBuilder(); 237 sb.append("iface{" + mInterfaceParams + "}," + sep); 238 sb.append("ntable=[" + sep); 239 String delimiter = ""; 240 for (Map.Entry<InetAddress, NeighborEvent> entry : mNeighborWatchList.entrySet()) { 241 sb.append(delimiter).append(entry.getKey().getHostAddress() + "/" + entry.getValue()); 242 delimiter = "," + sep; 243 } 244 sb.append("]"); 245 return sb.toString(); 246 } 247 248 private static boolean isOnLink(List<RouteInfo> routes, InetAddress ip) { 249 for (RouteInfo route : routes) { 250 if (!route.hasGateway() && route.matches(ip)) { 251 return true; 252 } 253 } 254 return false; 255 } 256 257 public void updateLinkProperties(LinkProperties lp) { 258 if (!mInterfaceParams.name.equals(lp.getInterfaceName())) { 259 // TODO: figure out whether / how to cope with interface changes. 260 Log.wtf(TAG, "requested LinkProperties interface '" + lp.getInterfaceName() + 261 "' does not match: " + mInterfaceParams.name); 262 return; 263 } 264 265 mLinkProperties = new LinkProperties(lp); 266 Map<InetAddress, NeighborEvent> newNeighborWatchList = new HashMap<>(); 267 268 final List<RouteInfo> routes = mLinkProperties.getRoutes(); 269 for (RouteInfo route : routes) { 270 if (route.hasGateway()) { 271 InetAddress gw = route.getGateway(); 272 if (isOnLink(routes, gw)) { 273 newNeighborWatchList.put(gw, mNeighborWatchList.getOrDefault(gw, null)); 274 } 275 } 276 } 277 278 for (InetAddress dns : lp.getDnsServers()) { 279 if (isOnLink(routes, dns)) { 280 newNeighborWatchList.put(dns, mNeighborWatchList.getOrDefault(dns, null)); 281 } 282 } 283 284 mNeighborWatchList = newNeighborWatchList; 285 if (DBG) { Log.d(TAG, "watch: " + describeWatchList()); } 286 } 287 288 public void clearLinkProperties() { 289 mLinkProperties.clear(); 290 mNeighborWatchList.clear(); 291 if (DBG) { Log.d(TAG, "clear: " + describeWatchList()); } 292 } 293 294 private void handleNeighborLost(NeighborEvent event) { 295 final LinkProperties whatIfLp = new LinkProperties(mLinkProperties); 296 297 InetAddress ip = null; 298 for (Map.Entry<InetAddress, NeighborEvent> entry : mNeighborWatchList.entrySet()) { 299 // TODO: Consider using NeighborEvent#isValid() here; it's more 300 // strict but may interact badly if other entries are somehow in 301 // NUD_INCOMPLETE (say, during network attach). 302 if (entry.getValue().nudState != StructNdMsg.NUD_FAILED) continue; 303 304 ip = entry.getKey(); 305 for (RouteInfo route : mLinkProperties.getRoutes()) { 306 if (ip.equals(route.getGateway())) { 307 whatIfLp.removeRoute(route); 308 } 309 } 310 311 if (avoidingBadLinks() || !(ip instanceof Inet6Address)) { 312 // We should do this unconditionally, but alas we cannot: b/31827713. 313 whatIfLp.removeDnsServer(ip); 314 } 315 } 316 317 final ProvisioningChange delta = LinkProperties.compareProvisioning( 318 mLinkProperties, whatIfLp); 319 320 if (delta == ProvisioningChange.LOST_PROVISIONING) { 321 final String logMsg = "FAILURE: LOST_PROVISIONING, " + event; 322 Log.w(TAG, logMsg); 323 if (mCallback != null) { 324 // TODO: remove |ip| when the callback signature no longer has 325 // an InetAddress argument. 326 mCallback.notifyLost(ip, logMsg); 327 } 328 } 329 logNudFailed(delta); 330 } 331 332 private boolean avoidingBadLinks() { 333 return (mMultinetworkPolicyTracker == null) || mMultinetworkPolicyTracker.getAvoidBadWifi(); 334 } 335 336 public void probeAll() { 337 final List<InetAddress> ipProbeList = new ArrayList<>(mNeighborWatchList.keySet()); 338 339 if (!ipProbeList.isEmpty()) { 340 // Keep the CPU awake long enough to allow all ARP/ND 341 // probes a reasonable chance at success. See b/23197666. 342 // 343 // The wakelock we use is (by default) refcounted, and this version 344 // of acquire(timeout) queues a release message to keep acquisitions 345 // and releases balanced. 346 mDependencies.acquireWakeLock(getProbeWakeLockDuration()); 347 } 348 349 for (InetAddress ip : ipProbeList) { 350 final int rval = IpNeighborMonitor.startKernelNeighborProbe(mInterfaceParams.index, ip); 351 mLog.log(String.format("put neighbor %s into NUD_PROBE state (rval=%d)", 352 ip.getHostAddress(), rval)); 353 logEvent(IpReachabilityEvent.PROBE, rval); 354 } 355 mLastProbeTimeMs = SystemClock.elapsedRealtime(); 356 } 357 358 private static long getProbeWakeLockDuration() { 359 // Ideally, this would be computed by examining the values of: 360 // 361 // /proc/sys/net/ipv[46]/neigh/<ifname>/ucast_solicit 362 // 363 // and: 364 // 365 // /proc/sys/net/ipv[46]/neigh/<ifname>/retrans_time_ms 366 // 367 // For now, just make some assumptions. 368 final long numUnicastProbes = 3; 369 final long retransTimeMs = 1000; 370 final long gracePeriodMs = 500; 371 return (numUnicastProbes * retransTimeMs) + gracePeriodMs; 372 } 373 374 private void logEvent(int probeType, int errorCode) { 375 int eventType = probeType | (errorCode & 0xff); 376 mMetricsLog.log(mInterfaceParams.name, new IpReachabilityEvent(eventType)); 377 } 378 379 private void logNudFailed(ProvisioningChange delta) { 380 long duration = SystemClock.elapsedRealtime() - mLastProbeTimeMs; 381 boolean isFromProbe = (duration < getProbeWakeLockDuration()); 382 boolean isProvisioningLost = (delta == ProvisioningChange.LOST_PROVISIONING); 383 int eventType = IpReachabilityEvent.nudFailureEventType(isFromProbe, isProvisioningLost); 384 mMetricsLog.log(mInterfaceParams.name, new IpReachabilityEvent(eventType)); 385 } 386 } 387