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