• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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