• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2016 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 com.android.internal.util.MessageUtils;
20 import com.android.internal.util.WakeupMessage;
21 
22 import android.content.Context;
23 import android.net.DhcpResults;
24 import android.net.INetd;
25 import android.net.InterfaceConfiguration;
26 import android.net.IpPrefix;
27 import android.net.LinkAddress;
28 import android.net.LinkProperties.ProvisioningChange;
29 import android.net.LinkProperties;
30 import android.net.ProxyInfo;
31 import android.net.RouteInfo;
32 import android.net.StaticIpConfiguration;
33 import android.net.apf.ApfCapabilities;
34 import android.net.apf.ApfFilter;
35 import android.net.dhcp.DhcpClient;
36 import android.net.metrics.IpConnectivityLog;
37 import android.net.metrics.IpManagerEvent;
38 import android.net.util.MultinetworkPolicyTracker;
39 import android.net.util.NetdService;
40 import android.net.util.NetworkConstants;
41 import android.net.util.SharedLog;
42 import android.os.INetworkManagementService;
43 import android.os.Message;
44 import android.os.RemoteException;
45 import android.os.ServiceManager;
46 import android.os.ServiceSpecificException;
47 import android.os.SystemClock;
48 import android.system.OsConstants;
49 import android.text.TextUtils;
50 import android.util.LocalLog;
51 import android.util.Log;
52 import android.util.SparseArray;
53 
54 import com.android.internal.annotations.VisibleForTesting;
55 import com.android.internal.R;
56 import com.android.internal.util.IndentingPrintWriter;
57 import com.android.internal.util.IState;
58 import com.android.internal.util.Preconditions;
59 import com.android.internal.util.State;
60 import com.android.internal.util.StateMachine;
61 import com.android.server.net.NetlinkTracker;
62 
63 import java.io.FileDescriptor;
64 import java.io.PrintWriter;
65 import java.net.Inet4Address;
66 import java.net.Inet6Address;
67 import java.net.InetAddress;
68 import java.net.NetworkInterface;
69 import java.net.SocketException;
70 import java.util.ArrayList;
71 import java.util.Collection;
72 import java.util.HashSet;
73 import java.util.Objects;
74 import java.util.List;
75 import java.util.Set;
76 import java.util.StringJoiner;
77 import java.util.function.Predicate;
78 import java.util.stream.Collectors;
79 
80 
81 /**
82  * IpManager
83  *
84  * This class provides the interface to IP-layer provisioning and maintenance
85  * functionality that can be used by transport layers like Wi-Fi, Ethernet,
86  * et cetera.
87  *
88  * [ Lifetime ]
89  * IpManager is designed to be instantiated as soon as the interface name is
90  * known and can be as long-lived as the class containing it (i.e. declaring
91  * it "private final" is okay).
92  *
93  * @hide
94  */
95 public class IpManager extends StateMachine {
96     private static final boolean DBG = false;
97     private static final boolean VDBG = false;
98 
99     // For message logging.
100     private static final Class[] sMessageClasses = { IpManager.class, DhcpClient.class };
101     private static final SparseArray<String> sWhatToString =
102             MessageUtils.findMessageNames(sMessageClasses);
103 
104     /**
105      * Callbacks for handling IpManager events.
106      */
107     public static class Callback {
108         // In order to receive onPreDhcpAction(), call #withPreDhcpAction()
109         // when constructing a ProvisioningConfiguration.
110         //
111         // Implementations of onPreDhcpAction() must call
112         // IpManager#completedPreDhcpAction() to indicate that DHCP is clear
113         // to proceed.
onPreDhcpAction()114         public void onPreDhcpAction() {}
onPostDhcpAction()115         public void onPostDhcpAction() {}
116 
117         // This is purely advisory and not an indication of provisioning
118         // success or failure.  This is only here for callers that want to
119         // expose DHCPv4 results to other APIs (e.g., WifiInfo#setInetAddress).
120         // DHCPv4 or static IPv4 configuration failure or success can be
121         // determined by whether or not the passed-in DhcpResults object is
122         // null or not.
onNewDhcpResults(DhcpResults dhcpResults)123         public void onNewDhcpResults(DhcpResults dhcpResults) {}
124 
onProvisioningSuccess(LinkProperties newLp)125         public void onProvisioningSuccess(LinkProperties newLp) {}
onProvisioningFailure(LinkProperties newLp)126         public void onProvisioningFailure(LinkProperties newLp) {}
127 
128         // Invoked on LinkProperties changes.
onLinkPropertiesChange(LinkProperties newLp)129         public void onLinkPropertiesChange(LinkProperties newLp) {}
130 
131         // Called when the internal IpReachabilityMonitor (if enabled) has
132         // detected the loss of a critical number of required neighbors.
onReachabilityLost(String logMsg)133         public void onReachabilityLost(String logMsg) {}
134 
135         // Called when the IpManager state machine terminates.
onQuit()136         public void onQuit() {}
137 
138         // Install an APF program to filter incoming packets.
installPacketFilter(byte[] filter)139         public void installPacketFilter(byte[] filter) {}
140 
141         // If multicast filtering cannot be accomplished with APF, this function will be called to
142         // actuate multicast filtering using another means.
setFallbackMulticastFilter(boolean enabled)143         public void setFallbackMulticastFilter(boolean enabled) {}
144 
145         // Enabled/disable Neighbor Discover offload functionality. This is
146         // called, for example, whenever 464xlat is being started or stopped.
setNeighborDiscoveryOffload(boolean enable)147         public void setNeighborDiscoveryOffload(boolean enable) {}
148     }
149 
150     public static class WaitForProvisioningCallback extends Callback {
151         private LinkProperties mCallbackLinkProperties;
152 
waitForProvisioning()153         public LinkProperties waitForProvisioning() {
154             synchronized (this) {
155                 try {
156                     wait();
157                 } catch (InterruptedException e) {}
158                 return mCallbackLinkProperties;
159             }
160         }
161 
162         @Override
onProvisioningSuccess(LinkProperties newLp)163         public void onProvisioningSuccess(LinkProperties newLp) {
164             synchronized (this) {
165                 mCallbackLinkProperties = newLp;
166                 notify();
167             }
168         }
169 
170         @Override
onProvisioningFailure(LinkProperties newLp)171         public void onProvisioningFailure(LinkProperties newLp) {
172             synchronized (this) {
173                 mCallbackLinkProperties = null;
174                 notify();
175             }
176         }
177     }
178 
179     // Use a wrapper class to log in order to ensure complete and detailed
180     // logging. This method is lighter weight than annotations/reflection
181     // and has the following benefits:
182     //
183     //     - No invoked method can be forgotten.
184     //       Any new method added to IpManager.Callback must be overridden
185     //       here or it will never be called.
186     //
187     //     - No invoking call site can be forgotten.
188     //       Centralized logging in this way means call sites don't need to
189     //       remember to log, and therefore no call site can be forgotten.
190     //
191     //     - No variation in log format among call sites.
192     //       Encourages logging of any available arguments, and all call sites
193     //       are necessarily logged identically.
194     //
195     // TODO: Find an lighter weight approach.
196     private class LoggingCallbackWrapper extends Callback {
197         private static final String PREFIX = "INVOKE ";
198         private Callback mCallback;
199 
LoggingCallbackWrapper(Callback callback)200         public LoggingCallbackWrapper(Callback callback) {
201             mCallback = callback;
202         }
203 
log(String msg)204         private void log(String msg) {
205             mLog.log(PREFIX + msg);
206         }
207 
208         @Override
onPreDhcpAction()209         public void onPreDhcpAction() {
210             mCallback.onPreDhcpAction();
211             log("onPreDhcpAction()");
212         }
213         @Override
onPostDhcpAction()214         public void onPostDhcpAction() {
215             mCallback.onPostDhcpAction();
216             log("onPostDhcpAction()");
217         }
218         @Override
onNewDhcpResults(DhcpResults dhcpResults)219         public void onNewDhcpResults(DhcpResults dhcpResults) {
220             mCallback.onNewDhcpResults(dhcpResults);
221             log("onNewDhcpResults({" + dhcpResults + "})");
222         }
223         @Override
onProvisioningSuccess(LinkProperties newLp)224         public void onProvisioningSuccess(LinkProperties newLp) {
225             mCallback.onProvisioningSuccess(newLp);
226             log("onProvisioningSuccess({" + newLp + "})");
227         }
228         @Override
onProvisioningFailure(LinkProperties newLp)229         public void onProvisioningFailure(LinkProperties newLp) {
230             mCallback.onProvisioningFailure(newLp);
231             log("onProvisioningFailure({" + newLp + "})");
232         }
233         @Override
onLinkPropertiesChange(LinkProperties newLp)234         public void onLinkPropertiesChange(LinkProperties newLp) {
235             mCallback.onLinkPropertiesChange(newLp);
236             log("onLinkPropertiesChange({" + newLp + "})");
237         }
238         @Override
onReachabilityLost(String logMsg)239         public void onReachabilityLost(String logMsg) {
240             mCallback.onReachabilityLost(logMsg);
241             log("onReachabilityLost(" + logMsg + ")");
242         }
243         @Override
onQuit()244         public void onQuit() {
245             mCallback.onQuit();
246             log("onQuit()");
247         }
248         @Override
installPacketFilter(byte[] filter)249         public void installPacketFilter(byte[] filter) {
250             mCallback.installPacketFilter(filter);
251             log("installPacketFilter(byte[" + filter.length + "])");
252         }
253         @Override
setFallbackMulticastFilter(boolean enabled)254         public void setFallbackMulticastFilter(boolean enabled) {
255             mCallback.setFallbackMulticastFilter(enabled);
256             log("setFallbackMulticastFilter(" + enabled + ")");
257         }
258         @Override
setNeighborDiscoveryOffload(boolean enable)259         public void setNeighborDiscoveryOffload(boolean enable) {
260             mCallback.setNeighborDiscoveryOffload(enable);
261             log("setNeighborDiscoveryOffload(" + enable + ")");
262         }
263     }
264 
265     /**
266      * This class encapsulates parameters to be passed to
267      * IpManager#startProvisioning(). A defensive copy is made by IpManager
268      * and the values specified herein are in force until IpManager#stop()
269      * is called.
270      *
271      * Example use:
272      *
273      *     final ProvisioningConfiguration config =
274      *             mIpManager.buildProvisioningConfiguration()
275      *                     .withPreDhcpAction()
276      *                     .withProvisioningTimeoutMs(36 * 1000)
277      *                     .build();
278      *     mIpManager.startProvisioning(config);
279      *     ...
280      *     mIpManager.stop();
281      *
282      * The specified provisioning configuration will only be active until
283      * IpManager#stop() is called. Future calls to IpManager#startProvisioning()
284      * must specify the configuration again.
285      */
286     public static class ProvisioningConfiguration {
287         // TODO: Delete this default timeout once those callers that care are
288         // fixed to pass in their preferred timeout.
289         //
290         // We pick 36 seconds so we can send DHCP requests at
291         //
292         //     t=0, t=2, t=6, t=14, t=30
293         //
294         // allowing for 10% jitter.
295         private static final int DEFAULT_TIMEOUT_MS = 36 * 1000;
296 
297         public static class Builder {
298             private ProvisioningConfiguration mConfig = new ProvisioningConfiguration();
299 
withoutIPv4()300             public Builder withoutIPv4() {
301                 mConfig.mEnableIPv4 = false;
302                 return this;
303             }
304 
withoutIPv6()305             public Builder withoutIPv6() {
306                 mConfig.mEnableIPv6 = false;
307                 return this;
308             }
309 
withoutIpReachabilityMonitor()310             public Builder withoutIpReachabilityMonitor() {
311                 mConfig.mUsingIpReachabilityMonitor = false;
312                 return this;
313             }
314 
withPreDhcpAction()315             public Builder withPreDhcpAction() {
316                 mConfig.mRequestedPreDhcpActionMs = DEFAULT_TIMEOUT_MS;
317                 return this;
318             }
319 
withPreDhcpAction(int dhcpActionTimeoutMs)320             public Builder withPreDhcpAction(int dhcpActionTimeoutMs) {
321                 mConfig.mRequestedPreDhcpActionMs = dhcpActionTimeoutMs;
322                 return this;
323             }
324 
withInitialConfiguration(InitialConfiguration initialConfig)325             public Builder withInitialConfiguration(InitialConfiguration initialConfig) {
326                 mConfig.mInitialConfig = initialConfig;
327                 return this;
328             }
329 
withStaticConfiguration(StaticIpConfiguration staticConfig)330             public Builder withStaticConfiguration(StaticIpConfiguration staticConfig) {
331                 mConfig.mStaticIpConfig = staticConfig;
332                 return this;
333             }
334 
withApfCapabilities(ApfCapabilities apfCapabilities)335             public Builder withApfCapabilities(ApfCapabilities apfCapabilities) {
336                 mConfig.mApfCapabilities = apfCapabilities;
337                 return this;
338             }
339 
withProvisioningTimeoutMs(int timeoutMs)340             public Builder withProvisioningTimeoutMs(int timeoutMs) {
341                 mConfig.mProvisioningTimeoutMs = timeoutMs;
342                 return this;
343             }
344 
withIPv6AddrGenModeEUI64()345             public Builder withIPv6AddrGenModeEUI64() {
346                 mConfig.mIPv6AddrGenMode = INetd.IPV6_ADDR_GEN_MODE_EUI64;
347                 return this;
348             }
349 
withIPv6AddrGenModeStablePrivacy()350             public Builder withIPv6AddrGenModeStablePrivacy() {
351                 mConfig.mIPv6AddrGenMode = INetd.IPV6_ADDR_GEN_MODE_STABLE_PRIVACY;
352                 return this;
353             }
354 
build()355             public ProvisioningConfiguration build() {
356                 return new ProvisioningConfiguration(mConfig);
357             }
358         }
359 
360         /* package */ boolean mEnableIPv4 = true;
361         /* package */ boolean mEnableIPv6 = true;
362         /* package */ boolean mUsingIpReachabilityMonitor = true;
363         /* package */ int mRequestedPreDhcpActionMs;
364         /* package */ InitialConfiguration mInitialConfig;
365         /* package */ StaticIpConfiguration mStaticIpConfig;
366         /* package */ ApfCapabilities mApfCapabilities;
367         /* package */ int mProvisioningTimeoutMs = DEFAULT_TIMEOUT_MS;
368         /* package */ int mIPv6AddrGenMode = INetd.IPV6_ADDR_GEN_MODE_STABLE_PRIVACY;
369 
ProvisioningConfiguration()370         public ProvisioningConfiguration() {} // used by Builder
371 
ProvisioningConfiguration(ProvisioningConfiguration other)372         public ProvisioningConfiguration(ProvisioningConfiguration other) {
373             mEnableIPv4 = other.mEnableIPv4;
374             mEnableIPv6 = other.mEnableIPv6;
375             mUsingIpReachabilityMonitor = other.mUsingIpReachabilityMonitor;
376             mRequestedPreDhcpActionMs = other.mRequestedPreDhcpActionMs;
377             mInitialConfig = InitialConfiguration.copy(other.mInitialConfig);
378             mStaticIpConfig = other.mStaticIpConfig;
379             mApfCapabilities = other.mApfCapabilities;
380             mProvisioningTimeoutMs = other.mProvisioningTimeoutMs;
381         }
382 
383         @Override
toString()384         public String toString() {
385             return new StringJoiner(", ", getClass().getSimpleName() + "{", "}")
386                     .add("mEnableIPv4: " + mEnableIPv4)
387                     .add("mEnableIPv6: " + mEnableIPv6)
388                     .add("mUsingIpReachabilityMonitor: " + mUsingIpReachabilityMonitor)
389                     .add("mRequestedPreDhcpActionMs: " + mRequestedPreDhcpActionMs)
390                     .add("mInitialConfig: " + mInitialConfig)
391                     .add("mStaticIpConfig: " + mStaticIpConfig)
392                     .add("mApfCapabilities: " + mApfCapabilities)
393                     .add("mProvisioningTimeoutMs: " + mProvisioningTimeoutMs)
394                     .add("mIPv6AddrGenMode: " + mIPv6AddrGenMode)
395                     .toString();
396         }
397 
isValid()398         public boolean isValid() {
399             return (mInitialConfig == null) || mInitialConfig.isValid();
400         }
401     }
402 
403     public static class InitialConfiguration {
404         public final Set<LinkAddress> ipAddresses = new HashSet<>();
405         public final Set<IpPrefix> directlyConnectedRoutes = new HashSet<>();
406         public final Set<InetAddress> dnsServers = new HashSet<>();
407         public Inet4Address gateway; // WiFi legacy behavior with static ipv4 config
408 
copy(InitialConfiguration config)409         public static InitialConfiguration copy(InitialConfiguration config) {
410             if (config == null) {
411                 return null;
412             }
413             InitialConfiguration configCopy = new InitialConfiguration();
414             configCopy.ipAddresses.addAll(config.ipAddresses);
415             configCopy.directlyConnectedRoutes.addAll(config.directlyConnectedRoutes);
416             configCopy.dnsServers.addAll(config.dnsServers);
417             return configCopy;
418         }
419 
420         @Override
toString()421         public String toString() {
422             return String.format(
423                     "InitialConfiguration(IPs: {%s}, prefixes: {%s}, DNS: {%s}, v4 gateway: %s)",
424                     join(", ", ipAddresses), join(", ", directlyConnectedRoutes),
425                     join(", ", dnsServers), gateway);
426         }
427 
isValid()428         public boolean isValid() {
429             if (ipAddresses.isEmpty()) {
430                 return false;
431             }
432 
433             // For every IP address, there must be at least one prefix containing that address.
434             for (LinkAddress addr : ipAddresses) {
435                 if (!any(directlyConnectedRoutes, (p) -> p.contains(addr.getAddress()))) {
436                     return false;
437                 }
438             }
439             // For every dns server, there must be at least one prefix containing that address.
440             for (InetAddress addr : dnsServers) {
441                 if (!any(directlyConnectedRoutes, (p) -> p.contains(addr))) {
442                     return false;
443                 }
444             }
445             // All IPv6 LinkAddresses have an RFC7421-suitable prefix length
446             // (read: compliant with RFC4291#section2.5.4).
447             if (any(ipAddresses, not(InitialConfiguration::isPrefixLengthCompliant))) {
448                 return false;
449             }
450             // If directlyConnectedRoutes contains an IPv6 default route
451             // then ipAddresses MUST contain at least one non-ULA GUA.
452             if (any(directlyConnectedRoutes, InitialConfiguration::isIPv6DefaultRoute)
453                     && all(ipAddresses, not(InitialConfiguration::isIPv6GUA))) {
454                 return false;
455             }
456             // The prefix length of routes in directlyConnectedRoutes be within reasonable
457             // bounds for IPv6: /48-/64 just as we’d accept in RIOs.
458             if (any(directlyConnectedRoutes, not(InitialConfiguration::isPrefixLengthCompliant))) {
459                 return false;
460             }
461             // There no more than one IPv4 address
462             if (ipAddresses.stream().filter(Inet4Address.class::isInstance).count() > 1) {
463                 return false;
464             }
465 
466             return true;
467         }
468 
469         /**
470          * @return true if the given list of addressess and routes satisfies provisioning for this
471          * InitialConfiguration. LinkAddresses and RouteInfo objects are not compared with equality
472          * because addresses and routes seen by Netlink will contain additional fields like flags,
473          * interfaces, and so on. If this InitialConfiguration has no IP address specified, the
474          * provisioning check always fails.
475          *
476          * If the given list of routes is null, only addresses are taken into considerations.
477          */
isProvisionedBy(List<LinkAddress> addresses, List<RouteInfo> routes)478         public boolean isProvisionedBy(List<LinkAddress> addresses, List<RouteInfo> routes) {
479             if (ipAddresses.isEmpty()) {
480                 return false;
481             }
482 
483             for (LinkAddress addr : ipAddresses) {
484                 if (!any(addresses, (addrSeen) -> addr.isSameAddressAs(addrSeen))) {
485                     return false;
486                 }
487             }
488 
489             if (routes != null) {
490                 for (IpPrefix prefix : directlyConnectedRoutes) {
491                     if (!any(routes, (routeSeen) -> isDirectlyConnectedRoute(routeSeen, prefix))) {
492                         return false;
493                     }
494                 }
495             }
496 
497             return true;
498         }
499 
isDirectlyConnectedRoute(RouteInfo route, IpPrefix prefix)500         private static boolean isDirectlyConnectedRoute(RouteInfo route, IpPrefix prefix) {
501             return !route.hasGateway() && prefix.equals(route.getDestination());
502         }
503 
isPrefixLengthCompliant(LinkAddress addr)504         private static boolean isPrefixLengthCompliant(LinkAddress addr) {
505             return addr.isIPv4() || isCompliantIPv6PrefixLength(addr.getPrefixLength());
506         }
507 
isPrefixLengthCompliant(IpPrefix prefix)508         private static boolean isPrefixLengthCompliant(IpPrefix prefix) {
509             return prefix.isIPv4() || isCompliantIPv6PrefixLength(prefix.getPrefixLength());
510         }
511 
isCompliantIPv6PrefixLength(int prefixLength)512         private static boolean isCompliantIPv6PrefixLength(int prefixLength) {
513             return (NetworkConstants.RFC6177_MIN_PREFIX_LENGTH <= prefixLength)
514                     && (prefixLength <= NetworkConstants.RFC7421_PREFIX_LENGTH);
515         }
516 
isIPv6DefaultRoute(IpPrefix prefix)517         private static boolean isIPv6DefaultRoute(IpPrefix prefix) {
518             return prefix.getAddress().equals(Inet6Address.ANY);
519         }
520 
isIPv6GUA(LinkAddress addr)521         private static boolean isIPv6GUA(LinkAddress addr) {
522             return addr.isIPv6() && addr.isGlobalPreferred();
523         }
524     }
525 
526     public static final String DUMP_ARG = "ipmanager";
527     public static final String DUMP_ARG_CONFIRM = "confirm";
528 
529     private static final int CMD_STOP = 1;
530     private static final int CMD_START = 2;
531     private static final int CMD_CONFIRM = 3;
532     private static final int EVENT_PRE_DHCP_ACTION_COMPLETE = 4;
533     // Sent by NetlinkTracker to communicate netlink events.
534     private static final int EVENT_NETLINK_LINKPROPERTIES_CHANGED = 5;
535     private static final int CMD_UPDATE_TCP_BUFFER_SIZES = 6;
536     private static final int CMD_UPDATE_HTTP_PROXY = 7;
537     private static final int CMD_SET_MULTICAST_FILTER = 8;
538     private static final int EVENT_PROVISIONING_TIMEOUT = 9;
539     private static final int EVENT_DHCPACTION_TIMEOUT = 10;
540 
541     private static final int MAX_LOG_RECORDS = 500;
542     private static final int MAX_PACKET_RECORDS = 100;
543 
544     private static final boolean NO_CALLBACKS = false;
545     private static final boolean SEND_CALLBACKS = true;
546 
547     // This must match the interface prefix in clatd.c.
548     // TODO: Revert this hack once IpManager and Nat464Xlat work in concert.
549     private static final String CLAT_PREFIX = "v4-";
550 
551     private final State mStoppedState = new StoppedState();
552     private final State mStoppingState = new StoppingState();
553     private final State mStartedState = new StartedState();
554     private final State mRunningState = new RunningState();
555 
556     private final String mTag;
557     private final Context mContext;
558     private final String mInterfaceName;
559     private final String mClatInterfaceName;
560     @VisibleForTesting
561     protected final Callback mCallback;
562     private final INetworkManagementService mNwService;
563     private final NetlinkTracker mNetlinkTracker;
564     private final WakeupMessage mProvisioningTimeoutAlarm;
565     private final WakeupMessage mDhcpActionTimeoutAlarm;
566     private final MultinetworkPolicyTracker mMultinetworkPolicyTracker;
567     private final SharedLog mLog;
568     private final LocalLog mConnectivityPacketLog;
569     private final MessageHandlingLogger mMsgStateLogger;
570     private final IpConnectivityLog mMetricsLog = new IpConnectivityLog();
571     private final INetd mNetd;
572 
573     private NetworkInterface mNetworkInterface;
574 
575     /**
576      * Non-final member variables accessed only from within our StateMachine.
577      */
578     private LinkProperties mLinkProperties;
579     private ProvisioningConfiguration mConfiguration;
580     private IpReachabilityMonitor mIpReachabilityMonitor;
581     private DhcpClient mDhcpClient;
582     private DhcpResults mDhcpResults;
583     private String mTcpBufferSizes;
584     private ProxyInfo mHttpProxy;
585     private ApfFilter mApfFilter;
586     private boolean mMulticastFiltering;
587     private long mStartTimeMillis;
588 
IpManager(Context context, String ifName, Callback callback)589     public IpManager(Context context, String ifName, Callback callback) {
590         this(context, ifName, callback, INetworkManagementService.Stub.asInterface(
591                 ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE)),
592                 NetdService.getInstance());
593     }
594 
595     /**
596      * An expanded constructor, useful for dependency injection.
597      * TODO: migrate all test users to mock IpManager directly and remove this ctor.
598      */
IpManager(Context context, String ifName, Callback callback, INetworkManagementService nwService)599     public IpManager(Context context, String ifName, Callback callback,
600             INetworkManagementService nwService) {
601         this(context, ifName, callback, nwService, NetdService.getInstance());
602     }
603 
604     @VisibleForTesting
IpManager(Context context, String ifName, Callback callback, INetworkManagementService nwService, INetd netd)605     IpManager(Context context, String ifName, Callback callback,
606             INetworkManagementService nwService, INetd netd) {
607         super(IpManager.class.getSimpleName() + "." + ifName);
608         mTag = getName();
609 
610         mContext = context;
611         mInterfaceName = ifName;
612         mClatInterfaceName = CLAT_PREFIX + ifName;
613         mCallback = new LoggingCallbackWrapper(callback);
614         mNwService = nwService;
615         mNetd = netd;
616 
617         mLog = new SharedLog(MAX_LOG_RECORDS, mTag);
618         mConnectivityPacketLog = new LocalLog(MAX_PACKET_RECORDS);
619         mMsgStateLogger = new MessageHandlingLogger();
620 
621         mNetlinkTracker = new NetlinkTracker(
622                 mInterfaceName,
623                 new NetlinkTracker.Callback() {
624                     @Override
625                     public void update() {
626                         sendMessage(EVENT_NETLINK_LINKPROPERTIES_CHANGED);
627                     }
628                 }) {
629             @Override
630             public void interfaceAdded(String iface) {
631                 super.interfaceAdded(iface);
632                 if (mClatInterfaceName.equals(iface)) {
633                     mCallback.setNeighborDiscoveryOffload(false);
634                 } else if (!mInterfaceName.equals(iface)) {
635                     return;
636                 }
637 
638                 final String msg = "interfaceAdded(" + iface +")";
639                 logMsg(msg);
640             }
641 
642             @Override
643             public void interfaceRemoved(String iface) {
644                 super.interfaceRemoved(iface);
645                 // TODO: Also observe mInterfaceName going down and take some
646                 // kind of appropriate action.
647                 if (mClatInterfaceName.equals(iface)) {
648                     // TODO: consider sending a message to the IpManager main
649                     // StateMachine thread, in case "NDO enabled" state becomes
650                     // tied to more things that 464xlat operation.
651                     mCallback.setNeighborDiscoveryOffload(true);
652                 } else if (!mInterfaceName.equals(iface)) {
653                     return;
654                 }
655 
656                 final String msg = "interfaceRemoved(" + iface +")";
657                 logMsg(msg);
658             }
659 
660             private void logMsg(String msg) {
661                 Log.d(mTag, msg);
662                 getHandler().post(() -> { mLog.log("OBSERVED " + msg); });
663             }
664         };
665 
666         mLinkProperties = new LinkProperties();
667         mLinkProperties.setInterfaceName(mInterfaceName);
668 
669         mMultinetworkPolicyTracker = new MultinetworkPolicyTracker(mContext, getHandler(),
670                 () -> { mLog.log("OBSERVED AvoidBadWifi changed"); });
671 
672         mProvisioningTimeoutAlarm = new WakeupMessage(mContext, getHandler(),
673                 mTag + ".EVENT_PROVISIONING_TIMEOUT", EVENT_PROVISIONING_TIMEOUT);
674         mDhcpActionTimeoutAlarm = new WakeupMessage(mContext, getHandler(),
675                 mTag + ".EVENT_DHCPACTION_TIMEOUT", EVENT_DHCPACTION_TIMEOUT);
676 
677         // Anything the StateMachine may access must have been instantiated
678         // before this point.
679         configureAndStartStateMachine();
680 
681         // Anything that may send messages to the StateMachine must only be
682         // configured to do so after the StateMachine has started (above).
683         startStateMachineUpdaters();
684     }
685 
configureAndStartStateMachine()686     private void configureAndStartStateMachine() {
687         addState(mStoppedState);
688         addState(mStartedState);
689             addState(mRunningState, mStartedState);
690         addState(mStoppingState);
691 
692         setInitialState(mStoppedState);
693 
694         super.start();
695     }
696 
startStateMachineUpdaters()697     private void startStateMachineUpdaters() {
698         try {
699             mNwService.registerObserver(mNetlinkTracker);
700         } catch (RemoteException e) {
701             logError("Couldn't register NetlinkTracker: %s", e);
702         }
703 
704         mMultinetworkPolicyTracker.start();
705     }
706 
707     @Override
onQuitting()708     protected void onQuitting() {
709         mCallback.onQuit();
710     }
711 
712     // Shut down this IpManager instance altogether.
shutdown()713     public void shutdown() {
714         stop();
715         mMultinetworkPolicyTracker.shutdown();
716         quit();
717     }
718 
buildProvisioningConfiguration()719     public static ProvisioningConfiguration.Builder buildProvisioningConfiguration() {
720         return new ProvisioningConfiguration.Builder();
721     }
722 
startProvisioning(ProvisioningConfiguration req)723     public void startProvisioning(ProvisioningConfiguration req) {
724         if (!req.isValid()) {
725             doImmediateProvisioningFailure(IpManagerEvent.ERROR_INVALID_PROVISIONING);
726             return;
727         }
728 
729         getNetworkInterface();
730 
731         mCallback.setNeighborDiscoveryOffload(true);
732         sendMessage(CMD_START, new ProvisioningConfiguration(req));
733     }
734 
735     // TODO: Delete this.
startProvisioning(StaticIpConfiguration staticIpConfig)736     public void startProvisioning(StaticIpConfiguration staticIpConfig) {
737         startProvisioning(buildProvisioningConfiguration()
738                 .withStaticConfiguration(staticIpConfig)
739                 .build());
740     }
741 
startProvisioning()742     public void startProvisioning() {
743         startProvisioning(new ProvisioningConfiguration());
744     }
745 
stop()746     public void stop() {
747         sendMessage(CMD_STOP);
748     }
749 
confirmConfiguration()750     public void confirmConfiguration() {
751         sendMessage(CMD_CONFIRM);
752     }
753 
completedPreDhcpAction()754     public void completedPreDhcpAction() {
755         sendMessage(EVENT_PRE_DHCP_ACTION_COMPLETE);
756     }
757 
758     /**
759      * Set the TCP buffer sizes to use.
760      *
761      * This may be called, repeatedly, at any time before or after a call to
762      * #startProvisioning(). The setting is cleared upon calling #stop().
763      */
setTcpBufferSizes(String tcpBufferSizes)764     public void setTcpBufferSizes(String tcpBufferSizes) {
765         sendMessage(CMD_UPDATE_TCP_BUFFER_SIZES, tcpBufferSizes);
766     }
767 
768     /**
769      * Set the HTTP Proxy configuration to use.
770      *
771      * This may be called, repeatedly, at any time before or after a call to
772      * #startProvisioning(). The setting is cleared upon calling #stop().
773      */
setHttpProxy(ProxyInfo proxyInfo)774     public void setHttpProxy(ProxyInfo proxyInfo) {
775         sendMessage(CMD_UPDATE_HTTP_PROXY, proxyInfo);
776     }
777 
778     /**
779      * Enable or disable the multicast filter.  Attempts to use APF to accomplish the filtering,
780      * if not, Callback.setFallbackMulticastFilter() is called.
781      */
setMulticastFilter(boolean enabled)782     public void setMulticastFilter(boolean enabled) {
783         sendMessage(CMD_SET_MULTICAST_FILTER, enabled);
784     }
785 
dump(FileDescriptor fd, PrintWriter writer, String[] args)786     public void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
787         if (args != null && args.length > 0 && DUMP_ARG_CONFIRM.equals(args[0])) {
788             // Execute confirmConfiguration() and take no further action.
789             confirmConfiguration();
790             return;
791         }
792 
793         // Thread-unsafe access to mApfFilter but just used for debugging.
794         final ApfFilter apfFilter = mApfFilter;
795         final ProvisioningConfiguration provisioningConfig = mConfiguration;
796         final ApfCapabilities apfCapabilities = (provisioningConfig != null)
797                 ? provisioningConfig.mApfCapabilities : null;
798 
799         IndentingPrintWriter pw = new IndentingPrintWriter(writer, "  ");
800         pw.println(mTag + " APF dump:");
801         pw.increaseIndent();
802         if (apfFilter != null) {
803             apfFilter.dump(pw);
804         } else {
805             pw.print("No active ApfFilter; ");
806             if (provisioningConfig == null) {
807                 pw.println("IpManager not yet started.");
808             } else if (apfCapabilities == null || apfCapabilities.apfVersionSupported == 0) {
809                 pw.println("Hardware does not support APF.");
810             } else {
811                 pw.println("ApfFilter not yet started, APF capabilities: " + apfCapabilities);
812             }
813         }
814         pw.decreaseIndent();
815 
816         pw.println();
817         pw.println(mTag + " current ProvisioningConfiguration:");
818         pw.increaseIndent();
819         pw.println(Objects.toString(provisioningConfig, "N/A"));
820         pw.decreaseIndent();
821 
822         pw.println();
823         pw.println(mTag + " StateMachine dump:");
824         pw.increaseIndent();
825         mLog.dump(fd, pw, args);
826         pw.decreaseIndent();
827 
828         pw.println();
829         pw.println(mTag + " connectivity packet log:");
830         pw.println();
831         pw.println("Debug with python and scapy via:");
832         pw.println("shell$ python");
833         pw.println(">>> from scapy import all as scapy");
834         pw.println(">>> scapy.Ether(\"<paste_hex_string>\".decode(\"hex\")).show2()");
835         pw.println();
836 
837         pw.increaseIndent();
838         mConnectivityPacketLog.readOnlyLocalLog().dump(fd, pw, args);
839         pw.decreaseIndent();
840     }
841 
842 
843     /**
844      * Internals.
845      */
846 
847     @Override
getWhatToString(int what)848     protected String getWhatToString(int what) {
849         return sWhatToString.get(what, "UNKNOWN: " + Integer.toString(what));
850     }
851 
852     @Override
getLogRecString(Message msg)853     protected String getLogRecString(Message msg) {
854         final String logLine = String.format(
855                 "%s/%d %d %d %s [%s]",
856                 mInterfaceName, mNetworkInterface == null ? -1 : mNetworkInterface.getIndex(),
857                 msg.arg1, msg.arg2, Objects.toString(msg.obj), mMsgStateLogger);
858 
859         final String richerLogLine = getWhatToString(msg.what) + " " + logLine;
860         mLog.log(richerLogLine);
861         if (VDBG) {
862             Log.d(mTag, richerLogLine);
863         }
864 
865         mMsgStateLogger.reset();
866         return logLine;
867     }
868 
869     @Override
recordLogRec(Message msg)870     protected boolean recordLogRec(Message msg) {
871         // Don't log EVENT_NETLINK_LINKPROPERTIES_CHANGED. They can be noisy,
872         // and we already log any LinkProperties change that results in an
873         // invocation of IpManager.Callback#onLinkPropertiesChange().
874         final boolean shouldLog = (msg.what != EVENT_NETLINK_LINKPROPERTIES_CHANGED);
875         if (!shouldLog) {
876             mMsgStateLogger.reset();
877         }
878         return shouldLog;
879     }
880 
logError(String fmt, Object... args)881     private void logError(String fmt, Object... args) {
882         final String msg = "ERROR " + String.format(fmt, args);
883         Log.e(mTag, msg);
884         mLog.log(msg);
885     }
886 
getNetworkInterface()887     private void getNetworkInterface() {
888         try {
889             mNetworkInterface = NetworkInterface.getByName(mInterfaceName);
890         } catch (SocketException | NullPointerException e) {
891             // TODO: throw new IllegalStateException.
892             logError("Failed to get interface object: %s", e);
893         }
894     }
895 
896     // This needs to be called with care to ensure that our LinkProperties
897     // are in sync with the actual LinkProperties of the interface. For example,
898     // we should only call this if we know for sure that there are no IP addresses
899     // assigned to the interface, etc.
resetLinkProperties()900     private void resetLinkProperties() {
901         mNetlinkTracker.clearLinkProperties();
902         mConfiguration = null;
903         mDhcpResults = null;
904         mTcpBufferSizes = "";
905         mHttpProxy = null;
906 
907         mLinkProperties = new LinkProperties();
908         mLinkProperties.setInterfaceName(mInterfaceName);
909     }
910 
recordMetric(final int type)911     private void recordMetric(final int type) {
912         if (mStartTimeMillis <= 0) { Log.wtf(mTag, "Start time undefined!"); }
913         final long duration = SystemClock.elapsedRealtime() - mStartTimeMillis;
914         mMetricsLog.log(mInterfaceName, new IpManagerEvent(type, duration));
915     }
916 
917     // For now: use WifiStateMachine's historical notion of provisioned.
918     @VisibleForTesting
isProvisioned(LinkProperties lp, InitialConfiguration config)919     static boolean isProvisioned(LinkProperties lp, InitialConfiguration config) {
920         // For historical reasons, we should connect even if all we have is
921         // an IPv4 address and nothing else.
922         if (lp.hasIPv4Address() || lp.isProvisioned()) {
923             return true;
924         }
925         if (config == null) {
926             return false;
927         }
928 
929         // When an InitialConfiguration is specified, ignore any difference with previous
930         // properties and instead check if properties observed match the desired properties.
931         return config.isProvisionedBy(lp.getLinkAddresses(), lp.getRoutes());
932     }
933 
934     // TODO: Investigate folding all this into the existing static function
935     // LinkProperties.compareProvisioning() or some other single function that
936     // takes two LinkProperties objects and returns a ProvisioningChange
937     // object that is a correct and complete assessment of what changed, taking
938     // account of the asymmetries described in the comments in this function.
939     // Then switch to using it everywhere (IpReachabilityMonitor, etc.).
compareProvisioning(LinkProperties oldLp, LinkProperties newLp)940     private ProvisioningChange compareProvisioning(LinkProperties oldLp, LinkProperties newLp) {
941         ProvisioningChange delta;
942         InitialConfiguration config = mConfiguration != null ? mConfiguration.mInitialConfig : null;
943         final boolean wasProvisioned = isProvisioned(oldLp, config);
944         final boolean isProvisioned = isProvisioned(newLp, config);
945 
946         if (!wasProvisioned && isProvisioned) {
947             delta = ProvisioningChange.GAINED_PROVISIONING;
948         } else if (wasProvisioned && isProvisioned) {
949             delta = ProvisioningChange.STILL_PROVISIONED;
950         } else if (!wasProvisioned && !isProvisioned) {
951             delta = ProvisioningChange.STILL_NOT_PROVISIONED;
952         } else {
953             // (wasProvisioned && !isProvisioned)
954             //
955             // Note that this is true even if we lose a configuration element
956             // (e.g., a default gateway) that would not be required to advance
957             // into provisioned state. This is intended: if we have a default
958             // router and we lose it, that's a sure sign of a problem, but if
959             // we connect to a network with no IPv4 DNS servers, we consider
960             // that to be a network without DNS servers and connect anyway.
961             //
962             // See the comment below.
963             delta = ProvisioningChange.LOST_PROVISIONING;
964         }
965 
966         final boolean lostIPv6 = oldLp.isIPv6Provisioned() && !newLp.isIPv6Provisioned();
967         final boolean lostIPv4Address = oldLp.hasIPv4Address() && !newLp.hasIPv4Address();
968         final boolean lostIPv6Router = oldLp.hasIPv6DefaultRoute() && !newLp.hasIPv6DefaultRoute();
969 
970         // If bad wifi avoidance is disabled, then ignore IPv6 loss of
971         // provisioning. Otherwise, when a hotspot that loses Internet
972         // access sends out a 0-lifetime RA to its clients, the clients
973         // will disconnect and then reconnect, avoiding the bad hotspot,
974         // instead of getting stuck on the bad hotspot. http://b/31827713 .
975         //
976         // This is incorrect because if the hotspot then regains Internet
977         // access with a different prefix, TCP connections on the
978         // deprecated addresses will remain stuck.
979         //
980         // Note that we can still be disconnected by IpReachabilityMonitor
981         // if the IPv6 default gateway (but not the IPv6 DNS servers; see
982         // accompanying code in IpReachabilityMonitor) is unreachable.
983         final boolean ignoreIPv6ProvisioningLoss = !mMultinetworkPolicyTracker.getAvoidBadWifi();
984 
985         // Additionally:
986         //
987         // Partial configurations (e.g., only an IPv4 address with no DNS
988         // servers and no default route) are accepted as long as DHCPv4
989         // succeeds. On such a network, isProvisioned() will always return
990         // false, because the configuration is not complete, but we want to
991         // connect anyway. It might be a disconnected network such as a
992         // Chromecast or a wireless printer, for example.
993         //
994         // Because on such a network isProvisioned() will always return false,
995         // delta will never be LOST_PROVISIONING. So check for loss of
996         // provisioning here too.
997         if (lostIPv4Address || (lostIPv6 && !ignoreIPv6ProvisioningLoss)) {
998             delta = ProvisioningChange.LOST_PROVISIONING;
999         }
1000 
1001         // Additionally:
1002         //
1003         // If the previous link properties had a global IPv6 address and an
1004         // IPv6 default route then also consider the loss of that default route
1005         // to be a loss of provisioning. See b/27962810.
1006         if (oldLp.hasGlobalIPv6Address() && (lostIPv6Router && !ignoreIPv6ProvisioningLoss)) {
1007             delta = ProvisioningChange.LOST_PROVISIONING;
1008         }
1009 
1010         return delta;
1011     }
1012 
dispatchCallback(ProvisioningChange delta, LinkProperties newLp)1013     private void dispatchCallback(ProvisioningChange delta, LinkProperties newLp) {
1014         switch (delta) {
1015             case GAINED_PROVISIONING:
1016                 if (VDBG) { Log.d(mTag, "onProvisioningSuccess()"); }
1017                 recordMetric(IpManagerEvent.PROVISIONING_OK);
1018                 mCallback.onProvisioningSuccess(newLp);
1019                 break;
1020 
1021             case LOST_PROVISIONING:
1022                 if (VDBG) { Log.d(mTag, "onProvisioningFailure()"); }
1023                 recordMetric(IpManagerEvent.PROVISIONING_FAIL);
1024                 mCallback.onProvisioningFailure(newLp);
1025                 break;
1026 
1027             default:
1028                 if (VDBG) { Log.d(mTag, "onLinkPropertiesChange()"); }
1029                 mCallback.onLinkPropertiesChange(newLp);
1030                 break;
1031         }
1032     }
1033 
1034     // Updates all IpManager-related state concerned with LinkProperties.
1035     // Returns a ProvisioningChange for possibly notifying other interested
1036     // parties that are not fronted by IpManager.
setLinkProperties(LinkProperties newLp)1037     private ProvisioningChange setLinkProperties(LinkProperties newLp) {
1038         if (mApfFilter != null) {
1039             mApfFilter.setLinkProperties(newLp);
1040         }
1041         if (mIpReachabilityMonitor != null) {
1042             mIpReachabilityMonitor.updateLinkProperties(newLp);
1043         }
1044 
1045         ProvisioningChange delta = compareProvisioning(mLinkProperties, newLp);
1046         mLinkProperties = new LinkProperties(newLp);
1047 
1048         if (delta == ProvisioningChange.GAINED_PROVISIONING) {
1049             // TODO: Add a proper ProvisionedState and cancel the alarm in
1050             // its enter() method.
1051             mProvisioningTimeoutAlarm.cancel();
1052         }
1053 
1054         return delta;
1055     }
1056 
assembleLinkProperties()1057     private LinkProperties assembleLinkProperties() {
1058         // [1] Create a new LinkProperties object to populate.
1059         LinkProperties newLp = new LinkProperties();
1060         newLp.setInterfaceName(mInterfaceName);
1061 
1062         // [2] Pull in data from netlink:
1063         //         - IPv4 addresses
1064         //         - IPv6 addresses
1065         //         - IPv6 routes
1066         //         - IPv6 DNS servers
1067         //
1068         // N.B.: this is fundamentally race-prone and should be fixed by
1069         // changing NetlinkTracker from a hybrid edge/level model to an
1070         // edge-only model, or by giving IpManager its own netlink socket(s)
1071         // so as to track all required information directly.
1072         LinkProperties netlinkLinkProperties = mNetlinkTracker.getLinkProperties();
1073         newLp.setLinkAddresses(netlinkLinkProperties.getLinkAddresses());
1074         for (RouteInfo route : netlinkLinkProperties.getRoutes()) {
1075             newLp.addRoute(route);
1076         }
1077         addAllReachableDnsServers(newLp, netlinkLinkProperties.getDnsServers());
1078 
1079         // [3] Add in data from DHCPv4, if available.
1080         //
1081         // mDhcpResults is never shared with any other owner so we don't have
1082         // to worry about concurrent modification.
1083         if (mDhcpResults != null) {
1084             for (RouteInfo route : mDhcpResults.getRoutes(mInterfaceName)) {
1085                 newLp.addRoute(route);
1086             }
1087             addAllReachableDnsServers(newLp, mDhcpResults.dnsServers);
1088             newLp.setDomains(mDhcpResults.domains);
1089 
1090             if (mDhcpResults.mtu != 0) {
1091                 newLp.setMtu(mDhcpResults.mtu);
1092             }
1093         }
1094 
1095         // [4] Add in TCP buffer sizes and HTTP Proxy config, if available.
1096         if (!TextUtils.isEmpty(mTcpBufferSizes)) {
1097             newLp.setTcpBufferSizes(mTcpBufferSizes);
1098         }
1099         if (mHttpProxy != null) {
1100             newLp.setHttpProxy(mHttpProxy);
1101         }
1102 
1103         // [5] Add data from InitialConfiguration
1104         if (mConfiguration != null && mConfiguration.mInitialConfig != null) {
1105             InitialConfiguration config = mConfiguration.mInitialConfig;
1106             // Add InitialConfiguration routes and dns server addresses once all addresses
1107             // specified in the InitialConfiguration have been observed with Netlink.
1108             if (config.isProvisionedBy(newLp.getLinkAddresses(), null)) {
1109                 for (IpPrefix prefix : config.directlyConnectedRoutes) {
1110                     newLp.addRoute(new RouteInfo(prefix, null, mInterfaceName));
1111                 }
1112             }
1113             addAllReachableDnsServers(newLp, config.dnsServers);
1114         }
1115         final LinkProperties oldLp = mLinkProperties;
1116         if (VDBG) {
1117             Log.d(mTag, String.format("Netlink-seen LPs: %s, new LPs: %s; old LPs: %s",
1118                     netlinkLinkProperties, newLp, oldLp));
1119         }
1120 
1121         // TODO: also learn via netlink routes specified by an InitialConfiguration and specified
1122         // from a static IP v4 config instead of manually patching them in in steps [3] and [5].
1123         return newLp;
1124     }
1125 
addAllReachableDnsServers( LinkProperties lp, Iterable<InetAddress> dnses)1126     private static void addAllReachableDnsServers(
1127             LinkProperties lp, Iterable<InetAddress> dnses) {
1128         // TODO: Investigate deleting this reachability check.  We should be
1129         // able to pass everything down to netd and let netd do evaluation
1130         // and RFC6724-style sorting.
1131         for (InetAddress dns : dnses) {
1132             if (!dns.isAnyLocalAddress() && lp.isReachable(dns)) {
1133                 lp.addDnsServer(dns);
1134             }
1135         }
1136     }
1137 
1138     // Returns false if we have lost provisioning, true otherwise.
handleLinkPropertiesUpdate(boolean sendCallbacks)1139     private boolean handleLinkPropertiesUpdate(boolean sendCallbacks) {
1140         final LinkProperties newLp = assembleLinkProperties();
1141         if (Objects.equals(newLp, mLinkProperties)) {
1142             return true;
1143         }
1144         final ProvisioningChange delta = setLinkProperties(newLp);
1145         if (sendCallbacks) {
1146             dispatchCallback(delta, newLp);
1147         }
1148         return (delta != ProvisioningChange.LOST_PROVISIONING);
1149     }
1150 
setIPv4Address(LinkAddress address)1151     private boolean setIPv4Address(LinkAddress address) {
1152         final InterfaceConfiguration ifcg = new InterfaceConfiguration();
1153         ifcg.setLinkAddress(address);
1154         try {
1155             mNwService.setInterfaceConfig(mInterfaceName, ifcg);
1156             if (VDBG) Log.d(mTag, "IPv4 configuration succeeded");
1157         } catch (IllegalStateException | RemoteException e) {
1158             logError("IPv4 configuration failed: %s", e);
1159             return false;
1160         }
1161         return true;
1162     }
1163 
clearIPv4Address()1164     private void clearIPv4Address() {
1165         try {
1166             final InterfaceConfiguration ifcg = new InterfaceConfiguration();
1167             ifcg.setLinkAddress(new LinkAddress("0.0.0.0/0"));
1168             mNwService.setInterfaceConfig(mInterfaceName, ifcg);
1169         } catch (IllegalStateException | RemoteException e) {
1170             logError("Failed to clear IPv4 address on interface %s: %s", mInterfaceName, e);
1171         }
1172     }
1173 
handleIPv4Success(DhcpResults dhcpResults)1174     private void handleIPv4Success(DhcpResults dhcpResults) {
1175         mDhcpResults = new DhcpResults(dhcpResults);
1176         final LinkProperties newLp = assembleLinkProperties();
1177         final ProvisioningChange delta = setLinkProperties(newLp);
1178 
1179         if (VDBG) {
1180             Log.d(mTag, "onNewDhcpResults(" + Objects.toString(dhcpResults) + ")");
1181         }
1182         mCallback.onNewDhcpResults(dhcpResults);
1183         dispatchCallback(delta, newLp);
1184     }
1185 
handleIPv4Failure()1186     private void handleIPv4Failure() {
1187         // TODO: Investigate deleting this clearIPv4Address() call.
1188         //
1189         // DhcpClient will send us CMD_CLEAR_LINKADDRESS in all circumstances
1190         // that could trigger a call to this function. If we missed handling
1191         // that message in StartedState for some reason we would still clear
1192         // any addresses upon entry to StoppedState.
1193         clearIPv4Address();
1194         mDhcpResults = null;
1195         if (VDBG) { Log.d(mTag, "onNewDhcpResults(null)"); }
1196         mCallback.onNewDhcpResults(null);
1197 
1198         handleProvisioningFailure();
1199     }
1200 
handleProvisioningFailure()1201     private void handleProvisioningFailure() {
1202         final LinkProperties newLp = assembleLinkProperties();
1203         ProvisioningChange delta = setLinkProperties(newLp);
1204         // If we've gotten here and we're still not provisioned treat that as
1205         // a total loss of provisioning.
1206         //
1207         // Either (a) static IP configuration failed or (b) DHCPv4 failed AND
1208         // there was no usable IPv6 obtained before a non-zero provisioning
1209         // timeout expired.
1210         //
1211         // Regardless: GAME OVER.
1212         if (delta == ProvisioningChange.STILL_NOT_PROVISIONED) {
1213             delta = ProvisioningChange.LOST_PROVISIONING;
1214         }
1215 
1216         dispatchCallback(delta, newLp);
1217         if (delta == ProvisioningChange.LOST_PROVISIONING) {
1218             transitionTo(mStoppingState);
1219         }
1220     }
1221 
doImmediateProvisioningFailure(int failureType)1222     private void doImmediateProvisioningFailure(int failureType) {
1223         logError("onProvisioningFailure(): %s", failureType);
1224         recordMetric(failureType);
1225         mCallback.onProvisioningFailure(new LinkProperties(mLinkProperties));
1226     }
1227 
startIPv4()1228     private boolean startIPv4() {
1229         // If we have a StaticIpConfiguration attempt to apply it and
1230         // handle the result accordingly.
1231         if (mConfiguration.mStaticIpConfig != null) {
1232             if (setIPv4Address(mConfiguration.mStaticIpConfig.ipAddress)) {
1233                 handleIPv4Success(new DhcpResults(mConfiguration.mStaticIpConfig));
1234             } else {
1235                 return false;
1236             }
1237         } else {
1238             // Start DHCPv4.
1239             mDhcpClient = DhcpClient.makeDhcpClient(mContext, IpManager.this, mInterfaceName);
1240             mDhcpClient.registerForPreDhcpNotification();
1241             mDhcpClient.sendMessage(DhcpClient.CMD_START_DHCP);
1242         }
1243 
1244         return true;
1245     }
1246 
setIPv6AddrGenModeIfSupported()1247     private void setIPv6AddrGenModeIfSupported() throws RemoteException {
1248         try {
1249             mNwService.setIPv6AddrGenMode(mInterfaceName, mConfiguration.mIPv6AddrGenMode);
1250         } catch (ServiceSpecificException e) {
1251             if (e.errorCode != OsConstants.EOPNOTSUPP) {
1252                 logError("Unable to set IPv6 addrgen mode: %s", e);
1253             }
1254         }
1255     }
1256 
startIPv6()1257     private boolean startIPv6() {
1258         // Set privacy extensions.
1259         try {
1260             mNwService.setInterfaceIpv6PrivacyExtensions(mInterfaceName, true);
1261 
1262             setIPv6AddrGenModeIfSupported();
1263             mNwService.enableIpv6(mInterfaceName);
1264         } catch (IllegalStateException | RemoteException | ServiceSpecificException e) {
1265             logError("Unable to change interface settings: %s", e);
1266             return false;
1267         }
1268 
1269         return true;
1270     }
1271 
applyInitialConfig(InitialConfiguration config)1272     private boolean applyInitialConfig(InitialConfiguration config) {
1273         if (mNetd == null) {
1274             logError("tried to add %s to %s but INetd was null", config, mInterfaceName);
1275             return false;
1276         }
1277 
1278         // TODO: also support specifying a static IPv4 configuration in InitialConfiguration.
1279         for (LinkAddress addr : findAll(config.ipAddresses, LinkAddress::isIPv6)) {
1280             try {
1281                 mNetd.interfaceAddAddress(
1282                         mInterfaceName, addr.getAddress().getHostAddress(), addr.getPrefixLength());
1283             } catch (ServiceSpecificException | RemoteException e) {
1284                 logError("failed to add %s to %s: %s", addr, mInterfaceName, e);
1285                 return false;
1286             }
1287         }
1288 
1289         return true;
1290     }
1291 
startIpReachabilityMonitor()1292     private boolean startIpReachabilityMonitor() {
1293         try {
1294             mIpReachabilityMonitor = new IpReachabilityMonitor(
1295                     mContext,
1296                     mInterfaceName,
1297                     mLog,
1298                     new IpReachabilityMonitor.Callback() {
1299                         @Override
1300                         public void notifyLost(InetAddress ip, String logMsg) {
1301                             mCallback.onReachabilityLost(logMsg);
1302                         }
1303                     },
1304                     mMultinetworkPolicyTracker);
1305         } catch (IllegalArgumentException iae) {
1306             // Failed to start IpReachabilityMonitor. Log it and call
1307             // onProvisioningFailure() immediately.
1308             //
1309             // See http://b/31038971.
1310             logError("IpReachabilityMonitor failure: %s", iae);
1311             mIpReachabilityMonitor = null;
1312         }
1313 
1314         return (mIpReachabilityMonitor != null);
1315     }
1316 
stopAllIP()1317     private void stopAllIP() {
1318         // We don't need to worry about routes, just addresses, because:
1319         //     - disableIpv6() will clear autoconf IPv6 routes as well, and
1320         //     - we don't get IPv4 routes from netlink
1321         // so we neither react to nor need to wait for changes in either.
1322 
1323         try {
1324             mNwService.disableIpv6(mInterfaceName);
1325         } catch (Exception e) {
1326             logError("Failed to disable IPv6: %s", e);
1327         }
1328 
1329         try {
1330             mNwService.clearInterfaceAddresses(mInterfaceName);
1331         } catch (Exception e) {
1332             logError("Failed to clear addresses: %s", e);
1333         }
1334     }
1335 
1336     class StoppedState extends State {
1337         @Override
enter()1338         public void enter() {
1339             stopAllIP();
1340 
1341             resetLinkProperties();
1342             if (mStartTimeMillis > 0) {
1343                 recordMetric(IpManagerEvent.COMPLETE_LIFECYCLE);
1344                 mStartTimeMillis = 0;
1345             }
1346         }
1347 
1348         @Override
processMessage(Message msg)1349         public boolean processMessage(Message msg) {
1350             switch (msg.what) {
1351                 case CMD_STOP:
1352                     break;
1353 
1354                 case CMD_START:
1355                     mConfiguration = (ProvisioningConfiguration) msg.obj;
1356                     transitionTo(mStartedState);
1357                     break;
1358 
1359                 case EVENT_NETLINK_LINKPROPERTIES_CHANGED:
1360                     handleLinkPropertiesUpdate(NO_CALLBACKS);
1361                     break;
1362 
1363                 case CMD_UPDATE_TCP_BUFFER_SIZES:
1364                     mTcpBufferSizes = (String) msg.obj;
1365                     handleLinkPropertiesUpdate(NO_CALLBACKS);
1366                     break;
1367 
1368                 case CMD_UPDATE_HTTP_PROXY:
1369                     mHttpProxy = (ProxyInfo) msg.obj;
1370                     handleLinkPropertiesUpdate(NO_CALLBACKS);
1371                     break;
1372 
1373                 case CMD_SET_MULTICAST_FILTER:
1374                     mMulticastFiltering = (boolean) msg.obj;
1375                     break;
1376 
1377                 case DhcpClient.CMD_ON_QUIT:
1378                     // Everything is already stopped.
1379                     logError("Unexpected CMD_ON_QUIT (already stopped).");
1380                     break;
1381 
1382                 default:
1383                     return NOT_HANDLED;
1384             }
1385 
1386             mMsgStateLogger.handled(this, getCurrentState());
1387             return HANDLED;
1388         }
1389     }
1390 
1391     class StoppingState extends State {
1392         @Override
enter()1393         public void enter() {
1394             if (mDhcpClient == null) {
1395                 // There's no DHCPv4 for which to wait; proceed to stopped.
1396                 transitionTo(mStoppedState);
1397             }
1398         }
1399 
1400         @Override
processMessage(Message msg)1401         public boolean processMessage(Message msg) {
1402             switch (msg.what) {
1403                 case CMD_STOP:
1404                     break;
1405 
1406                 case DhcpClient.CMD_CLEAR_LINKADDRESS:
1407                     clearIPv4Address();
1408                     break;
1409 
1410                 case DhcpClient.CMD_ON_QUIT:
1411                     mDhcpClient = null;
1412                     transitionTo(mStoppedState);
1413                     break;
1414 
1415                 default:
1416                     deferMessage(msg);
1417             }
1418 
1419             mMsgStateLogger.handled(this, getCurrentState());
1420             return HANDLED;
1421         }
1422     }
1423 
1424     class StartedState extends State {
1425         @Override
enter()1426         public void enter() {
1427             mStartTimeMillis = SystemClock.elapsedRealtime();
1428 
1429             if (mConfiguration.mProvisioningTimeoutMs > 0) {
1430                 final long alarmTime = SystemClock.elapsedRealtime() +
1431                         mConfiguration.mProvisioningTimeoutMs;
1432                 mProvisioningTimeoutAlarm.schedule(alarmTime);
1433             }
1434 
1435             if (readyToProceed()) {
1436                 transitionTo(mRunningState);
1437             } else {
1438                 // Clear all IPv4 and IPv6 before proceeding to RunningState.
1439                 // Clean up any leftover state from an abnormal exit from
1440                 // tethering or during an IpManager restart.
1441                 stopAllIP();
1442             }
1443         }
1444 
1445         @Override
exit()1446         public void exit() {
1447             mProvisioningTimeoutAlarm.cancel();
1448         }
1449 
1450         @Override
processMessage(Message msg)1451         public boolean processMessage(Message msg) {
1452             switch (msg.what) {
1453                 case CMD_STOP:
1454                     transitionTo(mStoppingState);
1455                     break;
1456 
1457                 case EVENT_NETLINK_LINKPROPERTIES_CHANGED:
1458                     handleLinkPropertiesUpdate(NO_CALLBACKS);
1459                     if (readyToProceed()) {
1460                         transitionTo(mRunningState);
1461                     }
1462                     break;
1463 
1464                 case EVENT_PROVISIONING_TIMEOUT:
1465                     handleProvisioningFailure();
1466                     break;
1467 
1468                 default:
1469                     // It's safe to process messages out of order because the
1470                     // only message that can both
1471                     //     a) be received at this time and
1472                     //     b) affect provisioning state
1473                     // is EVENT_NETLINK_LINKPROPERTIES_CHANGED (handled above).
1474                     deferMessage(msg);
1475             }
1476 
1477             mMsgStateLogger.handled(this, getCurrentState());
1478             return HANDLED;
1479         }
1480 
readyToProceed()1481         boolean readyToProceed() {
1482             return (!mLinkProperties.hasIPv4Address() &&
1483                     !mLinkProperties.hasGlobalIPv6Address());
1484         }
1485     }
1486 
1487     class RunningState extends State {
1488         private ConnectivityPacketTracker mPacketTracker;
1489         private boolean mDhcpActionInFlight;
1490 
1491         @Override
enter()1492         public void enter() {
1493             // Get the Configuration for ApfFilter from Context
1494             boolean filter802_3Frames =
1495                     mContext.getResources().getBoolean(R.bool.config_apfDrop802_3Frames);
1496 
1497             mApfFilter = ApfFilter.maybeCreate(mConfiguration.mApfCapabilities, mNetworkInterface,
1498                     mCallback, mMulticastFiltering, filter802_3Frames);
1499             // TODO: investigate the effects of any multicast filtering racing/interfering with the
1500             // rest of this IP configuration startup.
1501             if (mApfFilter == null) {
1502                 mCallback.setFallbackMulticastFilter(mMulticastFiltering);
1503             }
1504 
1505             mPacketTracker = createPacketTracker();
1506             if (mPacketTracker != null) mPacketTracker.start();
1507 
1508             if (mConfiguration.mEnableIPv6 && !startIPv6()) {
1509                 doImmediateProvisioningFailure(IpManagerEvent.ERROR_STARTING_IPV6);
1510                 transitionTo(mStoppingState);
1511                 return;
1512             }
1513 
1514             if (mConfiguration.mEnableIPv4 && !startIPv4()) {
1515                 doImmediateProvisioningFailure(IpManagerEvent.ERROR_STARTING_IPV4);
1516                 transitionTo(mStoppingState);
1517                 return;
1518             }
1519 
1520             InitialConfiguration config = mConfiguration.mInitialConfig;
1521             if ((config != null) && !applyInitialConfig(config)) {
1522                 // TODO introduce a new IpManagerEvent constant to distinguish this error case.
1523                 doImmediateProvisioningFailure(IpManagerEvent.ERROR_INVALID_PROVISIONING);
1524                 transitionTo(mStoppingState);
1525                 return;
1526             }
1527 
1528             if (mConfiguration.mUsingIpReachabilityMonitor && !startIpReachabilityMonitor()) {
1529                 doImmediateProvisioningFailure(
1530                         IpManagerEvent.ERROR_STARTING_IPREACHABILITYMONITOR);
1531                 transitionTo(mStoppingState);
1532                 return;
1533             }
1534         }
1535 
1536         @Override
exit()1537         public void exit() {
1538             stopDhcpAction();
1539 
1540             if (mIpReachabilityMonitor != null) {
1541                 mIpReachabilityMonitor.stop();
1542                 mIpReachabilityMonitor = null;
1543             }
1544 
1545             if (mDhcpClient != null) {
1546                 mDhcpClient.sendMessage(DhcpClient.CMD_STOP_DHCP);
1547                 mDhcpClient.doQuit();
1548             }
1549 
1550             if (mPacketTracker != null) {
1551                 mPacketTracker.stop();
1552                 mPacketTracker = null;
1553             }
1554 
1555             if (mApfFilter != null) {
1556                 mApfFilter.shutdown();
1557                 mApfFilter = null;
1558             }
1559 
1560             resetLinkProperties();
1561         }
1562 
createPacketTracker()1563         private ConnectivityPacketTracker createPacketTracker() {
1564             try {
1565                 return new ConnectivityPacketTracker(mNetworkInterface, mConnectivityPacketLog);
1566             } catch (IllegalArgumentException e) {
1567                 return null;
1568             }
1569         }
1570 
ensureDhcpAction()1571         private void ensureDhcpAction() {
1572             if (!mDhcpActionInFlight) {
1573                 mCallback.onPreDhcpAction();
1574                 mDhcpActionInFlight = true;
1575                 final long alarmTime = SystemClock.elapsedRealtime() +
1576                         mConfiguration.mRequestedPreDhcpActionMs;
1577                 mDhcpActionTimeoutAlarm.schedule(alarmTime);
1578             }
1579         }
1580 
stopDhcpAction()1581         private void stopDhcpAction() {
1582             mDhcpActionTimeoutAlarm.cancel();
1583             if (mDhcpActionInFlight) {
1584                 mCallback.onPostDhcpAction();
1585                 mDhcpActionInFlight = false;
1586             }
1587         }
1588 
1589         @Override
processMessage(Message msg)1590         public boolean processMessage(Message msg) {
1591             switch (msg.what) {
1592                 case CMD_STOP:
1593                     transitionTo(mStoppingState);
1594                     break;
1595 
1596                 case CMD_START:
1597                     logError("ALERT: START received in StartedState. Please fix caller.");
1598                     break;
1599 
1600                 case CMD_CONFIRM:
1601                     // TODO: Possibly introduce a second type of confirmation
1602                     // that both probes (a) on-link neighbors and (b) does
1603                     // a DHCPv4 RENEW.  We used to do this on Wi-Fi framework
1604                     // roams.
1605                     if (mIpReachabilityMonitor != null) {
1606                         mIpReachabilityMonitor.probeAll();
1607                     }
1608                     break;
1609 
1610                 case EVENT_PRE_DHCP_ACTION_COMPLETE:
1611                     // It's possible to reach here if, for example, someone
1612                     // calls completedPreDhcpAction() after provisioning with
1613                     // a static IP configuration.
1614                     if (mDhcpClient != null) {
1615                         mDhcpClient.sendMessage(DhcpClient.CMD_PRE_DHCP_ACTION_COMPLETE);
1616                     }
1617                     break;
1618 
1619                 case EVENT_NETLINK_LINKPROPERTIES_CHANGED:
1620                     if (!handleLinkPropertiesUpdate(SEND_CALLBACKS)) {
1621                         transitionTo(mStoppingState);
1622                     }
1623                     break;
1624 
1625                 case CMD_UPDATE_TCP_BUFFER_SIZES:
1626                     mTcpBufferSizes = (String) msg.obj;
1627                     // This cannot possibly change provisioning state.
1628                     handleLinkPropertiesUpdate(SEND_CALLBACKS);
1629                     break;
1630 
1631                 case CMD_UPDATE_HTTP_PROXY:
1632                     mHttpProxy = (ProxyInfo) msg.obj;
1633                     // This cannot possibly change provisioning state.
1634                     handleLinkPropertiesUpdate(SEND_CALLBACKS);
1635                     break;
1636 
1637                 case CMD_SET_MULTICAST_FILTER: {
1638                     mMulticastFiltering = (boolean) msg.obj;
1639                     if (mApfFilter != null) {
1640                         mApfFilter.setMulticastFilter(mMulticastFiltering);
1641                     } else {
1642                         mCallback.setFallbackMulticastFilter(mMulticastFiltering);
1643                     }
1644                     break;
1645                 }
1646 
1647                 case EVENT_DHCPACTION_TIMEOUT:
1648                     stopDhcpAction();
1649                     break;
1650 
1651                 case DhcpClient.CMD_PRE_DHCP_ACTION:
1652                     if (mConfiguration.mRequestedPreDhcpActionMs > 0) {
1653                         ensureDhcpAction();
1654                     } else {
1655                         sendMessage(EVENT_PRE_DHCP_ACTION_COMPLETE);
1656                     }
1657                     break;
1658 
1659                 case DhcpClient.CMD_CLEAR_LINKADDRESS:
1660                     clearIPv4Address();
1661                     break;
1662 
1663                 case DhcpClient.CMD_CONFIGURE_LINKADDRESS: {
1664                     final LinkAddress ipAddress = (LinkAddress) msg.obj;
1665                     if (setIPv4Address(ipAddress)) {
1666                         mDhcpClient.sendMessage(DhcpClient.EVENT_LINKADDRESS_CONFIGURED);
1667                     } else {
1668                         logError("Failed to set IPv4 address.");
1669                         dispatchCallback(ProvisioningChange.LOST_PROVISIONING,
1670                                 new LinkProperties(mLinkProperties));
1671                         transitionTo(mStoppingState);
1672                     }
1673                     break;
1674                 }
1675 
1676                 // This message is only received when:
1677                 //
1678                 //     a) initial address acquisition succeeds,
1679                 //     b) renew succeeds or is NAK'd,
1680                 //     c) rebind succeeds or is NAK'd, or
1681                 //     c) the lease expires,
1682                 //
1683                 // but never when initial address acquisition fails. The latter
1684                 // condition is now governed by the provisioning timeout.
1685                 case DhcpClient.CMD_POST_DHCP_ACTION:
1686                     stopDhcpAction();
1687 
1688                     switch (msg.arg1) {
1689                         case DhcpClient.DHCP_SUCCESS:
1690                             handleIPv4Success((DhcpResults) msg.obj);
1691                             break;
1692                         case DhcpClient.DHCP_FAILURE:
1693                             handleIPv4Failure();
1694                             break;
1695                         default:
1696                             logError("Unknown CMD_POST_DHCP_ACTION status: %s", msg.arg1);
1697                     }
1698                     break;
1699 
1700                 case DhcpClient.CMD_ON_QUIT:
1701                     // DHCPv4 quit early for some reason.
1702                     logError("Unexpected CMD_ON_QUIT.");
1703                     mDhcpClient = null;
1704                     break;
1705 
1706                 default:
1707                     return NOT_HANDLED;
1708             }
1709 
1710             mMsgStateLogger.handled(this, getCurrentState());
1711             return HANDLED;
1712         }
1713     }
1714 
1715     private static class MessageHandlingLogger {
1716         public String processedInState;
1717         public String receivedInState;
1718 
reset()1719         public void reset() {
1720             processedInState = null;
1721             receivedInState = null;
1722         }
1723 
handled(State processedIn, IState receivedIn)1724         public void handled(State processedIn, IState receivedIn) {
1725             processedInState = processedIn.getClass().getSimpleName();
1726             receivedInState = receivedIn.getName();
1727         }
1728 
toString()1729         public String toString() {
1730             return String.format("rcvd_in=%s, proc_in=%s",
1731                                  receivedInState, processedInState);
1732         }
1733     }
1734 
1735     // TODO: extract out into CollectionUtils.
any(Iterable<T> coll, Predicate<T> fn)1736     static <T> boolean any(Iterable<T> coll, Predicate<T> fn) {
1737         for (T t : coll) {
1738             if (fn.test(t)) {
1739                 return true;
1740             }
1741         }
1742         return false;
1743     }
1744 
all(Iterable<T> coll, Predicate<T> fn)1745     static <T> boolean all(Iterable<T> coll, Predicate<T> fn) {
1746         return !any(coll, not(fn));
1747     }
1748 
not(Predicate<T> fn)1749     static <T> Predicate<T> not(Predicate<T> fn) {
1750         return (t) -> !fn.test(t);
1751     }
1752 
join(String delimiter, Collection<T> coll)1753     static <T> String join(String delimiter, Collection<T> coll) {
1754         return coll.stream().map(Object::toString).collect(Collectors.joining(delimiter));
1755     }
1756 
find(Iterable<T> coll, Predicate<T> fn)1757     static <T> T find(Iterable<T> coll, Predicate<T> fn) {
1758         for (T t: coll) {
1759             if (fn.test(t)) {
1760               return t;
1761             }
1762         }
1763         return null;
1764     }
1765 
findAll(Collection<T> coll, Predicate<T> fn)1766     static <T> List<T> findAll(Collection<T> coll, Predicate<T> fn) {
1767         return coll.stream().filter(fn).collect(Collectors.toList());
1768     }
1769 }
1770