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