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