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