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 static android.net.metrics.IpReachabilityEvent.NUD_FAILED; 20 import static android.net.metrics.IpReachabilityEvent.NUD_FAILED_ORGANIC; 21 import static android.net.metrics.IpReachabilityEvent.PROVISIONING_LOST; 22 import static android.net.metrics.IpReachabilityEvent.PROVISIONING_LOST_ORGANIC; 23 import static android.provider.DeviceConfig.NAMESPACE_CONNECTIVITY; 24 25 import static com.android.networkstack.util.NetworkStackUtils.IP_REACHABILITY_IGNORE_INCOMPLETE_IPV6_DEFAULT_ROUTER_VERSION; 26 import static com.android.networkstack.util.NetworkStackUtils.IP_REACHABILITY_IGNORE_INCOMPLETE_IPV6_DNS_SERVER_VERSION; 27 import static com.android.networkstack.util.NetworkStackUtils.IP_REACHABILITY_MCAST_RESOLICIT_VERSION; 28 29 import android.content.Context; 30 import android.net.ConnectivityManager; 31 import android.net.INetd; 32 import android.net.LinkProperties; 33 import android.net.RouteInfo; 34 import android.net.metrics.IpConnectivityLog; 35 import android.net.metrics.IpReachabilityEvent; 36 import android.net.networkstack.aidl.ip.ReachabilityLossReason; 37 import android.os.ConditionVariable; 38 import android.os.Handler; 39 import android.os.Looper; 40 import android.os.PowerManager; 41 import android.os.PowerManager.WakeLock; 42 import android.os.RemoteException; 43 import android.os.SystemClock; 44 import android.stats.connectivity.IpType; 45 import android.stats.connectivity.NudEventType; 46 import android.stats.connectivity.NudNeighborType; 47 import android.text.TextUtils; 48 import android.util.Log; 49 50 import androidx.annotation.NonNull; 51 import androidx.annotation.Nullable; 52 53 import com.android.internal.annotations.VisibleForTesting; 54 import com.android.internal.util.Preconditions; 55 import com.android.net.module.util.DeviceConfigUtils; 56 import com.android.net.module.util.InterfaceParams; 57 import com.android.net.module.util.SharedLog; 58 import com.android.net.module.util.ip.IpNeighborMonitor; 59 import com.android.net.module.util.ip.IpNeighborMonitor.NeighborEvent; 60 import com.android.net.module.util.ip.IpNeighborMonitor.NeighborEventConsumer; 61 import com.android.net.module.util.netlink.StructNdMsg; 62 import com.android.networkstack.R; 63 import com.android.networkstack.metrics.IpReachabilityMonitorMetrics; 64 65 import java.io.PrintWriter; 66 import java.net.Inet6Address; 67 import java.net.InetAddress; 68 import java.util.ArrayList; 69 import java.util.HashMap; 70 import java.util.List; 71 import java.util.Map; 72 73 74 /** 75 * IpReachabilityMonitor. 76 * 77 * Monitors on-link IP reachability and notifies callers whenever any on-link 78 * addresses of interest appear to have become unresponsive. 79 * 80 * This code does not concern itself with "why" a neighbour might have become 81 * unreachable. Instead, it primarily reacts to the kernel's notion of IP 82 * reachability for each of the neighbours we know to be critically important 83 * to normal network connectivity. As such, it is often "just the messenger": 84 * the neighbours about which it warns are already deemed by the kernel to have 85 * become unreachable. 86 * 87 * 88 * How it works: 89 * 90 * 1. The "on-link neighbours of interest" found in a given LinkProperties 91 * instance are added to a "watch list" via #updateLinkProperties(). 92 * This usually means all default gateways and any on-link DNS servers. 93 * 94 * 2. We listen continuously for netlink neighbour messages (RTM_NEWNEIGH, 95 * RTM_DELNEIGH), watching only for neighbours in the watch list. 96 * 97 * - A neighbour going into NUD_REACHABLE, NUD_STALE, NUD_DELAY, and 98 * even NUD_PROBE is perfectly normal; we merely record the new state. 99 * 100 * - A neighbour's entry may be deleted (RTM_DELNEIGH), for example due 101 * to garbage collection. This is not necessarily of immediate 102 * concern; we record the neighbour as moving to NUD_NONE. 103 * 104 * - A neighbour transitioning to NUD_FAILED (for any reason) is 105 * critically important and is handled as described below in #4. 106 * 107 * 3. All on-link neighbours in the watch list can be forcibly "probed" by 108 * calling #probeAll(). This should be called whenever it is important to 109 * verify that critical neighbours on the link are still reachable, e.g. 110 * when roaming between BSSIDs. 111 * 112 * - The kernel will send unicast ARP requests for IPv4 neighbours and 113 * unicast NS packets for IPv6 neighbours. The expected replies will 114 * likely be unicast. 115 * 116 * - The forced probing is done holding a wakelock. The kernel may, 117 * however, initiate probing of a neighbor on its own, i.e. whenever 118 * a neighbour has expired from NUD_DELAY. 119 * 120 * - The kernel sends: 121 * 122 * /proc/sys/net/ipv{4,6}/neigh/<ifname>/ucast_solicit 123 * 124 * number of probes (usually 3) every: 125 * 126 * /proc/sys/net/ipv{4,6}/neigh/<ifname>/retrans_time_ms 127 * 128 * number of milliseconds (usually 1000ms). This normally results in 129 * 3 unicast packets, 1 per second. 130 * 131 * - If no response is received to any of the probe packets, the kernel 132 * marks the neighbour as being in state NUD_FAILED, and the listening 133 * process in #2 will learn of it. 134 * 135 * 4. We call the supplied Callback#notifyLost() function if the loss of a 136 * neighbour in NUD_FAILED would cause IPv4 or IPv6 configuration to 137 * become incomplete (a loss of provisioning). 138 * 139 * - For example, losing all our IPv4 on-link DNS servers (or losing 140 * our only IPv6 default gateway) constitutes a loss of IPv4 (IPv6) 141 * provisioning; Callback#notifyLost() would be called. 142 * 143 * - Since it can be non-trivial to reacquire certain IP provisioning 144 * state it may be best for the link to disconnect completely and 145 * reconnect afresh. 146 * 147 * Accessing an instance of this class from multiple threads is NOT safe. 148 * 149 * @hide 150 */ 151 public class IpReachabilityMonitor { 152 private static final String TAG = "IpReachabilityMonitor"; 153 private static final boolean DBG = Log.isLoggable(TAG, Log.DEBUG); 154 private static final boolean VDBG = Log.isLoggable(TAG, Log.VERBOSE); 155 156 // Upper and lower bound for NUD probe parameters. 157 protected static final int MAX_NUD_SOLICIT_NUM = 15; 158 protected static final int MIN_NUD_SOLICIT_NUM = 5; 159 protected static final int MAX_NUD_SOLICIT_INTERVAL_MS = 1000; 160 protected static final int MIN_NUD_SOLICIT_INTERVAL_MS = 750; 161 protected static final int NUD_MCAST_RESOLICIT_NUM = 3; 162 private static final int INVALID_NUD_MCAST_RESOLICIT_NUM = -1; 163 164 private static final int INVALID_LEGACY_NUD_FAILURE_TYPE = -1; 165 public static final int INVALID_REACHABILITY_LOSS_TYPE = -1; 166 167 public interface Callback { 168 /** 169 * This callback function must execute as quickly as possible as it is 170 * run on the same thread that listens to kernel neighbor updates. 171 * 172 * TODO: refactor to something like notifyProvisioningLost(String msg). 173 */ notifyLost(InetAddress ip, String logMsg, NudEventType type)174 void notifyLost(InetAddress ip, String logMsg, NudEventType type); 175 } 176 177 /** 178 * Encapsulates IpReachabilityMonitor dependencies on systems that hinder unit testing. 179 * TODO: consider also wrapping MultinetworkPolicyTracker in this interface. 180 */ 181 @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) 182 public interface Dependencies { acquireWakeLock(long durationMs)183 void acquireWakeLock(long durationMs); makeIpNeighborMonitor(Handler h, SharedLog log, NeighborEventConsumer cb)184 IpNeighborMonitor makeIpNeighborMonitor(Handler h, SharedLog log, NeighborEventConsumer cb); isFeatureEnabled(Context context, String name, boolean defaultEnabled)185 boolean isFeatureEnabled(Context context, String name, boolean defaultEnabled); getIpReachabilityMonitorMetrics()186 IpReachabilityMonitorMetrics getIpReachabilityMonitorMetrics(); 187 makeDefault(Context context, String iface)188 static Dependencies makeDefault(Context context, String iface) { 189 final String lockName = TAG + "." + iface; 190 final PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE); 191 final WakeLock lock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, lockName); 192 193 return new Dependencies() { 194 public void acquireWakeLock(long durationMs) { 195 lock.acquire(durationMs); 196 } 197 198 public IpNeighborMonitor makeIpNeighborMonitor(Handler h, SharedLog log, 199 NeighborEventConsumer cb) { 200 return new IpNeighborMonitor(h, log, cb); 201 } 202 203 public boolean isFeatureEnabled(final Context context, final String name, 204 boolean defaultEnabled) { 205 return DeviceConfigUtils.isFeatureEnabled(context, NAMESPACE_CONNECTIVITY, name, 206 defaultEnabled); 207 } 208 209 public IpReachabilityMonitorMetrics getIpReachabilityMonitorMetrics() { 210 return new IpReachabilityMonitorMetrics(); 211 } 212 }; 213 } 214 } 215 216 private final InterfaceParams mInterfaceParams; 217 private final IpNeighborMonitor mIpNeighborMonitor; 218 private final SharedLog mLog; 219 private final Dependencies mDependencies; 220 private final boolean mUsingMultinetworkPolicyTracker; 221 private final ConnectivityManager mCm; 222 private final IpConnectivityLog mMetricsLog; 223 private final Context mContext; 224 private final INetd mNetd; 225 private final IpReachabilityMonitorMetrics mIpReachabilityMetrics; 226 private LinkProperties mLinkProperties = new LinkProperties(); 227 private Map<InetAddress, NeighborEvent> mNeighborWatchList = new HashMap<>(); 228 // Time in milliseconds of the last forced probe request. 229 private volatile long mLastProbeTimeMs; 230 // Time in milliseconds of the last forced probe request due to roam or CMD_CONFIRM. 231 private long mLastProbeDueToRoamMs; 232 private long mLastProbeDueToConfirmMs; 233 private int mNumSolicits; 234 private int mInterSolicitIntervalMs; 235 @NonNull 236 private final Callback mCallback; 237 private final boolean mMulticastResolicitEnabled; 238 private final boolean mIgnoreIncompleteIpv6DnsServerEnabled; 239 private final boolean mIgnoreIncompleteIpv6DefaultRouterEnabled; 240 241 public IpReachabilityMonitor( 242 Context context, InterfaceParams ifParams, Handler h, SharedLog log, Callback callback, 243 boolean usingMultinetworkPolicyTracker, Dependencies dependencies, final INetd netd) { 244 this(context, ifParams, h, log, callback, usingMultinetworkPolicyTracker, dependencies, 245 new IpConnectivityLog(), netd); 246 } 247 248 @VisibleForTesting 249 public IpReachabilityMonitor(Context context, InterfaceParams ifParams, Handler h, 250 SharedLog log, Callback callback, boolean usingMultinetworkPolicyTracker, 251 Dependencies dependencies, final IpConnectivityLog metricsLog, final INetd netd) { 252 if (ifParams == null) throw new IllegalArgumentException("null InterfaceParams"); 253 254 mContext = context; 255 mInterfaceParams = ifParams; 256 mLog = log.forSubComponent(TAG); 257 mCallback = callback; 258 mUsingMultinetworkPolicyTracker = usingMultinetworkPolicyTracker; 259 mCm = context.getSystemService(ConnectivityManager.class); 260 mDependencies = dependencies; 261 mMulticastResolicitEnabled = dependencies.isFeatureEnabled(context, 262 IP_REACHABILITY_MCAST_RESOLICIT_VERSION, true /* defaultEnabled */); 263 mIgnoreIncompleteIpv6DnsServerEnabled = dependencies.isFeatureEnabled(context, 264 IP_REACHABILITY_IGNORE_INCOMPLETE_IPV6_DNS_SERVER_VERSION, 265 false /* defaultEnabled */); 266 mIgnoreIncompleteIpv6DefaultRouterEnabled = dependencies.isFeatureEnabled(context, 267 IP_REACHABILITY_IGNORE_INCOMPLETE_IPV6_DEFAULT_ROUTER_VERSION, 268 false /* defaultEnabled */); 269 mMetricsLog = metricsLog; 270 mNetd = netd; 271 Preconditions.checkNotNull(mNetd); 272 Preconditions.checkArgument(!TextUtils.isEmpty(mInterfaceParams.name)); 273 274 // In case the overylaid parameters specify an invalid configuration, set the parameters 275 // to the hardcoded defaults first, then set them to the values used in the steady state. 276 try { 277 int numResolicits = mMulticastResolicitEnabled 278 ? NUD_MCAST_RESOLICIT_NUM 279 : INVALID_NUD_MCAST_RESOLICIT_NUM; 280 setNeighborParameters(MIN_NUD_SOLICIT_NUM, MIN_NUD_SOLICIT_INTERVAL_MS, numResolicits); 281 } catch (Exception e) { 282 Log.e(TAG, "Failed to adjust neighbor parameters with hardcoded defaults"); 283 } 284 setNeighbourParametersForSteadyState(); 285 286 mIpNeighborMonitor = dependencies.makeIpNeighborMonitor(h, mLog, 287 (NeighborEvent event) -> { 288 if (mInterfaceParams.index != event.ifindex) return; 289 if (!mNeighborWatchList.containsKey(event.ip)) return; 290 291 final NeighborEvent prev = mNeighborWatchList.put(event.ip, event); 292 293 // TODO: Consider what to do with other states that are not within 294 // NeighborEvent#isValid() (i.e. NUD_NONE, NUD_INCOMPLETE). 295 if (event.nudState == StructNdMsg.NUD_FAILED) { 296 // After both unicast probe and multicast probe(if mcast_resolicit is not 0) 297 // attempts fail, trigger the neighbor lost event and disconnect. 298 mLog.w("ALERT neighbor went from: " + prev + " to: " + event); 299 handleNeighborLost(prev, event); 300 } else if (event.nudState == StructNdMsg.NUD_REACHABLE) { 301 handleNeighborReachable(prev, event); 302 } 303 }); 304 mIpNeighborMonitor.start(); 305 mIpReachabilityMetrics = dependencies.getIpReachabilityMonitorMetrics(); 306 } 307 308 public void stop() { 309 mIpNeighborMonitor.stop(); 310 clearLinkProperties(); 311 } 312 313 public void dump(PrintWriter pw) { 314 if (Looper.myLooper() == mIpNeighborMonitor.getHandler().getLooper()) { 315 pw.println(describeWatchList("\n")); 316 return; 317 } 318 319 final ConditionVariable cv = new ConditionVariable(false); 320 mIpNeighborMonitor.getHandler().post(() -> { 321 pw.println(describeWatchList("\n")); 322 cv.open(); 323 }); 324 325 if (!cv.block(1000)) { 326 pw.println("Timed out waiting for IpReachabilityMonitor dump"); 327 } 328 } 329 330 private String describeWatchList() { return describeWatchList(" "); } 331 332 private String describeWatchList(String sep) { 333 final StringBuilder sb = new StringBuilder(); 334 sb.append("iface{" + mInterfaceParams + "}," + sep); 335 sb.append("ntable=[" + sep); 336 String delimiter = ""; 337 for (Map.Entry<InetAddress, NeighborEvent> entry : mNeighborWatchList.entrySet()) { 338 sb.append(delimiter).append(entry.getKey().getHostAddress() + "/" + entry.getValue()); 339 delimiter = "," + sep; 340 } 341 sb.append("]"); 342 return sb.toString(); 343 } 344 345 @VisibleForTesting 346 static boolean isOnLink(List<RouteInfo> routes, InetAddress ip) { 347 for (RouteInfo route : routes) { 348 if (!route.hasGateway() && route.matches(ip) 349 && route.getType() == RouteInfo.RTN_UNICAST) { 350 return true; 351 } 352 } 353 return false; 354 } 355 356 private boolean hasDefaultRouterNeighborMacAddressChanged( 357 @Nullable final NeighborEvent prev, @NonNull final NeighborEvent event) { 358 if (prev == null || !isNeighborDefaultRouter(event)) return false; 359 return !event.macAddr.equals(prev.macAddr); 360 } 361 362 private boolean isNeighborDefaultRouter(@NonNull final NeighborEvent event) { 363 // For the IPv6 link-local scoped address, equals() works because the NeighborEvent.ip 364 // doesn't have a scope id and Inet6Address#equals doesn't consider scope id neither. 365 for (RouteInfo route : mLinkProperties.getRoutes()) { 366 if (route.isDefaultRoute() && event.ip.equals(route.getGateway())) return true; 367 } 368 return false; 369 } 370 371 private boolean isNeighborDnsServer(@NonNull final NeighborEvent event) { 372 for (InetAddress dns : mLinkProperties.getDnsServers()) { 373 if (event.ip.equals(dns)) return true; 374 } 375 return false; 376 } 377 378 public void updateLinkProperties(LinkProperties lp) { 379 if (!mInterfaceParams.name.equals(lp.getInterfaceName())) { 380 // TODO: figure out whether / how to cope with interface changes. 381 Log.wtf(TAG, "requested LinkProperties interface '" + lp.getInterfaceName() + 382 "' does not match: " + mInterfaceParams.name); 383 return; 384 } 385 386 mLinkProperties = new LinkProperties(lp); 387 Map<InetAddress, NeighborEvent> newNeighborWatchList = new HashMap<>(); 388 389 final List<RouteInfo> routes = mLinkProperties.getRoutes(); 390 for (RouteInfo route : routes) { 391 if (route.hasGateway()) { 392 InetAddress gw = route.getGateway(); 393 if (isOnLink(routes, gw)) { 394 newNeighborWatchList.put(gw, mNeighborWatchList.getOrDefault(gw, null)); 395 } 396 } 397 } 398 399 for (InetAddress dns : lp.getDnsServers()) { 400 if (isOnLink(routes, dns)) { 401 newNeighborWatchList.put(dns, mNeighborWatchList.getOrDefault(dns, null)); 402 } 403 } 404 405 mNeighborWatchList = newNeighborWatchList; 406 if (DBG) { Log.d(TAG, "watch: " + describeWatchList()); } 407 } 408 409 public void clearLinkProperties() { 410 mLinkProperties.clear(); 411 mNeighborWatchList.clear(); 412 if (DBG) { Log.d(TAG, "clear: " + describeWatchList()); } 413 } 414 415 private void handleNeighborReachable(@Nullable final NeighborEvent prev, 416 @NonNull final NeighborEvent event) { 417 if (mMulticastResolicitEnabled 418 && hasDefaultRouterNeighborMacAddressChanged(prev, event)) { 419 // This implies device has confirmed the neighbor's reachability from 420 // other states(e.g., NUD_PROBE or NUD_STALE), checking if the mac 421 // address hasn't changed is required. If Mac address does change, then 422 // trigger a new neighbor lost event and disconnect. 423 final String logMsg = "ALERT neighbor: " + event.ip 424 + " MAC address changed from: " + prev.macAddr 425 + " to: " + event.macAddr; 426 final NudEventType type = 427 getMacAddressChangedEventType(isFromProbe(), isNudFailureDueToRoam()); 428 mLog.w(logMsg); 429 mCallback.notifyLost(event.ip, logMsg, type); 430 logNudFailed(event, type); 431 return; 432 } 433 maybeRestoreNeighborParameters(); 434 } 435 436 private boolean shouldIgnoreIncompleteIpv6Neighbor(@Nullable final NeighborEvent prev, 437 @NonNull final NeighborEvent event) { 438 // If it isn't IPv6 neighbor, return false. 439 if (!(event.ip instanceof Inet6Address)) return false; 440 441 // If neighbor isn't in the watch list, return false. 442 if (!mNeighborWatchList.containsKey(event.ip)) return false; 443 444 // For on-link IPv6 DNS server or default router that never ever responds to address 445 // resolution, kernel will send RTM_NEWNEIGH with NUD_FAILED to user space directly, 446 // and there is no netlink neighbor events related to this neighbor received before. 447 return (prev == null || event.nudState == StructNdMsg.NUD_FAILED); 448 } 449 450 private void handleNeighborLost(@Nullable final NeighborEvent prev, 451 @NonNull final NeighborEvent event) { 452 final LinkProperties whatIfLp = new LinkProperties(mLinkProperties); 453 454 InetAddress ip = null; 455 for (Map.Entry<InetAddress, NeighborEvent> entry : mNeighborWatchList.entrySet()) { 456 // TODO: Consider using NeighborEvent#isValid() here; it's more 457 // strict but may interact badly if other entries are somehow in 458 // NUD_INCOMPLETE (say, during network attach). 459 final NeighborEvent val = entry.getValue(); 460 461 // Find all the neighbors that have gone into FAILED state. 462 // Ignore entries for which we have never received an event. If there are neighbors 463 // that never respond to ARP/ND, the kernel will send several FAILED events, then 464 // an INCOMPLETE event, and then more FAILED events. The INCOMPLETE event will 465 // populate the map and the subsequent FAILED event will be processed. 466 if (val == null || val.nudState != StructNdMsg.NUD_FAILED) continue; 467 468 ip = entry.getKey(); 469 for (RouteInfo route : mLinkProperties.getRoutes()) { 470 if (ip.equals(route.getGateway())) { 471 whatIfLp.removeRoute(route); 472 } 473 } 474 475 if (avoidingBadLinks() || !(ip instanceof Inet6Address)) { 476 // We should do this unconditionally, but alas we cannot: b/31827713. 477 whatIfLp.removeDnsServer(ip); 478 } 479 } 480 481 final boolean ignoreIncompleteIpv6DnsServer = 482 mIgnoreIncompleteIpv6DnsServerEnabled 483 && isNeighborDnsServer(event) 484 && shouldIgnoreIncompleteIpv6Neighbor(prev, event); 485 486 // Generally Router Advertisement should take SLLA option, then device won't do address 487 // resolution for default router's IPv6 link-local address automatically. But sometimes 488 // it may miss SLLA option, also add a flag to check these cases. 489 final boolean ignoreIncompleteIpv6DefaultRouter = 490 mIgnoreIncompleteIpv6DefaultRouterEnabled 491 && isNeighborDefaultRouter(event) 492 && shouldIgnoreIncompleteIpv6Neighbor(prev, event); 493 494 // Only ignore the incomplete IPv6 neighbor iff IPv4 is still provisioned. For IPv6-only 495 // networks, we MUST not ignore any incomplete IPv6 neighbor. 496 final boolean ignoreIncompleteIpv6Neighbor = 497 (ignoreIncompleteIpv6DnsServer || ignoreIncompleteIpv6DefaultRouter) 498 && whatIfLp.isIpv4Provisioned(); 499 500 // It's better to remove the incompleted on-link IPv6 DNS server or default router from 501 // watch list, otherwise, when wifi invokes probeAll later (e.g. post roam) to send probe 502 // to an incompleted on-link DNS server or default router, it should fail to send netlink 503 // message to kernel as there is no neighbor cache entry for it at all. 504 if (ignoreIncompleteIpv6Neighbor) { 505 Log.d(TAG, "remove incomplete IPv6 neighbor " + event.ip 506 + " which fails to respond to address resolution from watch list."); 507 mNeighborWatchList.remove(event.ip); 508 } 509 510 final boolean lostProvisioning = 511 (mLinkProperties.isIpv4Provisioned() && !whatIfLp.isIpv4Provisioned()) 512 || (mLinkProperties.isIpv6Provisioned() && !whatIfLp.isIpv6Provisioned() 513 && !ignoreIncompleteIpv6Neighbor); 514 final NudEventType type = getNudFailureEventType(isFromProbe(), 515 isNudFailureDueToRoam(), lostProvisioning); 516 517 if (lostProvisioning) { 518 final String logMsg = "FAILURE: LOST_PROVISIONING, " + event; 519 Log.w(TAG, logMsg); 520 // TODO: remove |ip| when the callback signature no longer has 521 // an InetAddress argument. 522 mCallback.notifyLost(ip, logMsg, type); 523 } 524 logNudFailed(event, type); 525 } 526 527 private void maybeRestoreNeighborParameters() { 528 for (Map.Entry<InetAddress, NeighborEvent> entry : mNeighborWatchList.entrySet()) { 529 if (DBG) { 530 Log.d(TAG, "neighbour IPv4(v6): " + entry.getKey() + " neighbour state: " 531 + StructNdMsg.stringForNudState(entry.getValue().nudState)); 532 } 533 final NeighborEvent val = entry.getValue(); 534 // If an entry is null, consider that probing for that neighbour has completed. 535 if (val == null || val.nudState != StructNdMsg.NUD_REACHABLE) return; 536 } 537 538 // Probing for all neighbours in the watchlist is complete and the connection is stable, 539 // restore NUD probe parameters to steadystate value. In the case where neighbours 540 // are responsive, this code will run before the wakelock expires. 541 setNeighbourParametersForSteadyState(); 542 } 543 544 private boolean avoidingBadLinks() { 545 return !mUsingMultinetworkPolicyTracker || mCm.shouldAvoidBadWifi(); 546 } 547 548 /** 549 * Force probe to verify whether or not the critical on-link neighbours are still reachable. 550 * 551 * @param dueToRoam indicate on which situation forced probe has been sent, e.g., on post 552 * roaming or receiving CMD_CONFIRM from IpClient. 553 */ 554 public void probeAll(boolean dueToRoam) { 555 setNeighbourParametersPostRoaming(); 556 557 final List<InetAddress> ipProbeList = new ArrayList<>(mNeighborWatchList.keySet()); 558 if (!ipProbeList.isEmpty()) { 559 // Keep the CPU awake long enough to allow all ARP/ND 560 // probes a reasonable chance at success. See b/23197666. 561 // 562 // The wakelock we use is (by default) refcounted, and this version 563 // of acquire(timeout) queues a release message to keep acquisitions 564 // and releases balanced. 565 mDependencies.acquireWakeLock(getProbeWakeLockDuration()); 566 } 567 568 for (InetAddress ip : ipProbeList) { 569 final int rval = IpNeighborMonitor.startKernelNeighborProbe(mInterfaceParams.index, ip); 570 mLog.log(String.format("put neighbor %s into NUD_PROBE state (rval=%d)", 571 ip.getHostAddress(), rval)); 572 logEvent(IpReachabilityEvent.PROBE, rval); 573 } 574 mLastProbeTimeMs = SystemClock.elapsedRealtime(); 575 if (dueToRoam) { 576 mLastProbeDueToRoamMs = mLastProbeTimeMs; 577 } else { 578 mLastProbeDueToConfirmMs = mLastProbeTimeMs; 579 } 580 } 581 582 private long getProbeWakeLockDuration() { 583 final long gracePeriodMs = 500; 584 final int numSolicits = 585 mNumSolicits + (mMulticastResolicitEnabled ? NUD_MCAST_RESOLICIT_NUM : 0); 586 return (long) (numSolicits * mInterSolicitIntervalMs) + gracePeriodMs; 587 } 588 589 private void setNeighbourParametersPostRoaming() { 590 setNeighborParametersFromResources(R.integer.config_nud_postroaming_solicit_num, 591 R.integer.config_nud_postroaming_solicit_interval); 592 } 593 594 private void setNeighbourParametersForSteadyState() { 595 setNeighborParametersFromResources(R.integer.config_nud_steadystate_solicit_num, 596 R.integer.config_nud_steadystate_solicit_interval); 597 } 598 599 private void setNeighborParametersFromResources(final int numResId, final int intervalResId) { 600 try { 601 final int numSolicits = mContext.getResources().getInteger(numResId); 602 final int interSolicitIntervalMs = mContext.getResources().getInteger(intervalResId); 603 setNeighborParameters(numSolicits, interSolicitIntervalMs); 604 } catch (Exception e) { 605 Log.e(TAG, "Failed to adjust neighbor parameters"); 606 } 607 } 608 609 private void setNeighborParameters(int numSolicits, int interSolicitIntervalMs) 610 throws RemoteException, IllegalArgumentException { 611 // Do not set mcast_resolicit param by default. 612 setNeighborParameters(numSolicits, interSolicitIntervalMs, INVALID_NUD_MCAST_RESOLICIT_NUM); 613 } 614 615 private void setNeighborParameters(int numSolicits, int interSolicitIntervalMs, 616 int numResolicits) throws RemoteException, IllegalArgumentException { 617 Preconditions.checkArgument(numSolicits >= MIN_NUD_SOLICIT_NUM, 618 "numSolicits must be at least " + MIN_NUD_SOLICIT_NUM); 619 Preconditions.checkArgument(numSolicits <= MAX_NUD_SOLICIT_NUM, 620 "numSolicits must be at most " + MAX_NUD_SOLICIT_NUM); 621 Preconditions.checkArgument(interSolicitIntervalMs >= MIN_NUD_SOLICIT_INTERVAL_MS, 622 "interSolicitIntervalMs must be at least " + MIN_NUD_SOLICIT_INTERVAL_MS); 623 Preconditions.checkArgument(interSolicitIntervalMs <= MAX_NUD_SOLICIT_INTERVAL_MS, 624 "interSolicitIntervalMs must be at most " + MAX_NUD_SOLICIT_INTERVAL_MS); 625 626 for (int family : new Integer[]{INetd.IPV4, INetd.IPV6}) { 627 mNetd.setProcSysNet(family, INetd.NEIGH, mInterfaceParams.name, "retrans_time_ms", 628 Integer.toString(interSolicitIntervalMs)); 629 mNetd.setProcSysNet(family, INetd.NEIGH, mInterfaceParams.name, "ucast_solicit", 630 Integer.toString(numSolicits)); 631 if (numResolicits != INVALID_NUD_MCAST_RESOLICIT_NUM) { 632 mNetd.setProcSysNet(family, INetd.NEIGH, mInterfaceParams.name, "mcast_resolicit", 633 Integer.toString(numResolicits)); 634 } 635 } 636 637 mNumSolicits = numSolicits; 638 mInterSolicitIntervalMs = interSolicitIntervalMs; 639 } 640 641 private boolean isFromProbe() { 642 final long duration = SystemClock.elapsedRealtime() - mLastProbeTimeMs; 643 return duration < getProbeWakeLockDuration(); 644 } 645 646 private boolean isNudFailureDueToRoam() { 647 if (!isFromProbe()) return false; 648 649 // Check to which probe expiry the curren timestamp gets close when NUD failure event 650 // happens, theoretically that indicates which probe event(due to roam or CMD_CONFIRM) 651 // was triggered eariler. 652 // 653 // Note that this would be incorrect if the probe or confirm was so long ago that the 654 // probe duration has already expired. That cannot happen because isFromProbe would return 655 // false. 656 final long probeExpiryAfterRoam = mLastProbeDueToRoamMs + getProbeWakeLockDuration(); 657 final long probeExpiryAfterConfirm = 658 mLastProbeDueToConfirmMs + getProbeWakeLockDuration(); 659 final long currentTime = SystemClock.elapsedRealtime(); 660 return Math.abs(probeExpiryAfterRoam - currentTime) 661 < Math.abs(probeExpiryAfterConfirm - currentTime); 662 } 663 664 private void logEvent(int probeType, int errorCode) { 665 int eventType = probeType | (errorCode & 0xff); 666 mMetricsLog.log(mInterfaceParams.name, new IpReachabilityEvent(eventType)); 667 } 668 669 private void logNudFailed(final NeighborEvent event, final NudEventType type) { 670 logNeighborLostEvent(event, type); 671 672 // The legacy metrics only record whether the failure came from a probe and whether 673 // the network is still provisioned. They do not record provisioning failures due to 674 // multicast resolicits finding that the MAC address has changed. 675 final int eventType = legacyNudFailureType(type); 676 if (eventType == INVALID_LEGACY_NUD_FAILURE_TYPE) return; 677 mMetricsLog.log(mInterfaceParams.name, new IpReachabilityEvent(eventType)); 678 } 679 680 /** 681 * Returns the neighbor type code corresponding to the given conditions. 682 */ 683 private NudNeighborType getNeighborType(final NeighborEvent event) { 684 final boolean isGateway = isNeighborDefaultRouter(event); 685 final boolean isDnsServer = isNeighborDnsServer(event); 686 687 if (isGateway && isDnsServer) return NudNeighborType.NUD_NEIGHBOR_BOTH; 688 if (isGateway && !isDnsServer) return NudNeighborType.NUD_NEIGHBOR_GATEWAY; 689 if (!isGateway && isDnsServer) return NudNeighborType.NUD_NEIGHBOR_DNS; 690 return NudNeighborType.NUD_NEIGHBOR_UNKNOWN; 691 } 692 693 /** 694 * Returns the NUD failure event type code corresponding to the given conditions. 695 */ 696 private static NudEventType getNudFailureEventType(boolean isFromProbe, boolean isDueToRoam, 697 boolean isProvisioningLost) { 698 if (!isFromProbe) { 699 return isProvisioningLost 700 ? NudEventType.NUD_ORGANIC_FAILED_CRITICAL 701 : NudEventType.NUD_ORGANIC_FAILED; 702 } 703 return isProvisioningLost 704 ? isDueToRoam 705 ? NudEventType.NUD_POST_ROAMING_FAILED_CRITICAL 706 : NudEventType.NUD_CONFIRM_FAILED_CRITICAL 707 : isDueToRoam 708 ? NudEventType.NUD_POST_ROAMING_FAILED 709 : NudEventType.NUD_CONFIRM_FAILED; 710 } 711 712 /** 713 * Returns the NUD failure event type code due to neighbor's MAC address has changed 714 * corresponding to the given conditions. 715 */ 716 private static NudEventType getMacAddressChangedEventType(boolean isFromProbe, 717 boolean isDueToRoam) { 718 return isFromProbe 719 ? isDueToRoam 720 ? NudEventType.NUD_POST_ROAMING_MAC_ADDRESS_CHANGED 721 : NudEventType.NUD_CONFIRM_MAC_ADDRESS_CHANGED 722 : NudEventType.NUD_ORGANIC_MAC_ADDRESS_CHANGED; 723 } 724 725 /** 726 * Log NUD failure metrics with new statsd APIs while the function using mMetricsLog API 727 * still sends the legacy metrics, @see #logNudFailed. 728 */ 729 private void logNeighborLostEvent(final NeighborEvent event, final NudEventType type) { 730 final IpType ipType = (event.ip instanceof Inet6Address) ? IpType.IPV6 : IpType.IPV4; 731 mIpReachabilityMetrics.setNudIpType(ipType); 732 mIpReachabilityMetrics.setNudNeighborType(getNeighborType(event)); 733 mIpReachabilityMetrics.setNudEventType(type); 734 mIpReachabilityMetrics.statsWrite(); 735 } 736 737 /** 738 * Returns the NUD failure event type code corresponding to the given conditions. 739 */ 740 private static int legacyNudFailureType(final NudEventType type) { 741 switch (type) { 742 case NUD_POST_ROAMING_FAILED: 743 case NUD_CONFIRM_FAILED: 744 return NUD_FAILED; 745 case NUD_POST_ROAMING_FAILED_CRITICAL: 746 case NUD_CONFIRM_FAILED_CRITICAL: 747 return PROVISIONING_LOST; 748 case NUD_ORGANIC_FAILED: 749 return NUD_FAILED_ORGANIC; 750 case NUD_ORGANIC_FAILED_CRITICAL: 751 return PROVISIONING_LOST_ORGANIC; 752 default: 753 // Do not log legacy event 754 return INVALID_LEGACY_NUD_FAILURE_TYPE; 755 } 756 } 757 758 /** 759 * Convert the NUD critical failure event type to a int constant defined in IIpClientCallbacks. 760 */ 761 public static int nudEventTypeToInt(final NudEventType type) { 762 switch (type) { 763 case NUD_POST_ROAMING_FAILED_CRITICAL: 764 case NUD_POST_ROAMING_MAC_ADDRESS_CHANGED: 765 return ReachabilityLossReason.ROAM; 766 case NUD_CONFIRM_FAILED_CRITICAL: 767 case NUD_CONFIRM_MAC_ADDRESS_CHANGED: 768 return ReachabilityLossReason.CONFIRM; 769 case NUD_ORGANIC_FAILED_CRITICAL: 770 case NUD_ORGANIC_MAC_ADDRESS_CHANGED: 771 return ReachabilityLossReason.ORGANIC; 772 // For other NudEventType which won't trigger notifyLost, just ignore these events. 773 default: 774 return INVALID_REACHABILITY_LOSS_TYPE; 775 } 776 } 777 } 778