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