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