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