• 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 
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