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