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.dhcp;
18 
19 import static android.net.dhcp.DhcpPacket.DHCP_BROADCAST_ADDRESS;
20 import static android.net.dhcp.DhcpPacket.DHCP_CAPTIVE_PORTAL;
21 import static android.net.dhcp.DhcpPacket.DHCP_DNS_SERVER;
22 import static android.net.dhcp.DhcpPacket.DHCP_DOMAIN_NAME;
23 import static android.net.dhcp.DhcpPacket.DHCP_LEASE_TIME;
24 import static android.net.dhcp.DhcpPacket.DHCP_MTU;
25 import static android.net.dhcp.DhcpPacket.DHCP_REBINDING_TIME;
26 import static android.net.dhcp.DhcpPacket.DHCP_RENEWAL_TIME;
27 import static android.net.dhcp.DhcpPacket.DHCP_ROUTER;
28 import static android.net.dhcp.DhcpPacket.DHCP_SUBNET_MASK;
29 import static android.net.dhcp.DhcpPacket.DHCP_VENDOR_INFO;
30 import static android.net.dhcp.DhcpPacket.INADDR_ANY;
31 import static android.net.dhcp.DhcpPacket.INADDR_BROADCAST;
32 import static android.net.dhcp.DhcpPacket.INFINITE_LEASE;
33 import static android.net.util.NetworkStackUtils.DHCP_INIT_REBOOT_VERSION;
34 import static android.net.util.NetworkStackUtils.DHCP_IP_CONFLICT_DETECT_VERSION;
35 import static android.net.util.NetworkStackUtils.DHCP_RAPID_COMMIT_VERSION;
36 import static android.net.util.NetworkStackUtils.closeSocketQuietly;
37 import static android.net.util.SocketUtils.makePacketSocketAddress;
38 import static android.provider.DeviceConfig.NAMESPACE_CONNECTIVITY;
39 import static android.system.OsConstants.AF_INET;
40 import static android.system.OsConstants.AF_PACKET;
41 import static android.system.OsConstants.ETH_P_ARP;
42 import static android.system.OsConstants.ETH_P_IP;
43 import static android.system.OsConstants.IPPROTO_UDP;
44 import static android.system.OsConstants.SOCK_DGRAM;
45 import static android.system.OsConstants.SOCK_NONBLOCK;
46 import static android.system.OsConstants.SOCK_RAW;
47 import static android.system.OsConstants.SOL_SOCKET;
48 import static android.system.OsConstants.SO_BROADCAST;
49 import static android.system.OsConstants.SO_RCVBUF;
50 import static android.system.OsConstants.SO_REUSEADDR;
51 
52 import static com.android.server.util.NetworkStackConstants.ARP_REQUEST;
53 import static com.android.server.util.NetworkStackConstants.ETHER_ADDR_LEN;
54 import static com.android.server.util.NetworkStackConstants.IPV4_ADDR_ANY;
55 import static com.android.server.util.NetworkStackConstants.IPV4_CONFLICT_ANNOUNCE_NUM;
56 import static com.android.server.util.NetworkStackConstants.IPV4_CONFLICT_PROBE_NUM;
57 
58 import android.content.Context;
59 import android.net.DhcpResults;
60 import android.net.InetAddresses;
61 import android.net.Layer2PacketParcelable;
62 import android.net.MacAddress;
63 import android.net.NetworkStackIpMemoryStore;
64 import android.net.TrafficStats;
65 import android.net.ip.IpClient;
66 import android.net.ipmemorystore.NetworkAttributes;
67 import android.net.ipmemorystore.OnNetworkAttributesRetrievedListener;
68 import android.net.ipmemorystore.OnStatusListener;
69 import android.net.metrics.DhcpClientEvent;
70 import android.net.metrics.DhcpErrorEvent;
71 import android.net.metrics.IpConnectivityLog;
72 import android.net.util.HostnameTransliterator;
73 import android.net.util.InterfaceParams;
74 import android.net.util.NetworkStackUtils;
75 import android.net.util.PacketReader;
76 import android.net.util.SocketUtils;
77 import android.os.Build;
78 import android.os.Handler;
79 import android.os.Message;
80 import android.os.PowerManager;
81 import android.os.SystemClock;
82 import android.provider.Settings;
83 import android.stats.connectivity.DhcpFeature;
84 import android.system.ErrnoException;
85 import android.system.Os;
86 import android.util.EventLog;
87 import android.util.Log;
88 import android.util.SparseArray;
89 
90 import androidx.annotation.NonNull;
91 import androidx.annotation.Nullable;
92 
93 import com.android.internal.annotations.VisibleForTesting;
94 import com.android.internal.util.HexDump;
95 import com.android.internal.util.MessageUtils;
96 import com.android.internal.util.State;
97 import com.android.internal.util.StateMachine;
98 import com.android.internal.util.TrafficStatsConstants;
99 import com.android.internal.util.WakeupMessage;
100 import com.android.networkstack.R;
101 import com.android.networkstack.apishim.CaptivePortalDataShimImpl;
102 import com.android.networkstack.apishim.SocketUtilsShimImpl;
103 import com.android.networkstack.apishim.common.ShimUtils;
104 import com.android.networkstack.arp.ArpPacket;
105 import com.android.networkstack.metrics.IpProvisioningMetrics;
106 
107 import java.io.FileDescriptor;
108 import java.io.IOException;
109 import java.net.Inet4Address;
110 import java.net.SocketAddress;
111 import java.net.SocketException;
112 import java.nio.ByteBuffer;
113 import java.util.Arrays;
114 import java.util.Random;
115 
116 /**
117  * A DHCPv4 client.
118  *
119  * Written to behave similarly to the DhcpStateMachine + dhcpcd 5.5.6 combination used in Android
120  * 5.1 and below, as configured on Nexus 6. The interface is the same as DhcpStateMachine.
121  *
122  * TODO:
123  *
124  * - Exponential backoff when receiving NAKs (not specified by the RFC, but current behaviour).
125  * - Support persisting lease state and support INIT-REBOOT. Android 5.1 does this, but it does not
126  *   do so correctly: instead of requesting the lease last obtained on a particular network (e.g., a
127  *   given SSID), it requests the last-leased IP address on the same interface, causing a delay if
128  *   the server NAKs or a timeout if it doesn't.
129  *
130  * Known differences from current behaviour:
131  *
132  * - Does not request the "static routes" option.
133  * - Does not support BOOTP servers. DHCP has been around since 1993, should be everywhere now.
134  * - Requests the "broadcast" option, but does nothing with it.
135  * - Rejects invalid subnet masks such as 255.255.255.1 (current code treats that as 255.255.255.0).
136  *
137  * @hide
138  */
139 public class DhcpClient extends StateMachine {
140 
141     private static final String TAG = "DhcpClient";
142     private static final boolean DBG = true;
143     private static final boolean VDBG = Log.isLoggable(TAG, Log.VERBOSE);
144     private static final boolean STATE_DBG = Log.isLoggable(TAG, Log.DEBUG);
145     private static final boolean MSG_DBG = Log.isLoggable(TAG, Log.DEBUG);
146     private static final boolean PACKET_DBG = Log.isLoggable(TAG, Log.DEBUG);
147 
148     // Metrics events: must be kept in sync with server-side aggregation code.
149     /** Represents transitions from DhcpInitState to DhcpBoundState */
150     private static final String EVENT_INITIAL_BOUND = "InitialBoundState";
151     /** Represents transitions from and to DhcpBoundState via DhcpRenewingState */
152     private static final String EVENT_RENEWING_BOUND = "RenewingBoundState";
153 
154     // Timers and timeouts.
155     private static final int SECONDS = 1000;
156     private static final int FIRST_TIMEOUT_MS         =   1 * SECONDS;
157     private static final int MAX_TIMEOUT_MS           = 512 * SECONDS;
158     private static final int IPMEMORYSTORE_TIMEOUT_MS =   1 * SECONDS;
159     private static final int DHCP_INITREBOOT_TIMEOUT_MS = 5 * SECONDS;
160 
161     // The waiting time to restart the DHCP configuration process after broadcasting a
162     // DHCPDECLINE message, (RFC2131 3.1.5 describes client SHOULD wait a minimum of 10
163     // seconds to avoid excessive traffic, but it's too long).
164     @VisibleForTesting
165     public static final String DHCP_RESTART_CONFIG_DELAY = "dhcp_restart_configuration_delay";
166     private static final int DEFAULT_DHCP_RESTART_CONFIG_DELAY_MS = 1 * SECONDS;
167     private static final int MAX_DHCP_CLIENT_RESTART_CONFIG_DELAY_MS = 10 * SECONDS;
168 
169     // Initial random delay before sending first ARP probe.
170     @VisibleForTesting
171     public static final String ARP_FIRST_PROBE_DELAY_MS = "arp_first_probe_delay";
172     private static final int DEFAULT_ARP_FIRST_PROBE_DELAY_MS = 100;
173     private static final int MAX_ARP_FIRST_PROBE_DELAY_MS = 1 * SECONDS;
174 
175     // Minimum delay until retransmitting the probe. The probe will be retransmitted after a
176     // random number of milliseconds in the range ARP_PROBE_MIN_MS and ARP_PROBE_MAX_MS.
177     @VisibleForTesting
178     public static final String ARP_PROBE_MIN_MS = "arp_probe_min";
179     private static final int DEFAULT_ARP_PROBE_MIN_MS = 100;
180     private static final int MAX_ARP_PROBE_MIN_MS = 1 * SECONDS;
181 
182     // Maximum delay until retransmitting the probe.
183     @VisibleForTesting
184     public static final String ARP_PROBE_MAX_MS = "arp_probe_max";
185     private static final int DEFAULT_ARP_PROBE_MAX_MS = 300;
186     private static final int MAX_ARP_PROBE_MAX_MS = 2 * SECONDS;
187 
188     // Initial random delay before sending first ARP Announcement after completing Probe packet
189     // transmission.
190     @VisibleForTesting
191     public static final String ARP_FIRST_ANNOUNCE_DELAY_MS = "arp_first_announce_delay";
192     private static final int DEFAULT_ARP_FIRST_ANNOUNCE_DELAY_MS = 100;
193     private static final int MAX_ARP_FIRST_ANNOUNCE_DELAY_MS = 2 * SECONDS;
194 
195     // Time between retransmitting ARP Announcement packets.
196     @VisibleForTesting
197     public static final String ARP_ANNOUNCE_INTERVAL_MS = "arp_announce_interval";
198     private static final int DEFAULT_ARP_ANNOUNCE_INTERVAL_MS = 100;
199     private static final int MAX_ARP_ANNOUNCE_INTERVAL_MS = 2 * SECONDS;
200 
201     // Max conflict count before configuring interface with declined IP address anyway.
202     private static final int MAX_CONFLICTS_COUNT = 2;
203 
204     // This is not strictly needed, since the client is asynchronous and implements exponential
205     // backoff. It's maintained for backwards compatibility with the previous DHCP code, which was
206     // a blocking operation with a 30-second timeout. We pick 18 seconds so we can send packets at
207     // t=0, t=1, t=3, t=7, t=16, allowing for 10% jitter.
208     private static final int DHCP_TIMEOUT_MS    =  36 * SECONDS;
209 
210     // DhcpClient uses IpClient's handler.
211     private static final int PUBLIC_BASE = IpClient.DHCPCLIENT_CMD_BASE;
212 
213     // Below constants are picked up by MessageUtils and exempt from ProGuard optimization.
214     /* Commands from controller to start/stop DHCP */
215     public static final int CMD_START_DHCP                  = PUBLIC_BASE + 1;
216     public static final int CMD_STOP_DHCP                   = PUBLIC_BASE + 2;
217 
218     /* Notification from DHCP state machine prior to DHCP discovery/renewal */
219     public static final int CMD_PRE_DHCP_ACTION             = PUBLIC_BASE + 3;
220     /* Notification from DHCP state machine post DHCP discovery/renewal. Indicates
221      * success/failure */
222     public static final int CMD_POST_DHCP_ACTION            = PUBLIC_BASE + 4;
223     /* Notification from DHCP state machine before quitting */
224     public static final int CMD_ON_QUIT                     = PUBLIC_BASE + 5;
225 
226     /* Command from controller to indicate DHCP discovery/renewal can continue
227      * after pre DHCP action is complete */
228     public static final int CMD_PRE_DHCP_ACTION_COMPLETE    = PUBLIC_BASE + 6;
229 
230     /* Command and event notification to/from IpManager requesting the setting
231      * (or clearing) of an IPv4 LinkAddress.
232      */
233     public static final int CMD_CLEAR_LINKADDRESS           = PUBLIC_BASE + 7;
234     public static final int CMD_CONFIGURE_LINKADDRESS       = PUBLIC_BASE + 8;
235     public static final int EVENT_LINKADDRESS_CONFIGURED    = PUBLIC_BASE + 9;
236 
237     // Command to IpClient starting/aborting preconnection process.
238     public static final int CMD_START_PRECONNECTION         = PUBLIC_BASE + 10;
239     public static final int CMD_ABORT_PRECONNECTION         = PUBLIC_BASE + 11;
240 
241     // Command to rebind the leased IPv4 address on L2 roaming happened.
242     public static final int CMD_REFRESH_LINKADDRESS         = PUBLIC_BASE + 12;
243 
244     /* Message.arg1 arguments to CMD_POST_DHCP_ACTION notification */
245     public static final int DHCP_SUCCESS = 1;
246     public static final int DHCP_FAILURE = 2;
247 
248     // Internal messages.
249     private static final int PRIVATE_BASE         = IpClient.DHCPCLIENT_CMD_BASE + 100;
250     private static final int CMD_KICK             = PRIVATE_BASE + 1;
251     private static final int CMD_RECEIVED_PACKET  = PRIVATE_BASE + 2;
252     @VisibleForTesting
253     public static final int CMD_TIMEOUT           = PRIVATE_BASE + 3;
254     private static final int CMD_RENEW_DHCP       = PRIVATE_BASE + 4;
255     private static final int CMD_REBIND_DHCP      = PRIVATE_BASE + 5;
256     private static final int CMD_EXPIRE_DHCP      = PRIVATE_BASE + 6;
257     private static final int EVENT_CONFIGURATION_TIMEOUT   = PRIVATE_BASE + 7;
258     private static final int EVENT_CONFIGURATION_OBTAINED  = PRIVATE_BASE + 8;
259     private static final int EVENT_CONFIGURATION_INVALID   = PRIVATE_BASE + 9;
260     private static final int EVENT_IP_CONFLICT             = PRIVATE_BASE + 10;
261     private static final int CMD_ARP_PROBE        = PRIVATE_BASE + 11;
262     private static final int CMD_ARP_ANNOUNCEMENT = PRIVATE_BASE + 12;
263 
264     // constant to represent this DHCP lease has been expired.
265     @VisibleForTesting
266     public static final long EXPIRED_LEASE = 1L;
267 
268     // For message logging.
269     private static final Class[] sMessageClasses = { DhcpClient.class };
270     private static final SparseArray<String> sMessageNames =
271             MessageUtils.findMessageNames(sMessageClasses);
272 
273     // DHCP parameters that we request by default.
274     @VisibleForTesting
275     /* package */ static final byte[] DEFAULT_REQUESTED_PARAMS = new byte[] {
276         DHCP_SUBNET_MASK,
277         DHCP_ROUTER,
278         DHCP_DNS_SERVER,
279         DHCP_DOMAIN_NAME,
280         DHCP_MTU,
281         DHCP_BROADCAST_ADDRESS,  // TODO: currently ignored.
282         DHCP_LEASE_TIME,
283         DHCP_RENEWAL_TIME,
284         DHCP_REBINDING_TIME,
285         DHCP_VENDOR_INFO,
286     };
287 
288     @NonNull
getRequestedParams()289     private byte[] getRequestedParams() {
290         if (isCapportApiEnabled()) {
291             final byte[] params = Arrays.copyOf(DEFAULT_REQUESTED_PARAMS,
292                     DEFAULT_REQUESTED_PARAMS.length + 1);
293             params[params.length - 1] = DHCP_CAPTIVE_PORTAL;
294             return params;
295         }
296         return DEFAULT_REQUESTED_PARAMS;
297     }
298 
isCapportApiEnabled()299     private static boolean isCapportApiEnabled() {
300         return CaptivePortalDataShimImpl.isSupported();
301     }
302 
303     // DHCP flag that means "yes, we support unicast."
304     private static final boolean DO_UNICAST   = false;
305 
306     // System services / libraries we use.
307     private final Context mContext;
308     private final Random mRandom;
309     private final IpConnectivityLog mMetricsLog = new IpConnectivityLog();
310     @NonNull
311     private final IpProvisioningMetrics mMetrics;
312 
313     // We use a UDP socket to send, so the kernel handles ARP and routing for us (DHCP servers can
314     // be off-link as well as on-link).
315     private FileDescriptor mUdpSock;
316 
317     // State variables.
318     private final StateMachine mController;
319     private final WakeupMessage mKickAlarm;
320     private final WakeupMessage mTimeoutAlarm;
321     private final WakeupMessage mRenewAlarm;
322     private final WakeupMessage mRebindAlarm;
323     private final WakeupMessage mExpiryAlarm;
324     private final String mIfaceName;
325 
326     private boolean mRegisteredForPreDhcpNotification;
327     private InterfaceParams mIface;
328     // TODO: MacAddress-ify more of this class hierarchy.
329     private byte[] mHwAddr;
330     private SocketAddress mInterfaceBroadcastAddr;
331     private int mTransactionId;
332     private long mTransactionStartMillis;
333     private DhcpResults mDhcpLease;
334     private long mDhcpLeaseExpiry;
335     private DhcpResults mOffer;
336     private Configuration mConfiguration;
337     private Inet4Address mLastAssignedIpv4Address;
338     private int mConflictCount;
339     private long mLastAssignedIpv4AddressExpiry;
340     private Dependencies mDependencies;
341     @NonNull
342     private final NetworkStackIpMemoryStore mIpMemoryStore;
343     @Nullable
344     private DhcpPacketHandler mDhcpPacketHandler;
345     @Nullable
346     private final String mHostname;
347 
348     // Milliseconds SystemClock timestamps used to record transition times to DhcpBoundState.
349     private long mLastInitEnterTime;
350     private long mLastBoundExitTime;
351 
352     // States.
353     private State mStoppedState = new StoppedState();
354     private State mDhcpState = new DhcpState();
355     private State mDhcpInitState = new DhcpInitState();
356     private State mDhcpPreconnectingState = new DhcpPreconnectingState();
357     private State mDhcpSelectingState = new DhcpSelectingState();
358     private State mDhcpRequestingState = new DhcpRequestingState();
359     private State mDhcpHaveLeaseState = new DhcpHaveLeaseState();
360     private State mConfiguringInterfaceState = new ConfiguringInterfaceState();
361     private State mDhcpBoundState = new DhcpBoundState();
362     private State mDhcpRenewingState = new DhcpRenewingState();
363     private State mDhcpRebindingState = new DhcpRebindingState();
364     private State mDhcpInitRebootState = new DhcpInitRebootState();
365     private State mDhcpRebootingState = new DhcpRebootingState();
366     private State mObtainingConfigurationState = new ObtainingConfigurationState();
367     private State mWaitBeforeStartState = new WaitBeforeStartState(mDhcpInitState);
368     private State mWaitBeforeRenewalState = new WaitBeforeRenewalState(mDhcpRenewingState);
369     private State mWaitBeforeObtainingConfigurationState =
370             new WaitBeforeObtainingConfigurationState(mObtainingConfigurationState);
371     private State mIpAddressConflictDetectingState = new IpAddressConflictDetectingState();
372     private State mDhcpDecliningState = new DhcpDecliningState();
373 
makeWakeupMessage(String cmdName, int cmd)374     private WakeupMessage makeWakeupMessage(String cmdName, int cmd) {
375         cmdName = DhcpClient.class.getSimpleName() + "." + mIfaceName + "." + cmdName;
376         return new WakeupMessage(mContext, getHandler(), cmdName, cmd);
377     }
378 
379     /**
380      * Encapsulates DhcpClient depencencies that's used for unit testing and
381      * integration testing.
382      */
383     public static class Dependencies {
384         private final NetworkStackIpMemoryStore mNetworkStackIpMemoryStore;
385         private final IpProvisioningMetrics mMetrics;
386 
Dependencies(NetworkStackIpMemoryStore store, IpProvisioningMetrics metrics)387         public Dependencies(NetworkStackIpMemoryStore store, IpProvisioningMetrics metrics) {
388             mNetworkStackIpMemoryStore = store;
389             mMetrics = metrics;
390         }
391 
392         /**
393          * Get the configuration from RRO to check whether or not to send hostname option in
394          * DHCPDISCOVER/DHCPREQUEST message.
395          */
getSendHostnameOption(final Context context)396         public boolean getSendHostnameOption(final Context context) {
397             return context.getResources().getBoolean(R.bool.config_dhcp_client_hostname);
398         }
399 
400         /**
401          * Get the device name from system settings.
402          */
getDeviceName(final Context context)403         public String getDeviceName(final Context context) {
404             return Settings.Global.getString(context.getContentResolver(),
405                     Settings.Global.DEVICE_NAME);
406         }
407 
408         /**
409          * Get a IpMemoryStore instance.
410          */
getIpMemoryStore()411         public NetworkStackIpMemoryStore getIpMemoryStore() {
412             return mNetworkStackIpMemoryStore;
413         }
414 
415         /**
416          * Get a IpProvisioningMetrics instance.
417          */
getIpProvisioningMetrics()418         public IpProvisioningMetrics getIpProvisioningMetrics() {
419             return mMetrics;
420         }
421 
422         /**
423          * Return whether a feature guarded by a feature flag is enabled.
424          * @see NetworkStackUtils#isFeatureEnabled(Context, String, String)
425          */
isFeatureEnabled(final Context context, final String name, boolean defaultEnabled)426         public boolean isFeatureEnabled(final Context context, final String name,
427                 boolean defaultEnabled) {
428             return NetworkStackUtils.isFeatureEnabled(context, NAMESPACE_CONNECTIVITY, name,
429                     defaultEnabled);
430         }
431 
432         /**
433          * Get the Integer value of relevant DeviceConfig properties of Connectivity namespace.
434          */
getIntDeviceConfig(final String name, int minimumValue, int maximumValue, int defaultValue)435         public int getIntDeviceConfig(final String name, int minimumValue, int maximumValue,
436                 int defaultValue) {
437             return NetworkStackUtils.getDeviceConfigPropertyInt(NAMESPACE_CONNECTIVITY,
438                     name, minimumValue, maximumValue, defaultValue);
439         }
440 
441         /**
442          * Get a new wake lock to force CPU keeping awake when transmitting packets or waiting
443          * for timeout.
444          */
getWakeLock(final PowerManager powerManager)445         public PowerManager.WakeLock getWakeLock(final PowerManager powerManager) {
446             return powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG);
447         }
448     }
449 
450     // TODO: Take an InterfaceParams instance instead of an interface name String.
DhcpClient(Context context, StateMachine controller, String iface, Dependencies deps)451     private DhcpClient(Context context, StateMachine controller, String iface,
452             Dependencies deps) {
453         super(TAG, controller.getHandler());
454 
455         mDependencies = deps;
456         mContext = context;
457         mController = controller;
458         mIfaceName = iface;
459         mIpMemoryStore = deps.getIpMemoryStore();
460         mMetrics = deps.getIpProvisioningMetrics();
461 
462         // CHECKSTYLE:OFF IndentationCheck
463         addState(mStoppedState);
464         addState(mDhcpState);
465             addState(mDhcpInitState, mDhcpState);
466             addState(mWaitBeforeStartState, mDhcpState);
467             addState(mWaitBeforeObtainingConfigurationState, mDhcpState);
468             addState(mDhcpPreconnectingState, mDhcpState);
469             addState(mObtainingConfigurationState, mDhcpState);
470             addState(mDhcpSelectingState, mDhcpState);
471             addState(mDhcpRequestingState, mDhcpState);
472             addState(mIpAddressConflictDetectingState, mDhcpState);
473             addState(mDhcpHaveLeaseState, mDhcpState);
474                 addState(mConfiguringInterfaceState, mDhcpHaveLeaseState);
475                 addState(mDhcpBoundState, mDhcpHaveLeaseState);
476                 addState(mWaitBeforeRenewalState, mDhcpHaveLeaseState);
477                 addState(mDhcpRenewingState, mDhcpHaveLeaseState);
478                 addState(mDhcpRebindingState, mDhcpHaveLeaseState);
479                 addState(mDhcpDecliningState, mDhcpHaveLeaseState);
480             addState(mDhcpInitRebootState, mDhcpState);
481             addState(mDhcpRebootingState, mDhcpState);
482         // CHECKSTYLE:ON IndentationCheck
483 
484         setInitialState(mStoppedState);
485 
486         mRandom = new Random();
487 
488         // Used to schedule packet retransmissions.
489         mKickAlarm = makeWakeupMessage("KICK", CMD_KICK);
490         // Used to time out PacketRetransmittingStates.
491         mTimeoutAlarm = makeWakeupMessage("TIMEOUT", CMD_TIMEOUT);
492         // Used to schedule DHCP reacquisition.
493         mRenewAlarm = makeWakeupMessage("RENEW", CMD_RENEW_DHCP);
494         mRebindAlarm = makeWakeupMessage("REBIND", CMD_REBIND_DHCP);
495         mExpiryAlarm = makeWakeupMessage("EXPIRY", CMD_EXPIRE_DHCP);
496 
497         // Transliterate hostname read from system settings if RRO option is enabled.
498         final boolean sendHostname = deps.getSendHostnameOption(context);
499         mHostname = sendHostname ? new HostnameTransliterator().transliterate(
500                 deps.getDeviceName(mContext)) : null;
501         mMetrics.setHostnameTransinfo(sendHostname, mHostname != null);
502     }
503 
registerForPreDhcpNotification()504     public void registerForPreDhcpNotification() {
505         mRegisteredForPreDhcpNotification = true;
506     }
507 
makeDhcpClient( Context context, StateMachine controller, InterfaceParams ifParams, Dependencies deps)508     public static DhcpClient makeDhcpClient(
509             Context context, StateMachine controller, InterfaceParams ifParams,
510             Dependencies deps) {
511         DhcpClient client = new DhcpClient(context, controller, ifParams.name, deps);
512         client.mIface = ifParams;
513         client.start();
514         return client;
515     }
516 
517     /**
518      * check whether or not to support caching the last lease info and INIT-REBOOT state.
519      *
520      * INIT-REBOOT state is supported on Android R by default if there is no experiment flag set to
521      * disable this feature explicitly, meanwhile turning this feature on/off by pushing experiment
522      * flag makes it possible to do A/B test and metrics collection on both of Android Q and R, but
523      * it's disabled on Android Q by default.
524      */
isDhcpLeaseCacheEnabled()525     public boolean isDhcpLeaseCacheEnabled() {
526         final boolean defaultEnabled =
527                 ShimUtils.isReleaseOrDevelopmentApiAbove(Build.VERSION_CODES.Q);
528         return mDependencies.isFeatureEnabled(mContext, DHCP_INIT_REBOOT_VERSION, defaultEnabled);
529     }
530 
531     /**
532      * check whether or not to support DHCP Rapid Commit option.
533      */
isDhcpRapidCommitEnabled()534     public boolean isDhcpRapidCommitEnabled() {
535         return mDependencies.isFeatureEnabled(mContext, DHCP_RAPID_COMMIT_VERSION,
536                 false /* defaultEnabled */);
537     }
538 
539     /**
540      * check whether or not to support IP address conflict detection and DHCPDECLINE.
541      */
isDhcpIpConflictDetectEnabled()542     public boolean isDhcpIpConflictDetectEnabled() {
543         return mDependencies.isFeatureEnabled(mContext, DHCP_IP_CONFLICT_DETECT_VERSION,
544                 false /* defaultEnabled */);
545     }
546 
recordMetricEnabledFeatures()547     private void recordMetricEnabledFeatures() {
548         if (isDhcpLeaseCacheEnabled()) mMetrics.setDhcpEnabledFeature(DhcpFeature.DF_INITREBOOT);
549         if (isDhcpRapidCommitEnabled()) mMetrics.setDhcpEnabledFeature(DhcpFeature.DF_RAPIDCOMMIT);
550         if (isDhcpIpConflictDetectEnabled()) mMetrics.setDhcpEnabledFeature(DhcpFeature.DF_DAD);
551         if (mConfiguration.isPreconnectionEnabled) {
552             mMetrics.setDhcpEnabledFeature(DhcpFeature.DF_FILS);
553         }
554     }
555 
confirmDhcpLease(DhcpPacket packet, DhcpResults results)556     private void confirmDhcpLease(DhcpPacket packet, DhcpResults results) {
557         setDhcpLeaseExpiry(packet);
558         acceptDhcpResults(results, "Confirmed");
559     }
560 
initInterface()561     private boolean initInterface() {
562         if (mIface == null) mIface = InterfaceParams.getByName(mIfaceName);
563         if (mIface == null) {
564             Log.e(TAG, "Can't determine InterfaceParams for " + mIfaceName);
565             return false;
566         }
567 
568         mHwAddr = mIface.macAddr.toByteArray();
569         mInterfaceBroadcastAddr = SocketUtilsShimImpl.newInstance().makePacketSocketAddress(
570                 ETH_P_IP, mIface.index, DhcpPacket.ETHER_BROADCAST);
571         return true;
572     }
573 
startNewTransaction()574     private void startNewTransaction() {
575         mTransactionId = mRandom.nextInt();
576         mTransactionStartMillis = SystemClock.elapsedRealtime();
577     }
578 
initUdpSocket()579     private boolean initUdpSocket() {
580         final int oldTag = TrafficStats.getAndSetThreadStatsTag(
581                 TrafficStatsConstants.TAG_SYSTEM_DHCP);
582         try {
583             mUdpSock = Os.socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
584             SocketUtils.bindSocketToInterface(mUdpSock, mIfaceName);
585             Os.setsockoptInt(mUdpSock, SOL_SOCKET, SO_REUSEADDR, 1);
586             Os.setsockoptInt(mUdpSock, SOL_SOCKET, SO_BROADCAST, 1);
587             Os.setsockoptInt(mUdpSock, SOL_SOCKET, SO_RCVBUF, 0);
588             Os.bind(mUdpSock, IPV4_ADDR_ANY, DhcpPacket.DHCP_CLIENT);
589         } catch (SocketException | ErrnoException e) {
590             Log.e(TAG, "Error creating UDP socket", e);
591             return false;
592         } finally {
593             TrafficStats.setThreadStatsTag(oldTag);
594         }
595         return true;
596     }
597 
connectUdpSock(Inet4Address to)598     private boolean connectUdpSock(Inet4Address to) {
599         try {
600             Os.connect(mUdpSock, to, DhcpPacket.DHCP_SERVER);
601             return true;
602         } catch (SocketException | ErrnoException e) {
603             Log.e(TAG, "Error connecting UDP socket", e);
604             return false;
605         }
606     }
607 
608     private class DhcpPacketHandler extends PacketReader {
609         private FileDescriptor mPacketSock;
610 
DhcpPacketHandler(Handler handler)611         DhcpPacketHandler(Handler handler) {
612             super(handler);
613         }
614 
615         @Override
handlePacket(byte[] recvbuf, int length)616         protected void handlePacket(byte[] recvbuf, int length) {
617             try {
618                 final byte[] optionsToSkip =
619                         isCapportApiEnabled() ? new byte[0] : new byte[] { DHCP_CAPTIVE_PORTAL };
620                 final DhcpPacket packet = DhcpPacket.decodeFullPacket(recvbuf, length,
621                         DhcpPacket.ENCAP_L2, optionsToSkip);
622                 if (DBG) Log.d(TAG, "Received packet: " + packet);
623                 sendMessage(CMD_RECEIVED_PACKET, packet);
624             } catch (DhcpPacket.ParseException e) {
625                 Log.e(TAG, "Can't parse packet: " + e.getMessage());
626                 if (PACKET_DBG) {
627                     Log.d(TAG, HexDump.dumpHexString(recvbuf, 0, length));
628                 }
629                 if (e.errorCode == DhcpErrorEvent.DHCP_NO_COOKIE) {
630                     final int snetTagId = 0x534e4554;
631                     final String bugId = "31850211";
632                     final int uid = -1;
633                     final String data = DhcpPacket.ParseException.class.getName();
634                     EventLog.writeEvent(snetTagId, bugId, uid, data);
635                 }
636                 mMetricsLog.log(mIfaceName, new DhcpErrorEvent(e.errorCode));
637                 mMetrics.addDhcpErrorCode(e.errorCode);
638             }
639         }
640 
641         @Override
createFd()642         protected FileDescriptor createFd() {
643             try {
644                 mPacketSock = Os.socket(AF_PACKET, SOCK_RAW | SOCK_NONBLOCK, 0 /* protocol */);
645                 NetworkStackUtils.attachDhcpFilter(mPacketSock);
646                 final SocketAddress addr = makePacketSocketAddress(ETH_P_IP, mIface.index);
647                 Os.bind(mPacketSock, addr);
648             } catch (SocketException | ErrnoException e) {
649                 logError("Error creating packet socket", e);
650                 closeFd(mPacketSock);
651                 mPacketSock = null;
652                 return null;
653             }
654             return mPacketSock;
655         }
656 
657         @Override
readPacket(FileDescriptor fd, byte[] packetBuffer)658         protected int readPacket(FileDescriptor fd, byte[] packetBuffer) throws Exception {
659             try {
660                 return Os.read(fd, packetBuffer, 0, packetBuffer.length);
661             } catch (IOException | ErrnoException e) {
662                 mMetricsLog.log(mIfaceName, new DhcpErrorEvent(DhcpErrorEvent.RECEIVE_ERROR));
663                 throw e;
664             }
665         }
666 
667         @Override
logError(@onNull String msg, @Nullable Exception e)668         protected void logError(@NonNull String msg, @Nullable Exception e) {
669             Log.e(TAG, msg, e);
670         }
671 
transmitPacket(final ByteBuffer buf, final SocketAddress socketAddress)672         public int transmitPacket(final ByteBuffer buf, final SocketAddress socketAddress)
673                 throws ErrnoException, SocketException {
674             return Os.sendto(mPacketSock, buf.array(), 0 /* byteOffset */,
675                     buf.limit() /* byteCount */, 0 /* flags */, socketAddress);
676         }
677     }
678 
getSecs()679     private short getSecs() {
680         return (short) ((SystemClock.elapsedRealtime() - mTransactionStartMillis) / 1000);
681     }
682 
transmitPacket(ByteBuffer buf, String description, int encap, Inet4Address to)683     private boolean transmitPacket(ByteBuffer buf, String description, int encap, Inet4Address to) {
684         try {
685             if (encap == DhcpPacket.ENCAP_L2) {
686                 if (DBG) Log.d(TAG, "Broadcasting " + description);
687                 mDhcpPacketHandler.transmitPacket(buf, mInterfaceBroadcastAddr);
688             } else if (encap == DhcpPacket.ENCAP_BOOTP && to.equals(INADDR_BROADCAST)) {
689                 if (DBG) Log.d(TAG, "Broadcasting " + description);
690                 // We only send L3-encapped broadcasts in DhcpRebindingState,
691                 // where we have an IP address and an unconnected UDP socket.
692                 //
693                 // N.B.: We only need this codepath because DhcpRequestPacket
694                 // hardcodes the source IP address to 0.0.0.0. We could reuse
695                 // the packet socket if this ever changes.
696                 Os.sendto(mUdpSock, buf, 0, to, DhcpPacket.DHCP_SERVER);
697             } else {
698                 // It's safe to call getpeername here, because we only send unicast packets if we
699                 // have an IP address, and we connect the UDP socket in DhcpBoundState#enter.
700                 if (DBG) Log.d(TAG, String.format("Unicasting %s to %s",
701                         description, Os.getpeername(mUdpSock)));
702                 Os.write(mUdpSock, buf);
703             }
704         } catch (ErrnoException | IOException e) {
705             Log.e(TAG, "Can't send packet: ", e);
706             return false;
707         }
708         return true;
709     }
710 
sendDiscoverPacket()711     private boolean sendDiscoverPacket() {
712         final ByteBuffer packet = DhcpPacket.buildDiscoverPacket(
713                 DhcpPacket.ENCAP_L2, mTransactionId, getSecs(), mHwAddr,
714                 DO_UNICAST, getRequestedParams(), isDhcpRapidCommitEnabled(), mHostname);
715         mMetrics.incrementCountForDiscover();
716         return transmitPacket(packet, "DHCPDISCOVER", DhcpPacket.ENCAP_L2, INADDR_BROADCAST);
717     }
718 
sendRequestPacket( final Inet4Address clientAddress, final Inet4Address requestedAddress, final Inet4Address serverAddress, final Inet4Address to)719     private boolean sendRequestPacket(
720             final Inet4Address clientAddress, final Inet4Address requestedAddress,
721             final Inet4Address serverAddress, final Inet4Address to) {
722         // TODO: should we use the transaction ID from the server?
723         final int encap = INADDR_ANY.equals(clientAddress)
724                 ? DhcpPacket.ENCAP_L2 : DhcpPacket.ENCAP_BOOTP;
725 
726         final ByteBuffer packet = DhcpPacket.buildRequestPacket(
727                 encap, mTransactionId, getSecs(), clientAddress,
728                 DO_UNICAST, mHwAddr, requestedAddress,
729                 serverAddress, getRequestedParams(), mHostname);
730         String serverStr = (serverAddress != null) ? serverAddress.getHostAddress() : null;
731         String description = "DHCPREQUEST ciaddr=" + clientAddress.getHostAddress() +
732                              " request=" + requestedAddress.getHostAddress() +
733                              " serverid=" + serverStr;
734         mMetrics.incrementCountForRequest();
735         return transmitPacket(packet, description, encap, to);
736     }
737 
sendDeclinePacket(final Inet4Address requestedAddress, final Inet4Address serverIdentifier)738     private boolean sendDeclinePacket(final Inet4Address requestedAddress,
739             final Inet4Address serverIdentifier) {
740         // Requested IP address and Server Identifier options are mandatory for DHCPDECLINE.
741         final ByteBuffer packet = DhcpPacket.buildDeclinePacket(DhcpPacket.ENCAP_L2,
742                 mTransactionId, mHwAddr, requestedAddress, serverIdentifier);
743         return transmitPacket(packet, "DHCPDECLINE", DhcpPacket.ENCAP_L2, INADDR_BROADCAST);
744     }
745 
scheduleLeaseTimers()746     private void scheduleLeaseTimers() {
747         if (mDhcpLeaseExpiry == 0) {
748             Log.d(TAG, "Infinite lease, no timer scheduling needed");
749             return;
750         }
751 
752         final long now = SystemClock.elapsedRealtime();
753 
754         // TODO: consider getting the renew and rebind timers from T1 and T2.
755         // See also:
756         //     https://tools.ietf.org/html/rfc2131#section-4.4.5
757         //     https://tools.ietf.org/html/rfc1533#section-9.9
758         //     https://tools.ietf.org/html/rfc1533#section-9.10
759         final long remainingDelay = mDhcpLeaseExpiry - now;
760         final long renewDelay = remainingDelay / 2;
761         final long rebindDelay = remainingDelay * 7 / 8;
762         mRenewAlarm.schedule(now + renewDelay);
763         mRebindAlarm.schedule(now + rebindDelay);
764         mExpiryAlarm.schedule(now + remainingDelay);
765         Log.d(TAG, "Scheduling renewal in " + (renewDelay / 1000) + "s");
766         Log.d(TAG, "Scheduling rebind in " + (rebindDelay / 1000) + "s");
767         Log.d(TAG, "Scheduling expiry in " + (remainingDelay / 1000) + "s");
768     }
769 
setLeaseExpiredToIpMemoryStore()770     private void setLeaseExpiredToIpMemoryStore() {
771         final String l2Key = mConfiguration.l2Key;
772         if (l2Key == null) return;
773         final NetworkAttributes.Builder na = new NetworkAttributes.Builder();
774         // TODO: clear out the address and lease instead of storing an expired lease
775         na.setAssignedV4AddressExpiry(EXPIRED_LEASE);
776 
777         final OnStatusListener listener = status -> {
778             if (!status.isSuccess()) Log.e(TAG, "Failed to set lease expiry, status: " + status);
779         };
780         mIpMemoryStore.storeNetworkAttributes(l2Key, na.build(), listener);
781     }
782 
maybeSaveLeaseToIpMemoryStore()783     private void maybeSaveLeaseToIpMemoryStore() {
784         final String l2Key = mConfiguration.l2Key;
785         if (l2Key == null || mDhcpLease == null || mDhcpLease.ipAddress == null) return;
786         final NetworkAttributes.Builder na = new NetworkAttributes.Builder();
787         na.setAssignedV4Address((Inet4Address) mDhcpLease.ipAddress.getAddress());
788         na.setAssignedV4AddressExpiry((mDhcpLease.leaseDuration == INFINITE_LEASE)
789                 ? Long.MAX_VALUE
790                 : mDhcpLease.leaseDuration * 1000 + System.currentTimeMillis());
791         na.setDnsAddresses(mDhcpLease.dnsServers);
792         na.setMtu(mDhcpLease.mtu);
793 
794         final OnStatusListener listener = status -> {
795             if (!status.isSuccess()) Log.e(TAG, "Failed to store network attrs, status: " + status);
796         };
797         mIpMemoryStore.storeNetworkAttributes(l2Key, na.build(), listener);
798     }
799 
notifySuccess()800     private void notifySuccess() {
801         if (isDhcpLeaseCacheEnabled()) {
802             maybeSaveLeaseToIpMemoryStore();
803         }
804         mController.sendMessage(
805                 CMD_POST_DHCP_ACTION, DHCP_SUCCESS, 0, new DhcpResults(mDhcpLease));
806     }
807 
notifyFailure()808     private void notifyFailure() {
809         if (isDhcpLeaseCacheEnabled()) {
810             setLeaseExpiredToIpMemoryStore();
811         }
812         mController.sendMessage(CMD_POST_DHCP_ACTION, DHCP_FAILURE, 0, null);
813     }
814 
acceptDhcpResults(DhcpResults results, String msg)815     private void acceptDhcpResults(DhcpResults results, String msg) {
816         mDhcpLease = results;
817         if (mDhcpLease.dnsServers.isEmpty()) {
818             // supplement customized dns servers
819             final String[] dnsServersList =
820                     mContext.getResources().getStringArray(R.array.config_default_dns_servers);
821             for (final String dnsServer : dnsServersList) {
822                 try {
823                     mDhcpLease.dnsServers.add(InetAddresses.parseNumericAddress(dnsServer));
824                 } catch (IllegalArgumentException e) {
825                     Log.e(TAG, "Invalid default DNS server: " + dnsServer, e);
826                 }
827             }
828         }
829         mOffer = null;
830         Log.d(TAG, msg + " lease: " + mDhcpLease);
831     }
832 
clearDhcpState()833     private void clearDhcpState() {
834         mDhcpLease = null;
835         mDhcpLeaseExpiry = 0;
836         mOffer = null;
837     }
838 
839     /**
840      * Quit the DhcpStateMachine.
841      *
842      * @hide
843      */
doQuit()844     public void doQuit() {
845         Log.d(TAG, "doQuit");
846         quit();
847     }
848 
849     @Override
onQuitting()850     protected void onQuitting() {
851         Log.d(TAG, "onQuitting");
852         mController.sendMessage(CMD_ON_QUIT);
853     }
854 
855     abstract class LoggingState extends State {
856         private long mEnterTimeMs;
857 
858         @Override
enter()859         public void enter() {
860             if (STATE_DBG) Log.d(TAG, "Entering state " + getName());
861             mEnterTimeMs = SystemClock.elapsedRealtime();
862         }
863 
864         @Override
exit()865         public void exit() {
866             long durationMs = SystemClock.elapsedRealtime() - mEnterTimeMs;
867             logState(getName(), (int) durationMs);
868         }
869 
messageName(int what)870         private String messageName(int what) {
871             return sMessageNames.get(what, Integer.toString(what));
872         }
873 
messageToString(Message message)874         private String messageToString(Message message) {
875             long now = SystemClock.uptimeMillis();
876             return new StringBuilder(" ")
877                     .append(message.getWhen() - now)
878                     .append(messageName(message.what))
879                     .append(" ").append(message.arg1)
880                     .append(" ").append(message.arg2)
881                     .append(" ").append(message.obj)
882                     .toString();
883         }
884 
885         @Override
processMessage(Message message)886         public boolean processMessage(Message message) {
887             if (MSG_DBG) {
888                 Log.d(TAG, getName() + messageToString(message));
889             }
890             return NOT_HANDLED;
891         }
892 
893         @Override
getName()894         public String getName() {
895             // All DhcpClient's states are inner classes with a well defined name.
896             // Use getSimpleName() and avoid super's getName() creating new String instances.
897             return getClass().getSimpleName();
898         }
899     }
900 
901     // Sends CMD_PRE_DHCP_ACTION to the controller, waits for the controller to respond with
902     // CMD_PRE_DHCP_ACTION_COMPLETE, and then transitions to mOtherState.
903     abstract class WaitBeforeOtherState extends LoggingState {
904         private final State mOtherState;
905 
WaitBeforeOtherState(State otherState)906         WaitBeforeOtherState(State otherState) {
907             mOtherState = otherState;
908         }
909 
910         @Override
enter()911         public void enter() {
912             super.enter();
913             mController.sendMessage(CMD_PRE_DHCP_ACTION);
914         }
915 
916         @Override
processMessage(Message message)917         public boolean processMessage(Message message) {
918             super.processMessage(message);
919             switch (message.what) {
920                 case CMD_PRE_DHCP_ACTION_COMPLETE:
921                     transitionTo(mOtherState);
922                     return HANDLED;
923                 default:
924                     return NOT_HANDLED;
925             }
926         }
927     }
928 
929     /**
930      * Helper method to transition to the appropriate state according to whether the pre dhcp
931      * action (e.g. turn off power optimization while doing DHCP) is required to execute.
932      * waitStateForPreDhcpAction is used to wait the pre dhcp action completed before moving to
933      * other state. If the pre dhcp action is unnecessary, transition to the target state directly.
934      */
preDhcpTransitionTo(final State waitStateForPreDhcpAction, final State targetState)935     private void preDhcpTransitionTo(final State waitStateForPreDhcpAction,
936             final State targetState) {
937         transitionTo(mRegisteredForPreDhcpNotification ? waitStateForPreDhcpAction : targetState);
938     }
939 
940     /**
941      * This class is used to convey initial configuration to DhcpClient when starting DHCP.
942      */
943     public static class Configuration {
944         // This is part of the initial configuration because it is passed in on startup and
945         // never updated.
946         // TODO: decide what to do about L2 key changes while the client is connected.
947         public final String l2Key;
948         public final boolean isPreconnectionEnabled;
949 
Configuration(String l2Key, boolean isPreconnectionEnabled)950         public Configuration(String l2Key, boolean isPreconnectionEnabled) {
951             this.l2Key = l2Key;
952             this.isPreconnectionEnabled = isPreconnectionEnabled;
953         }
954     }
955 
956     class StoppedState extends State {
957         @Override
processMessage(Message message)958         public boolean processMessage(Message message) {
959             switch (message.what) {
960                 case CMD_START_DHCP:
961                     mConfiguration = (Configuration) message.obj;
962                     if (mConfiguration.isPreconnectionEnabled) {
963                         transitionTo(mDhcpPreconnectingState);
964                     } else {
965                         startInitRebootOrInit();
966                     }
967                     recordMetricEnabledFeatures();
968                     return HANDLED;
969                 default:
970                     return NOT_HANDLED;
971             }
972         }
973     }
974 
975     class WaitBeforeStartState extends WaitBeforeOtherState {
WaitBeforeStartState(State otherState)976         WaitBeforeStartState(State otherState) {
977             super(otherState);
978         }
979     }
980 
981     class WaitBeforeRenewalState extends WaitBeforeOtherState {
WaitBeforeRenewalState(State otherState)982         WaitBeforeRenewalState(State otherState) {
983             super(otherState);
984         }
985     }
986 
987     class WaitBeforeObtainingConfigurationState extends WaitBeforeOtherState {
WaitBeforeObtainingConfigurationState(State otherState)988         WaitBeforeObtainingConfigurationState(State otherState) {
989             super(otherState);
990         }
991     }
992 
993     class DhcpState extends State {
994         @Override
enter()995         public void enter() {
996             clearDhcpState();
997             mConflictCount = 0;
998             if (initInterface() && initUdpSocket()) {
999                 mDhcpPacketHandler = new DhcpPacketHandler(getHandler());
1000                 if (mDhcpPacketHandler.start()) return;
1001                 Log.e(TAG, "Fail to start DHCP Packet Handler");
1002             }
1003             notifyFailure();
1004             // We cannot call transitionTo because a transition is still in progress.
1005             // Instead, ensure that we process CMD_STOP_DHCP as soon as the transition is complete.
1006             deferMessage(obtainMessage(CMD_STOP_DHCP));
1007         }
1008 
1009         @Override
exit()1010         public void exit() {
1011             if (mDhcpPacketHandler != null) {
1012                 mDhcpPacketHandler.stop();
1013                 if (DBG) Log.d(TAG, "DHCP Packet Handler stopped");
1014             }
1015             closeSocketQuietly(mUdpSock);
1016             clearDhcpState();
1017         }
1018 
1019         @Override
processMessage(Message message)1020         public boolean processMessage(Message message) {
1021             super.processMessage(message);
1022             switch (message.what) {
1023                 case CMD_STOP_DHCP:
1024                     transitionTo(mStoppedState);
1025                     return HANDLED;
1026                 default:
1027                     return NOT_HANDLED;
1028             }
1029         }
1030     }
1031 
isValidPacket(DhcpPacket packet)1032     public boolean isValidPacket(DhcpPacket packet) {
1033         // TODO: check checksum.
1034         int xid = packet.getTransactionId();
1035         if (xid != mTransactionId) {
1036             Log.d(TAG, "Unexpected transaction ID " + xid + ", expected " + mTransactionId);
1037             return false;
1038         }
1039         if (!Arrays.equals(packet.getClientMac(), mHwAddr)) {
1040             Log.d(TAG, "MAC addr mismatch: got " +
1041                     HexDump.toHexString(packet.getClientMac()) + ", expected " +
1042                     HexDump.toHexString(packet.getClientMac()));
1043             return false;
1044         }
1045         return true;
1046     }
1047 
setDhcpLeaseExpiry(DhcpPacket packet)1048     public void setDhcpLeaseExpiry(DhcpPacket packet) {
1049         long leaseTimeMillis = packet.getLeaseTimeMillis();
1050         mDhcpLeaseExpiry =
1051                 (leaseTimeMillis > 0) ? SystemClock.elapsedRealtime() + leaseTimeMillis : 0;
1052     }
1053 
1054     abstract class TimeoutState extends LoggingState {
1055         protected int mTimeout = 0;
1056 
1057         @Override
enter()1058         public void enter() {
1059             super.enter();
1060             maybeInitTimeout();
1061         }
1062 
1063         @Override
processMessage(Message message)1064         public boolean processMessage(Message message) {
1065             super.processMessage(message);
1066             switch (message.what) {
1067                 case CMD_TIMEOUT:
1068                     timeout();
1069                     return HANDLED;
1070                 default:
1071                     return NOT_HANDLED;
1072             }
1073         }
1074 
1075         @Override
exit()1076         public void exit() {
1077             super.exit();
1078             mTimeoutAlarm.cancel();
1079         }
1080 
timeout()1081         protected abstract void timeout();
maybeInitTimeout()1082         private void maybeInitTimeout() {
1083             if (mTimeout > 0) {
1084                 long alarmTime = SystemClock.elapsedRealtime() + mTimeout;
1085                 mTimeoutAlarm.schedule(alarmTime);
1086             }
1087         }
1088     }
1089 
1090     /**
1091      * Retransmits packets using jittered exponential backoff with an optional timeout. Packet
1092      * transmission is triggered by CMD_KICK, which is sent by an AlarmManager alarm. If a subclass
1093      * sets mTimeout to a positive value, then timeout() is called by an AlarmManager alarm mTimeout
1094      * milliseconds after entering the state. Kicks and timeouts are cancelled when leaving the
1095      * state.
1096      *
1097      * Concrete subclasses must implement sendPacket, which is called when the alarm fires and a
1098      * packet needs to be transmitted, and receivePacket, which is triggered by CMD_RECEIVED_PACKET
1099      * sent by the receive thread. They may also set mTimeout and implement timeout.
1100      */
1101     abstract class PacketRetransmittingState extends TimeoutState {
1102         private int mTimer;
1103 
1104         @Override
enter()1105         public void enter() {
1106             super.enter();
1107             initTimer();
1108             sendMessage(CMD_KICK);
1109         }
1110 
1111         @Override
processMessage(Message message)1112         public boolean processMessage(Message message) {
1113             if (super.processMessage(message) == HANDLED) {
1114                 return HANDLED;
1115             }
1116 
1117             switch (message.what) {
1118                 case CMD_KICK:
1119                     sendPacket();
1120                     scheduleKick();
1121                     return HANDLED;
1122                 case CMD_RECEIVED_PACKET:
1123                     receivePacket((DhcpPacket) message.obj);
1124                     return HANDLED;
1125                 default:
1126                     return NOT_HANDLED;
1127             }
1128         }
1129 
1130         @Override
exit()1131         public void exit() {
1132             super.exit();
1133             mKickAlarm.cancel();
1134         }
1135 
sendPacket()1136         protected abstract boolean sendPacket();
receivePacket(DhcpPacket packet)1137         protected abstract void receivePacket(DhcpPacket packet);
timeout()1138         protected void timeout() {}
1139 
initTimer()1140         protected void initTimer() {
1141             mTimer = FIRST_TIMEOUT_MS;
1142         }
1143 
jitterTimer(int baseTimer)1144         protected int jitterTimer(int baseTimer) {
1145             int maxJitter = baseTimer / 10;
1146             int jitter = mRandom.nextInt(2 * maxJitter) - maxJitter;
1147             return baseTimer + jitter;
1148         }
1149 
scheduleKick()1150         protected void scheduleKick() {
1151             long now = SystemClock.elapsedRealtime();
1152             long timeout = jitterTimer(mTimer);
1153             long alarmTime = now + timeout;
1154             mKickAlarm.schedule(alarmTime);
1155             mTimer *= 2;
1156             if (mTimer > MAX_TIMEOUT_MS) {
1157                 mTimer = MAX_TIMEOUT_MS;
1158             }
1159         }
1160     }
1161 
1162     class ObtainingConfigurationState extends LoggingState {
1163         @Override
enter()1164         public void enter() {
1165             super.enter();
1166 
1167             // Set a timeout for retrieving network attributes operation
1168             sendMessageDelayed(EVENT_CONFIGURATION_TIMEOUT, IPMEMORYSTORE_TIMEOUT_MS);
1169 
1170             final OnNetworkAttributesRetrievedListener listener = (status, l2Key, attributes) -> {
1171                 if (null == attributes || null == attributes.assignedV4Address) {
1172                     if (!status.isSuccess()) {
1173                         Log.e(TAG, "Error retrieving network attributes: " + status);
1174                     }
1175                     sendMessage(EVENT_CONFIGURATION_INVALID);
1176                     return;
1177                 }
1178                 sendMessage(EVENT_CONFIGURATION_OBTAINED, attributes);
1179             };
1180             mIpMemoryStore.retrieveNetworkAttributes(mConfiguration.l2Key, listener);
1181         }
1182 
1183         @Override
processMessage(Message message)1184         public boolean processMessage(Message message) {
1185             super.processMessage(message);
1186             switch (message.what) {
1187                 case EVENT_CONFIGURATION_INVALID:
1188                 case EVENT_CONFIGURATION_TIMEOUT:
1189                     transitionTo(mDhcpInitState);
1190                     return HANDLED;
1191 
1192                 case EVENT_CONFIGURATION_OBTAINED:
1193                     final long currentTime = System.currentTimeMillis();
1194                     NetworkAttributes attributes = (NetworkAttributes) message.obj;
1195                     if (DBG) {
1196                         Log.d(TAG, "l2key: "         + mConfiguration.l2Key
1197                                 + " lease address: " + attributes.assignedV4Address
1198                                 + " lease expiry: "  + attributes.assignedV4AddressExpiry
1199                                 + " current time: "  + currentTime);
1200                     }
1201                     if (currentTime >= attributes.assignedV4AddressExpiry) {
1202                         // Lease has expired.
1203                         transitionTo(mDhcpInitState);
1204                         return HANDLED;
1205                     }
1206                     mLastAssignedIpv4Address = attributes.assignedV4Address;
1207                     mLastAssignedIpv4AddressExpiry = attributes.assignedV4AddressExpiry;
1208                     transitionTo(mDhcpInitRebootState);
1209                     return HANDLED;
1210 
1211                 default:
1212                     deferMessage(message);
1213                     return HANDLED;
1214             }
1215         }
1216 
1217         @Override
exit()1218         public void exit() {
1219             super.exit();
1220             removeMessages(EVENT_CONFIGURATION_INVALID);
1221             removeMessages(EVENT_CONFIGURATION_TIMEOUT);
1222             removeMessages(EVENT_CONFIGURATION_OBTAINED);
1223         }
1224     }
1225 
receiveOfferOrAckPacket(final DhcpPacket packet, final boolean acceptRapidCommit)1226     private void receiveOfferOrAckPacket(final DhcpPacket packet, final boolean acceptRapidCommit) {
1227         if (!isValidPacket(packet)) return;
1228 
1229         // 1. received the DHCPOFFER packet, process it by following RFC2131.
1230         // 2. received the DHCPACK packet from DHCP Servers that support Rapid
1231         //    Commit option, process it by following RFC4039.
1232         if (packet instanceof DhcpOfferPacket) {
1233             mOffer = packet.toDhcpResults();
1234             if (mOffer != null) {
1235                 Log.d(TAG, "Got pending lease: " + mOffer);
1236                 transitionTo(mDhcpRequestingState);
1237             }
1238         } else if (packet instanceof DhcpAckPacket) {
1239             // If rapid commit is not enabled in DhcpInitState, or enablePreconnection is
1240             // not enabled in DhcpPreconnectingState, ignore DHCPACK packet. Only DHCPACK
1241             // with the rapid commit option are valid.
1242             if (!acceptRapidCommit || !packet.mRapidCommit) return;
1243 
1244             final DhcpResults results = packet.toDhcpResults();
1245             if (results != null) {
1246                 confirmDhcpLease(packet, results);
1247                 transitionTo(isDhcpIpConflictDetectEnabled()
1248                         ? mIpAddressConflictDetectingState : mConfiguringInterfaceState);
1249             }
1250         }
1251     }
1252 
1253     class DhcpInitState extends PacketRetransmittingState {
DhcpInitState()1254         public DhcpInitState() {
1255             super();
1256         }
1257 
1258         @Override
enter()1259         public void enter() {
1260             super.enter();
1261             startNewTransaction();
1262             mLastInitEnterTime = SystemClock.elapsedRealtime();
1263         }
1264 
sendPacket()1265         protected boolean sendPacket() {
1266             return sendDiscoverPacket();
1267         }
1268 
receivePacket(DhcpPacket packet)1269         protected void receivePacket(DhcpPacket packet) {
1270             receiveOfferOrAckPacket(packet, isDhcpRapidCommitEnabled());
1271         }
1272     }
1273 
startInitRebootOrInit()1274     private void startInitRebootOrInit() {
1275         if (isDhcpLeaseCacheEnabled()) {
1276             preDhcpTransitionTo(mWaitBeforeObtainingConfigurationState,
1277                     mObtainingConfigurationState);
1278         } else {
1279             preDhcpTransitionTo(mWaitBeforeStartState, mDhcpInitState);
1280         }
1281     }
1282 
1283     class DhcpPreconnectingState extends TimeoutState {
1284         // This state is used to support Fast Initial Link Setup (FILS) IP Address Setup
1285         // procedure defined in the IEEE802.11ai (2016) currently. However, this state could
1286         // be extended to support other intended useage as well in the future, e.g. pre-actions
1287         // should be completed in advance before the normal DHCP solicit process starts.
DhcpPreconnectingState()1288         DhcpPreconnectingState() {
1289             mTimeout = FIRST_TIMEOUT_MS;
1290         }
1291 
1292         @Override
enter()1293         public void enter() {
1294             super.enter();
1295             startNewTransaction();
1296             mLastInitEnterTime = SystemClock.elapsedRealtime();
1297             sendPreconnectionPacket();
1298         }
1299 
1300         @Override
processMessage(Message message)1301         public boolean processMessage(Message message) {
1302             if (super.processMessage(message) == HANDLED) {
1303                 return HANDLED;
1304             }
1305 
1306             switch (message.what) {
1307                 case CMD_RECEIVED_PACKET:
1308                     receiveOfferOrAckPacket((DhcpPacket) message.obj,
1309                             mConfiguration.isPreconnectionEnabled);
1310                     return HANDLED;
1311                 case CMD_ABORT_PRECONNECTION:
1312                     startInitRebootOrInit();
1313                     return HANDLED;
1314                 default:
1315                     return NOT_HANDLED;
1316             }
1317         }
1318 
1319         // This timeout is necessary and cannot just be replaced with a notification that
1320         // preconnection is complete. This is because:
1321         // - The preconnection complete notification could arrive before the ACK with rapid
1322         //   commit arrives. In this case we would go back to init state, pick a new transaction
1323         //   ID, and when the ACK with rapid commit arrives, we would ignore it because the
1324         //   transaction ID doesn't match.
1325         // - We cannot just wait in this state until the ACK with rapid commit arrives, because
1326         //   if that ACK never arrives (e.g., dropped by the network), we'll never go back to init
1327         //   and send a DISCOVER.
1328         @Override
timeout()1329         public void timeout() {
1330             startInitRebootOrInit();
1331         }
1332 
sendPreconnectionPacket()1333         private void sendPreconnectionPacket() {
1334             final Layer2PacketParcelable l2Packet = new Layer2PacketParcelable();
1335             final ByteBuffer packet = DhcpPacket.buildDiscoverPacket(
1336                     DhcpPacket.ENCAP_L2, mTransactionId, getSecs(), mHwAddr,
1337                     DO_UNICAST, getRequestedParams(), true /* rapid commit */, mHostname);
1338 
1339             l2Packet.dstMacAddress = MacAddress.fromBytes(DhcpPacket.ETHER_BROADCAST);
1340             l2Packet.payload = Arrays.copyOf(packet.array(), packet.limit());
1341             mController.sendMessage(CMD_START_PRECONNECTION, l2Packet);
1342         }
1343     }
1344 
1345     // Not implemented. We request the first offer we receive.
1346     class DhcpSelectingState extends LoggingState {
1347     }
1348 
1349     class DhcpRequestingState extends PacketRetransmittingState {
DhcpRequestingState()1350         public DhcpRequestingState() {
1351             mTimeout = DHCP_TIMEOUT_MS / 2;
1352         }
1353 
sendPacket()1354         protected boolean sendPacket() {
1355             return sendRequestPacket(
1356                     INADDR_ANY,                                    // ciaddr
1357                     (Inet4Address) mOffer.ipAddress.getAddress(),  // DHCP_REQUESTED_IP
1358                     (Inet4Address) mOffer.serverAddress,           // DHCP_SERVER_IDENTIFIER
1359                     INADDR_BROADCAST);                             // packet destination address
1360         }
1361 
receivePacket(DhcpPacket packet)1362         protected void receivePacket(DhcpPacket packet) {
1363             if (!isValidPacket(packet)) return;
1364             if ((packet instanceof DhcpAckPacket)) {
1365                 DhcpResults results = packet.toDhcpResults();
1366                 if (results != null) {
1367                     confirmDhcpLease(packet, results);
1368                     transitionTo(isDhcpIpConflictDetectEnabled()
1369                             ? mIpAddressConflictDetectingState : mConfiguringInterfaceState);
1370                 }
1371             } else if (packet instanceof DhcpNakPacket) {
1372                 // TODO: Wait a while before returning into INIT state.
1373                 Log.d(TAG, "Received NAK, returning to INIT");
1374                 mOffer = null;
1375                 transitionTo(mDhcpInitState);
1376             }
1377         }
1378 
1379         @Override
timeout()1380         protected void timeout() {
1381             // After sending REQUESTs unsuccessfully for a while, go back to init.
1382             transitionTo(mDhcpInitState);
1383         }
1384     }
1385 
1386     class DhcpHaveLeaseState extends State {
1387         @Override
processMessage(Message message)1388         public boolean processMessage(Message message) {
1389             switch (message.what) {
1390                 case CMD_EXPIRE_DHCP:
1391                     Log.d(TAG, "Lease expired!");
1392                     notifyFailure();
1393                     transitionTo(mDhcpInitState);
1394                     return HANDLED;
1395                 default:
1396                     return NOT_HANDLED;
1397             }
1398         }
1399 
1400         @Override
exit()1401         public void exit() {
1402             // Clear any extant alarms.
1403             mRenewAlarm.cancel();
1404             mRebindAlarm.cancel();
1405             mExpiryAlarm.cancel();
1406             clearDhcpState();
1407             // Tell IpManager to clear the IPv4 address. There is no need to
1408             // wait for confirmation since any subsequent packets are sent from
1409             // INADDR_ANY anyway (DISCOVER, REQUEST).
1410             mController.sendMessage(CMD_CLEAR_LINKADDRESS);
1411         }
1412     }
1413 
1414     class ConfiguringInterfaceState extends LoggingState {
1415         @Override
enter()1416         public void enter() {
1417             super.enter();
1418             // We must call notifySuccess to apply the rest of the DHCP configuration (e.g., DNS
1419             // servers) before adding the IP address to the interface. Otherwise, as soon as
1420             // IpClient sees the IP address appear, it will enter provisioned state without any
1421             // configuration information from DHCP. http://b/146850745.
1422             notifySuccess();
1423             mController.sendMessage(CMD_CONFIGURE_LINKADDRESS, mDhcpLease.ipAddress);
1424         }
1425 
1426         @Override
processMessage(Message message)1427         public boolean processMessage(Message message) {
1428             super.processMessage(message);
1429             switch (message.what) {
1430                 case EVENT_LINKADDRESS_CONFIGURED:
1431                     transitionTo(mDhcpBoundState);
1432                     return HANDLED;
1433                 default:
1434                     return NOT_HANDLED;
1435             }
1436         }
1437     }
1438 
1439     private class IpConflictDetector extends PacketReader {
1440         private FileDescriptor mArpSock;
1441         private final Inet4Address mTargetIp;
1442 
IpConflictDetector(@onNull Handler handler, @NonNull Inet4Address ipAddress)1443         IpConflictDetector(@NonNull Handler handler, @NonNull Inet4Address ipAddress) {
1444             super(handler);
1445             mTargetIp = ipAddress;
1446         }
1447 
1448         @Override
handlePacket(byte[] recvbuf, int length)1449         protected void handlePacket(byte[] recvbuf, int length) {
1450             try {
1451                 final ArpPacket packet = ArpPacket.parseArpPacket(recvbuf, length);
1452                 if (hasIpAddressConflict(packet, mTargetIp)) {
1453                     mMetrics.incrementCountForIpConflict();
1454                     sendMessage(EVENT_IP_CONFLICT);
1455                 }
1456             } catch (ArpPacket.ParseException e) {
1457                 logError("Can't parse ARP packet", e);
1458             }
1459         }
1460 
1461         @Override
createFd()1462         protected FileDescriptor createFd() {
1463             try {
1464                 // TODO: attach ARP packet only filter.
1465                 mArpSock = Os.socket(AF_PACKET, SOCK_RAW | SOCK_NONBLOCK, 0 /* protocol */);
1466                 SocketAddress addr = makePacketSocketAddress(ETH_P_ARP, mIface.index);
1467                 Os.bind(mArpSock, addr);
1468                 return mArpSock;
1469             } catch (SocketException | ErrnoException e) {
1470                 logError("Error creating ARP socket", e);
1471                 closeFd(mArpSock);
1472                 mArpSock = null;
1473                 return null;
1474             }
1475         }
1476 
1477         @Override
logError(@onNull String msg, @NonNull Exception e)1478         protected void logError(@NonNull String msg, @NonNull Exception e) {
1479             Log.e(TAG, msg, e);
1480         }
1481 
transmitPacket(@onNull Inet4Address targetIp, @NonNull Inet4Address senderIp, final byte[] hwAddr, @NonNull SocketAddress sockAddr)1482         public boolean transmitPacket(@NonNull Inet4Address targetIp,
1483                 @NonNull Inet4Address senderIp, final byte[] hwAddr,
1484                 @NonNull SocketAddress sockAddr) {
1485             // RFC5227 3. describes both ARP Probes and Announcements use ARP Request packet.
1486             final ByteBuffer packet = ArpPacket.buildArpPacket(DhcpPacket.ETHER_BROADCAST, hwAddr,
1487                     targetIp.getAddress(), new byte[ETHER_ADDR_LEN], senderIp.getAddress(),
1488                     (short) ARP_REQUEST);
1489             try {
1490                 Os.sendto(mArpSock, packet.array(), 0 /* byteOffset */,
1491                         packet.limit() /* byteCount */, 0 /* flags */, sockAddr);
1492                 return true;
1493             } catch (ErrnoException | SocketException e) {
1494                 logError("Can't send ARP packet", e);
1495                 return false;
1496             }
1497         }
1498     }
1499 
isArpProbe(@onNull ArpPacket packet)1500     private boolean isArpProbe(@NonNull ArpPacket packet) {
1501         return (packet.opCode == ARP_REQUEST && packet.senderIp.equals(INADDR_ANY)
1502                 && !packet.targetIp.equals(INADDR_ANY));
1503     }
1504 
1505     // RFC5227 2.1.1 says, during probing period:
1506     // 1. the host receives any ARP packet (Request *or* Reply) on the interface where the
1507     //    probe is being performed, where the packet's 'sender IP address' is the address
1508     //    being probed for, then the host MUST treat this address as conflict.
1509     // 2. the host receives any ARP Probe where the packet's 'target IP address' is the
1510     //    address being probed for, and the packet's 'sender hardware address' is not the
1511     //    hardware address of any of the host's interfaces, then the host SHOULD similarly
1512     //    treat this as an address conflict.
packetHasIpAddressConflict(@onNull ArpPacket packet, @NonNull Inet4Address targetIp)1513     private boolean packetHasIpAddressConflict(@NonNull ArpPacket packet,
1514             @NonNull Inet4Address targetIp) {
1515         return (((!packet.senderIp.equals(INADDR_ANY) && packet.senderIp.equals(targetIp))
1516                 || (isArpProbe(packet) && packet.targetIp.equals(targetIp)))
1517                 && !Arrays.equals(packet.senderHwAddress.toByteArray(), mHwAddr));
1518     }
1519 
hasIpAddressConflict(@onNull ArpPacket packet, @NonNull Inet4Address targetIp)1520     private boolean hasIpAddressConflict(@NonNull ArpPacket packet,
1521             @NonNull Inet4Address targetIp) {
1522         if (!packetHasIpAddressConflict(packet, targetIp)) return false;
1523         if (DBG) {
1524             final String senderIpString = packet.senderIp.getHostAddress();
1525             final String targetIpString = packet.targetIp.getHostAddress();
1526             final MacAddress senderMacAddress = packet.senderHwAddress;
1527             final MacAddress hostMacAddress = MacAddress.fromBytes(mHwAddr);
1528             Log.d(TAG, "IP address conflict detected:"
1529                     + (packet.opCode == ARP_REQUEST ? "ARP Request" : "ARP Reply")
1530                     + " ARP sender MAC: " + senderMacAddress.toString()
1531                     + " host MAC: "       + hostMacAddress.toString()
1532                     + " ARP sender IP: "  + senderIpString
1533                     + " ARP target IP: "  + targetIpString
1534                     + " host target IP: " + targetIp.getHostAddress());
1535         }
1536         return true;
1537     }
1538 
1539     class IpAddressConflictDetectingState extends LoggingState {
1540         private int mArpProbeCount;
1541         private int mArpAnnounceCount;
1542         private Inet4Address mTargetIp;
1543         private IpConflictDetector mIpConflictDetector;
1544         private PowerManager.WakeLock mTimeoutWakeLock;
1545 
1546         private int mArpFirstProbeDelayMs;
1547         private int mArpProbeMaxDelayMs;
1548         private int mArpProbeMinDelayMs;
1549         private int mArpFirstAnnounceDelayMs;
1550         private int mArpAnnounceIntervalMs;
1551 
1552         @Override
enter()1553         public void enter() {
1554             super.enter();
1555 
1556             mArpProbeCount = 0;
1557             mArpAnnounceCount = 0;
1558 
1559             // IP address conflict detection occurs after receiving DHCPACK
1560             // message every time, i.e. we already get an available lease from
1561             // DHCP server, that ensures mDhcpLease should be NonNull, see
1562             // {@link DhcpRequestingState#receivePacket} for details.
1563             mTargetIp = (Inet4Address) mDhcpLease.ipAddress.getAddress();
1564             mIpConflictDetector = new IpConflictDetector(getHandler(), mTargetIp);
1565 
1566             // IpConflictDetector might fail to create the raw socket.
1567             if (!mIpConflictDetector.start()) {
1568                 Log.e(TAG, "Fail to start IP Conflict Detector");
1569                 transitionTo(mConfiguringInterfaceState);
1570                 return;
1571             }
1572 
1573             // Read the customized parameters from DeviceConfig.
1574             readIpConflictParametersFromDeviceConfig();
1575             if (VDBG) {
1576                 Log.d(TAG, "ARP First Probe delay: "    + mArpFirstProbeDelayMs
1577                         + " ARP Probe Max delay: "      + mArpProbeMaxDelayMs
1578                         + " ARP Probe Min delay: "      + mArpProbeMinDelayMs
1579                         + " ARP First Announce delay: " + mArpFirstAnnounceDelayMs
1580                         + " ARP Announce interval: "    + mArpAnnounceIntervalMs);
1581             }
1582 
1583             // Note that when we get here, we're still processing the WakeupMessage that caused
1584             // us to transition into this state, and thus the AlarmManager is still holding its
1585             // wakelock. That wakelock might expire as soon as this method returns.
1586             final PowerManager powerManager = mContext.getSystemService(PowerManager.class);
1587             mTimeoutWakeLock = mDependencies.getWakeLock(powerManager);
1588             mTimeoutWakeLock.acquire();
1589 
1590             // RFC5227 2.1.1 describes waiting for a random time interval between 0 and
1591             // PROBE_WAIT seconds before sending probe packets PROBE_NUM times, this delay
1592             // helps avoid hosts send initial probe packet simultaneously upon power on.
1593             // Probe packet transmission interval spaces randomly and uniformly between
1594             // PROBE_MIN and PROBE_MAX.
1595             sendMessageDelayed(CMD_ARP_PROBE, mRandom.nextInt(mArpFirstProbeDelayMs));
1596         }
1597 
1598         @Override
processMessage(Message message)1599         public boolean processMessage(Message message) {
1600             super.processMessage(message);
1601             switch (message.what) {
1602                 case CMD_ARP_PROBE:
1603                     // According to RFC5227, wait ANNOUNCE_WAIT seconds after
1604                     // the last ARP Probe, and no conflicting ARP Reply or ARP
1605                     // Probe has been received within this period, then host can
1606                     // determine the desired IP address may be used safely.
1607                     sendArpProbe();
1608                     if (++mArpProbeCount < IPV4_CONFLICT_PROBE_NUM) {
1609                         scheduleProbe();
1610                     } else {
1611                         scheduleAnnounce(mArpFirstAnnounceDelayMs);
1612                     }
1613                     return HANDLED;
1614                 case CMD_ARP_ANNOUNCEMENT:
1615                     sendArpAnnounce();
1616                     if (++mArpAnnounceCount < IPV4_CONFLICT_ANNOUNCE_NUM) {
1617                         scheduleAnnounce(mArpAnnounceIntervalMs);
1618                     } else {
1619                         transitionTo(mConfiguringInterfaceState);
1620                     }
1621                     return HANDLED;
1622                 case EVENT_IP_CONFLICT:
1623                     transitionTo(mDhcpDecliningState);
1624                     return HANDLED;
1625                 default:
1626                     return NOT_HANDLED;
1627             }
1628         }
1629 
1630         // Because the timing parameters used in IP Address detection mechanism are in
1631         // milliseconds, WakeupMessage would be too imprecise for small timeouts.
scheduleProbe()1632         private void scheduleProbe() {
1633             long timeout = mRandom.nextInt(mArpProbeMaxDelayMs - mArpProbeMinDelayMs)
1634                     + mArpProbeMinDelayMs;
1635             sendMessageDelayed(CMD_ARP_PROBE, timeout);
1636         }
1637 
scheduleAnnounce(final int timeout)1638         private void scheduleAnnounce(final int timeout) {
1639             sendMessageDelayed(CMD_ARP_ANNOUNCEMENT, timeout);
1640         }
1641 
1642         @Override
exit()1643         public void exit() {
1644             super.exit();
1645             mTimeoutWakeLock.release();
1646             mIpConflictDetector.stop();
1647             if (DBG) Log.d(TAG, "IP Conflict Detector stopped");
1648             removeMessages(CMD_ARP_PROBE);
1649             removeMessages(CMD_ARP_ANNOUNCEMENT);
1650             removeMessages(EVENT_IP_CONFLICT);
1651         }
1652 
1653         // The following timing parameters are defined in RFC5227 as fixed constants, which
1654         // are too long to adopt in the mobile network scenario, however more appropriate to
1655         // reference these fixed value as maximumValue argument to restrict the upper bound,
1656         // the minimum values of 10/20ms are used to avoid tight loops due to misconfiguration.
readIpConflictParametersFromDeviceConfig()1657         private void readIpConflictParametersFromDeviceConfig() {
1658             // PROBE_WAIT
1659             mArpFirstProbeDelayMs = mDependencies.getIntDeviceConfig(ARP_FIRST_PROBE_DELAY_MS,
1660                     10, MAX_ARP_FIRST_PROBE_DELAY_MS, DEFAULT_ARP_FIRST_PROBE_DELAY_MS);
1661 
1662             // PROBE_MIN
1663             mArpProbeMinDelayMs = mDependencies.getIntDeviceConfig(ARP_PROBE_MIN_MS, 10,
1664                     MAX_ARP_PROBE_MIN_MS, DEFAULT_ARP_PROBE_MIN_MS);
1665 
1666             // PROBE_MAX
1667             mArpProbeMaxDelayMs = Math.max(mArpProbeMinDelayMs + 1,
1668                     mDependencies.getIntDeviceConfig(ARP_PROBE_MAX_MS, 20, MAX_ARP_PROBE_MAX_MS,
1669                     DEFAULT_ARP_PROBE_MAX_MS));
1670 
1671             // ANNOUNCE_WAIT
1672             mArpFirstAnnounceDelayMs = mDependencies.getIntDeviceConfig(ARP_FIRST_ANNOUNCE_DELAY_MS,
1673                     20, MAX_ARP_FIRST_ANNOUNCE_DELAY_MS, DEFAULT_ARP_FIRST_ANNOUNCE_DELAY_MS);
1674 
1675             // ANNOUNCE_INTERVAL
1676             mArpAnnounceIntervalMs = mDependencies.getIntDeviceConfig(ARP_ANNOUNCE_INTERVAL_MS, 20,
1677                     MAX_ARP_ANNOUNCE_INTERVAL_MS, DEFAULT_ARP_ANNOUNCE_INTERVAL_MS);
1678         }
1679 
sendArpProbe()1680         private boolean sendArpProbe() {
1681             return mIpConflictDetector.transmitPacket(mTargetIp /* target IP */,
1682                     INADDR_ANY /* sender IP */, mHwAddr, mInterfaceBroadcastAddr);
1683         }
1684 
sendArpAnnounce()1685         private boolean sendArpAnnounce() {
1686             return mIpConflictDetector.transmitPacket(mTargetIp /* target IP */,
1687                     mTargetIp /* sender IP */, mHwAddr, mInterfaceBroadcastAddr);
1688         }
1689     }
1690 
1691     class DhcpBoundState extends LoggingState {
1692         @Override
enter()1693         public void enter() {
1694             super.enter();
1695             if (mDhcpLease.serverAddress != null && !connectUdpSock(mDhcpLease.serverAddress)) {
1696                 // There's likely no point in going into DhcpInitState here, we'll probably
1697                 // just repeat the transaction, get the same IP address as before, and fail.
1698                 //
1699                 // NOTE: It is observed that connectUdpSock() basically never fails, due to
1700                 // SO_BINDTODEVICE. Examining the local socket address shows it will happily
1701                 // return an IPv4 address from another interface, or even return "0.0.0.0".
1702                 //
1703                 // TODO: Consider deleting this check, following testing on several kernels.
1704                 notifyFailure();
1705                 transitionTo(mStoppedState);
1706             }
1707 
1708             scheduleLeaseTimers();
1709             logTimeToBoundState();
1710         }
1711 
1712         @Override
exit()1713         public void exit() {
1714             super.exit();
1715             mLastBoundExitTime = SystemClock.elapsedRealtime();
1716         }
1717 
1718         @Override
processMessage(Message message)1719         public boolean processMessage(Message message) {
1720             super.processMessage(message);
1721             switch (message.what) {
1722                 case CMD_RENEW_DHCP:
1723                     preDhcpTransitionTo(mWaitBeforeRenewalState, mDhcpRenewingState);
1724                     return HANDLED;
1725                 case CMD_REFRESH_LINKADDRESS:
1726                     transitionTo(mDhcpRebindingState);
1727                     return HANDLED;
1728                 default:
1729                     return NOT_HANDLED;
1730             }
1731         }
1732 
logTimeToBoundState()1733         private void logTimeToBoundState() {
1734             long now = SystemClock.elapsedRealtime();
1735             if (mLastBoundExitTime > mLastInitEnterTime) {
1736                 logState(EVENT_RENEWING_BOUND, (int) (now - mLastBoundExitTime));
1737             } else {
1738                 logState(EVENT_INITIAL_BOUND, (int) (now - mLastInitEnterTime));
1739             }
1740         }
1741     }
1742 
1743     abstract class DhcpReacquiringState extends PacketRetransmittingState {
1744         protected String mLeaseMsg;
1745 
1746         @Override
enter()1747         public void enter() {
1748             super.enter();
1749             startNewTransaction();
1750         }
1751 
packetDestination()1752         protected abstract Inet4Address packetDestination();
1753 
sendPacket()1754         protected boolean sendPacket() {
1755             return sendRequestPacket(
1756                     (Inet4Address) mDhcpLease.ipAddress.getAddress(),  // ciaddr
1757                     INADDR_ANY,                                        // DHCP_REQUESTED_IP
1758                     null,                                              // DHCP_SERVER_IDENTIFIER
1759                     packetDestination());                              // packet destination address
1760         }
1761 
receivePacket(DhcpPacket packet)1762         protected void receivePacket(DhcpPacket packet) {
1763             if (!isValidPacket(packet)) return;
1764             if ((packet instanceof DhcpAckPacket)) {
1765                 final DhcpResults results = packet.toDhcpResults();
1766                 if (results != null) {
1767                     if (!mDhcpLease.ipAddress.equals(results.ipAddress)) {
1768                         Log.d(TAG, "Renewed lease not for our current IP address!");
1769                         notifyFailure();
1770                         transitionTo(mDhcpInitState);
1771                         return;
1772                     }
1773                     setDhcpLeaseExpiry(packet);
1774                     // Updating our notion of DhcpResults here only causes the
1775                     // DNS servers and routes to be updated in LinkProperties
1776                     // in IpManager and by any overridden relevant handlers of
1777                     // the registered IpManager.Callback.  IP address changes
1778                     // are not supported here.
1779                     acceptDhcpResults(results, mLeaseMsg);
1780                     notifySuccess();
1781                     transitionTo(mDhcpBoundState);
1782                 }
1783             } else if (packet instanceof DhcpNakPacket) {
1784                 Log.d(TAG, "Received NAK, returning to INIT");
1785                 notifyFailure();
1786                 transitionTo(mDhcpInitState);
1787             }
1788         }
1789     }
1790 
1791     class DhcpRenewingState extends DhcpReacquiringState {
DhcpRenewingState()1792         public DhcpRenewingState() {
1793             mLeaseMsg = "Renewed";
1794         }
1795 
1796         @Override
processMessage(Message message)1797         public boolean processMessage(Message message) {
1798             if (super.processMessage(message) == HANDLED) {
1799                 return HANDLED;
1800             }
1801 
1802             switch (message.what) {
1803                 case CMD_REBIND_DHCP:
1804                     transitionTo(mDhcpRebindingState);
1805                     return HANDLED;
1806                 default:
1807                     return NOT_HANDLED;
1808             }
1809         }
1810 
1811         @Override
packetDestination()1812         protected Inet4Address packetDestination() {
1813             // Not specifying a SERVER_IDENTIFIER option is a violation of RFC 2131, but...
1814             // http://b/25343517 . Try to make things work anyway by using broadcast renews.
1815             return (mDhcpLease.serverAddress != null) ?
1816                     mDhcpLease.serverAddress : INADDR_BROADCAST;
1817         }
1818     }
1819 
1820     class DhcpRebindingState extends DhcpReacquiringState {
DhcpRebindingState()1821         public DhcpRebindingState() {
1822             mLeaseMsg = "Rebound";
1823         }
1824 
1825         @Override
enter()1826         public void enter() {
1827             super.enter();
1828 
1829             // We need to broadcast and possibly reconnect the socket to a
1830             // completely different server.
1831             closeSocketQuietly(mUdpSock);
1832             if (!initUdpSocket()) {
1833                 Log.e(TAG, "Failed to recreate UDP socket");
1834                 transitionTo(mDhcpInitState);
1835             }
1836         }
1837 
1838         @Override
packetDestination()1839         protected Inet4Address packetDestination() {
1840             return INADDR_BROADCAST;
1841         }
1842     }
1843 
1844     class DhcpInitRebootState extends DhcpRequestingState {
1845         @Override
enter()1846         public void enter() {
1847             mTimeout = DHCP_INITREBOOT_TIMEOUT_MS;
1848             super.enter();
1849             startNewTransaction();
1850         }
1851 
1852         // RFC 2131 4.3.2 describes generated DHCPREQUEST message during
1853         // INIT-REBOOT state:
1854         // 'server identifier' MUST NOT be filled in, 'requested IP address'
1855         // option MUST be filled in with client's notion of its previously
1856         // assigned address. 'ciaddr' MUST be zero. The client is seeking to
1857         // verify a previously allocated, cached configuration. Server SHOULD
1858         // send a DHCPNAK message to the client if the 'requested IP address'
1859         // is incorrect, or is on the wrong network.
1860         @Override
sendPacket()1861         protected boolean sendPacket() {
1862             return sendRequestPacket(
1863                     INADDR_ANY,                                        // ciaddr
1864                     mLastAssignedIpv4Address,                          // DHCP_REQUESTED_IP
1865                     null,                                              // DHCP_SERVER_IDENTIFIER
1866                     INADDR_BROADCAST);                                 // packet destination address
1867         }
1868 
1869         @Override
exit()1870         public void exit() {
1871             mLastAssignedIpv4Address = null;
1872             mLastAssignedIpv4AddressExpiry = 0;
1873         }
1874     }
1875 
1876     class DhcpRebootingState extends LoggingState {
1877     }
1878 
1879     class DhcpDecliningState extends TimeoutState {
1880         @Override
enter()1881         public void enter() {
1882             // If the host experiences MAX_CONFLICTS or more address conflicts on the
1883             // interface, configure interface with this IP address anyway.
1884             if (++mConflictCount > MAX_CONFLICTS_COUNT) {
1885                 transitionTo(mConfiguringInterfaceState);
1886                 return;
1887             }
1888 
1889             mTimeout = mDependencies.getIntDeviceConfig(DHCP_RESTART_CONFIG_DELAY, 100,
1890                     MAX_DHCP_CLIENT_RESTART_CONFIG_DELAY_MS, DEFAULT_DHCP_RESTART_CONFIG_DELAY_MS);
1891             super.enter();
1892             sendPacket();
1893         }
1894 
1895         // No need to override processMessage here since this state is
1896         // functionally identical to its superclass TimeoutState.
timeout()1897         protected void timeout() {
1898             transitionTo(mDhcpInitState);
1899         }
1900 
sendPacket()1901         private boolean sendPacket() {
1902             return sendDeclinePacket(
1903                     (Inet4Address) mDhcpLease.ipAddress.getAddress(),  // requested IP
1904                     (Inet4Address) mDhcpLease.serverAddress);          // serverIdentifier
1905         }
1906     }
1907 
logState(String name, int durationMs)1908     private void logState(String name, int durationMs) {
1909         final DhcpClientEvent event = new DhcpClientEvent.Builder()
1910                 .setMsg(name)
1911                 .setDurationMs(durationMs)
1912                 .build();
1913         mMetricsLog.log(mIfaceName, event);
1914     }
1915 }
1916