Loading src/android/net/dhcp/DhcpAckPacket.java +5 −4 Original line number Diff line number Diff line Loading @@ -46,10 +46,11 @@ public class DhcpAckPacket extends DhcpPacket { dnsServers += dnsServer.toString() + " "; } return s + " ACK: your new IP " + mYourIp + ", netmask " + mSubnetMask + ", gateways " + mGateways + dnsServers + ", lease time " + mLeaseTime; return s + " ACK: your new IP " + mYourIp + ", netmask " + mSubnetMask + ", gateways " + mGateways + dnsServers + ", lease time " + mLeaseTime + (mIpv6OnlyWaitTime != null ? ", V6ONLY_WAIT " + mIpv6OnlyWaitTime : ""); } /** Loading src/android/net/dhcp/DhcpClient.java +86 −13 Original line number Diff line number Diff line Loading @@ -20,6 +20,7 @@ import static android.net.dhcp.DhcpPacket.DHCP_BROADCAST_ADDRESS; import static android.net.dhcp.DhcpPacket.DHCP_CAPTIVE_PORTAL; import static android.net.dhcp.DhcpPacket.DHCP_DNS_SERVER; import static android.net.dhcp.DhcpPacket.DHCP_DOMAIN_NAME; import static android.net.dhcp.DhcpPacket.DHCP_IPV6_ONLY_PREFERRED; import static android.net.dhcp.DhcpPacket.DHCP_LEASE_TIME; import static android.net.dhcp.DhcpPacket.DHCP_MTU; import static android.net.dhcp.DhcpPacket.DHCP_REBINDING_TIME; Loading @@ -31,6 +32,7 @@ import static android.net.dhcp.DhcpPacket.INADDR_ANY; import static android.net.dhcp.DhcpPacket.INADDR_BROADCAST; import static android.net.dhcp.DhcpPacket.INFINITE_LEASE; import static android.net.util.NetworkStackUtils.DHCP_INIT_REBOOT_VERSION; import static android.net.util.NetworkStackUtils.DHCP_IPV6_ONLY_PREFERRED_VERSION; import static android.net.util.NetworkStackUtils.DHCP_IP_CONFLICT_DETECT_VERSION; import static android.net.util.NetworkStackUtils.DHCP_RAPID_COMMIT_VERSION; import static android.net.util.NetworkStackUtils.closeSocketQuietly; Loading Loading @@ -104,6 +106,7 @@ import com.android.networkstack.apishim.common.ShimUtils; import com.android.networkstack.arp.ArpPacket; import com.android.networkstack.metrics.IpProvisioningMetrics; import java.io.ByteArrayOutputStream; import java.io.FileDescriptor; import java.io.IOException; import java.net.Inet4Address; Loading Loading @@ -244,6 +247,7 @@ public class DhcpClient extends StateMachine { /* Message.arg1 arguments to CMD_POST_DHCP_ACTION notification */ public static final int DHCP_SUCCESS = 1; public static final int DHCP_FAILURE = 2; public static final int DHCP_IPV6_ONLY = 3; // Internal messages. private static final int PRIVATE_BASE = IpClient.DHCPCLIENT_CMD_BASE + 100; Loading Loading @@ -287,13 +291,18 @@ public class DhcpClient extends StateMachine { @NonNull private byte[] getRequestedParams() { // Set an initial size large enough for all optional parameters that we might request. final int numOptionalParams = 2; final ByteArrayOutputStream params = new ByteArrayOutputStream(DEFAULT_REQUESTED_PARAMS.length + numOptionalParams); params.write(DEFAULT_REQUESTED_PARAMS, 0, DEFAULT_REQUESTED_PARAMS.length); if (isCapportApiEnabled()) { final byte[] params = Arrays.copyOf(DEFAULT_REQUESTED_PARAMS, DEFAULT_REQUESTED_PARAMS.length + 1); params[params.length - 1] = DHCP_CAPTIVE_PORTAL; return params; params.write(DHCP_CAPTIVE_PORTAL); } return DEFAULT_REQUESTED_PARAMS; if (isIPv6OnlyPreferredModeEnabled()) { params.write(DHCP_IPV6_ONLY_PREFERRED); } return params.toByteArray(); } private static boolean isCapportApiEnabled() { Loading Loading @@ -338,10 +347,10 @@ public class DhcpClient extends StateMachine { private int mConflictCount; private long mLastAssignedIpv4AddressExpiry; private Dependencies mDependencies; @NonNull private final NetworkStackIpMemoryStore mIpMemoryStore; @Nullable private DhcpPacketHandler mDhcpPacketHandler; @NonNull private final NetworkStackIpMemoryStore mIpMemoryStore; @Nullable private final String mHostname; Loading @@ -349,6 +358,10 @@ public class DhcpClient extends StateMachine { private long mLastInitEnterTime; private long mLastBoundExitTime; // 32-bit unsigned integer used to indicate the number of milliseconds the DHCP client should // disable DHCPv4. private long mIpv6OnlyWaitTimeMs; // States. private State mStoppedState = new StoppedState(); private State mDhcpState = new DhcpState(); Loading @@ -370,6 +383,7 @@ public class DhcpClient extends StateMachine { new WaitBeforeObtainingConfigurationState(mObtainingConfigurationState); private State mIpAddressConflictDetectingState = new IpAddressConflictDetectingState(); private State mDhcpDecliningState = new DhcpDecliningState(); private State mIpv6OnlyWaitState = new Ipv6OnlyWaitState(); private WakeupMessage makeWakeupMessage(String cmdName, int cmd) { cmdName = DhcpClient.class.getSimpleName() + "." + mIfaceName + "." + cmdName; Loading Loading @@ -470,6 +484,7 @@ public class DhcpClient extends StateMachine { addState(mDhcpSelectingState, mDhcpState); addState(mDhcpRequestingState, mDhcpState); addState(mIpAddressConflictDetectingState, mDhcpState); addState(mIpv6OnlyWaitState, mDhcpState); addState(mDhcpHaveLeaseState, mDhcpState); addState(mConfiguringInterfaceState, mDhcpHaveLeaseState); addState(mDhcpBoundState, mDhcpHaveLeaseState); Loading Loading @@ -544,6 +559,14 @@ public class DhcpClient extends StateMachine { false /* defaultEnabled */); } /** * check whether or not to support IPv6-only preferred option. */ public boolean isIPv6OnlyPreferredModeEnabled() { return mDependencies.isFeatureEnabled(mContext, DHCP_IPV6_ONLY_PREFERRED_VERSION, false /* defaultEnabled */); } private void recordMetricEnabledFeatures() { if (isDhcpLeaseCacheEnabled()) mMetrics.setDhcpEnabledFeature(DhcpFeature.DF_INITREBOOT); if (isDhcpRapidCommitEnabled()) mMetrics.setDhcpEnabledFeature(DhcpFeature.DF_RAPIDCOMMIT); Loading Loading @@ -605,6 +628,13 @@ public class DhcpClient extends StateMachine { } } private byte[] getOptionsToSkip() { final ByteArrayOutputStream optionsToSkip = new ByteArrayOutputStream(2); if (!isCapportApiEnabled()) optionsToSkip.write(DHCP_CAPTIVE_PORTAL); if (!isIPv6OnlyPreferredModeEnabled()) optionsToSkip.write(DHCP_IPV6_ONLY_PREFERRED); return optionsToSkip.toByteArray(); } private class DhcpPacketHandler extends PacketReader { private FileDescriptor mPacketSock; Loading @@ -615,10 +645,8 @@ public class DhcpClient extends StateMachine { @Override protected void handlePacket(byte[] recvbuf, int length) { try { final byte[] optionsToSkip = isCapportApiEnabled() ? new byte[0] : new byte[] { DHCP_CAPTIVE_PORTAL }; final DhcpPacket packet = DhcpPacket.decodeFullPacket(recvbuf, length, DhcpPacket.ENCAP_L2, optionsToSkip); DhcpPacket.ENCAP_L2, getOptionsToSkip()); if (DBG) Log.d(TAG, "Received packet: " + packet); sendMessage(CMD_RECEIVED_PACKET, packet); } catch (DhcpPacket.ParseException e) { Loading Loading @@ -944,10 +972,11 @@ public class DhcpClient extends StateMachine { // This is part of the initial configuration because it is passed in on startup and // never updated. // TODO: decide what to do about L2 key changes while the client is connected. @Nullable public final String l2Key; public final boolean isPreconnectionEnabled; public Configuration(String l2Key, boolean isPreconnectionEnabled) { public Configuration(@Nullable final String l2Key, final boolean isPreconnectionEnabled) { this.l2Key = l2Key; this.isPreconnectionEnabled = isPreconnectionEnabled; } Loading Loading @@ -1052,7 +1081,7 @@ public class DhcpClient extends StateMachine { } abstract class TimeoutState extends LoggingState { protected int mTimeout = 0; protected long mTimeout = 0; @Override public void enter() { Loading Loading @@ -1223,6 +1252,15 @@ public class DhcpClient extends StateMachine { } } private boolean maybeTransitionToIpv6OnlyWaitState(@NonNull final DhcpPacket packet) { if (!isIPv6OnlyPreferredModeEnabled()) return false; if (packet.getIpv6OnlyWaitTimeMillis() == DhcpPacket.V6ONLY_PREFERRED_ABSENCE) return false; mIpv6OnlyWaitTimeMs = packet.getIpv6OnlyWaitTimeMillis(); transitionTo(mIpv6OnlyWaitState); return true; } private void receiveOfferOrAckPacket(final DhcpPacket packet, final boolean acceptRapidCommit) { if (!isValidPacket(packet)) return; Loading @@ -1230,6 +1268,9 @@ public class DhcpClient extends StateMachine { // 2. received the DHCPACK packet from DHCP Servers that support Rapid // Commit option, process it by following RFC4039. if (packet instanceof DhcpOfferPacket) { if (maybeTransitionToIpv6OnlyWaitState(packet)) { return; } mOffer = packet.toDhcpResults(); if (mOffer != null) { Log.d(TAG, "Got pending lease: " + mOffer); Loading Loading @@ -1362,7 +1403,10 @@ public class DhcpClient extends StateMachine { protected void receivePacket(DhcpPacket packet) { if (!isValidPacket(packet)) return; if ((packet instanceof DhcpAckPacket)) { DhcpResults results = packet.toDhcpResults(); if (maybeTransitionToIpv6OnlyWaitState(packet)) { return; } final DhcpResults results = packet.toDhcpResults(); if (results != null) { confirmDhcpLease(packet, results); transitionTo(isDhcpIpConflictDetectEnabled() Loading Loading @@ -1762,6 +1806,9 @@ public class DhcpClient extends StateMachine { protected void receivePacket(DhcpPacket packet) { if (!isValidPacket(packet)) return; if ((packet instanceof DhcpAckPacket)) { if (maybeTransitionToIpv6OnlyWaitState(packet)) { return; } final DhcpResults results = packet.toDhcpResults(); if (results != null) { if (!mDhcpLease.ipAddress.equals(results.ipAddress)) { Loading Loading @@ -1905,6 +1952,32 @@ public class DhcpClient extends StateMachine { } } // This state is used for IPv6-only preferred mode defined in the draft-ietf-dhc-v6only. // For IPv6-only capable host, it will forgo obtaining an IPv4 address for V6ONLY_WAIT // period if the network indicates that it can provide IPv6 connectivity by replying // with a valid IPv6-only preferred option in the DHCPOFFER or DHCPACK. class Ipv6OnlyWaitState extends TimeoutState { @Override public void enter() { mTimeout = mIpv6OnlyWaitTimeMs; super.enter(); // Restore power save and suspend optimization if it was disabled before. if (mRegisteredForPreDhcpNotification) { mController.sendMessage(CMD_POST_DHCP_ACTION, DHCP_IPV6_ONLY, 0, null); } } @Override public void exit() { mIpv6OnlyWaitTimeMs = 0; } protected void timeout() { startInitRebootOrInit(); } } private void logState(String name, int durationMs) { final DhcpClientEvent event = new DhcpClientEvent.Builder() .setMsg(name) Loading src/android/net/dhcp/DhcpOfferPacket.java +6 −3 Original line number Diff line number Diff line Loading @@ -47,9 +47,12 @@ public class DhcpOfferPacket extends DhcpPacket { } } return s + " OFFER, ip " + mYourIp + ", mask " + mSubnetMask + dnsServers + ", gateways " + mGateways + " lease time " + mLeaseTime + ", domain " + mDomainName; return s + " OFFER, ip " + mYourIp + ", mask " + mSubnetMask + dnsServers + ", gateways " + mGateways + ", lease time " + mLeaseTime + ", domain " + mDomainName + (mIpv6OnlyWaitTime != null ? ", V6ONLY_WAIT " + mIpv6OnlyWaitTime : ""); } /** Loading src/android/net/dhcp/DhcpPacket.java +69 −4 Original line number Diff line number Diff line Loading @@ -88,6 +88,10 @@ public abstract class DhcpPacket { public static final int HWADDR_LEN = 16; public static final int MAX_OPTION_LEN = 255; // The lower boundary for V6ONLY_WAIT. public static final long MIN_V6ONLY_WAIT_MS = 300_000; public static final long V6ONLY_PREFERRED_ABSENCE = -1L; /** * The minimum and maximum MTU that we are prepared to use. We set the minimum to the minimum * IPv6 MTU because the IPv6 stack enters unusual codepaths when the link MTU drops below 1280, Loading Loading @@ -307,6 +311,16 @@ public abstract class DhcpPacket { public static final byte DHCP_RAPID_COMMIT = 80; protected boolean mRapidCommit; /** * DHCP IPv6-Only Preferred Option(draft-ietf-dhc-v6only). * Indicate that a host supports an IPv6-only mode and willing to forgo obtaining an IPv4 * address for V6ONLY_WAIT period if the network provides IPv6 connectivity. V6ONLY_WAIT * is 32-bit unsigned integer, so the Integer value cannot be used as-is. */ public static final byte DHCP_IPV6_ONLY_PREFERRED = (byte) 108; @VisibleForTesting(otherwise = VisibleForTesting.PROTECTED) public Integer mIpv6OnlyWaitTime; public static final byte DHCP_CAPTIVE_PORTAL = (byte) 114; protected String mCaptivePortalUrl; Loading Loading @@ -789,6 +803,9 @@ public abstract class DhcpPacket { if (mMtu != null && Short.toUnsignedInt(mMtu) >= IPV4_MIN_MTU) { addTlv(buf, DHCP_MTU, mMtu); } if (mIpv6OnlyWaitTime != null) { addTlv(buf, DHCP_IPV6_ONLY_PREFERRED, (int) Integer.toUnsignedLong(mIpv6OnlyWaitTime)); } addTlv(buf, DHCP_CAPTIVE_PORTAL, mCaptivePortalUrl); } Loading Loading @@ -942,6 +959,7 @@ public abstract class DhcpPacket { Integer leaseTime = null; Integer T1 = null; Integer T2 = null; Integer ipv6OnlyWaitTime = null; // dhcp options byte dhcpType = (byte) 0xFF; Loading Loading @@ -1204,6 +1222,10 @@ public abstract class DhcpPacket { expectedLen = optionLen; captivePortalUrl = readAsciiString(packet, optionLen, true); break; case DHCP_IPV6_ONLY_PREFERRED: expectedLen = 4; ipv6OnlyWaitTime = Integer.valueOf(packet.getInt()); break; default: expectedLen = skipOption(packet, optionLen); } Loading Loading @@ -1292,6 +1314,7 @@ public abstract class DhcpPacket { newPacket.mVendorId = vendorId; newPacket.mVendorInfo = vendorInfo; newPacket.mCaptivePortalUrl = captivePortalUrl; newPacket.mIpv6OnlyWaitTime = ipv6OnlyWaitTime; if ((optionOverload & OPTION_OVERLOAD_SNAME) == 0) { newPacket.mServerHostName = serverHostName; } else { Loading Loading @@ -1384,6 +1407,14 @@ public abstract class DhcpPacket { } } /** * Returns the IPv6-only wait time, in milliseconds, or -1 if the option is not present. */ public long getIpv6OnlyWaitTimeMillis() { if (mIpv6OnlyWaitTime == null) return V6ONLY_PREFERRED_ABSENCE; return Math.max(MIN_V6ONLY_WAIT_MS, Integer.toUnsignedLong(mIpv6OnlyWaitTime) * 1000); } /** * Builds a DHCP-DISCOVER packet from the required specified * parameters. Loading @@ -1399,15 +1430,14 @@ public abstract class DhcpPacket { } /** * Builds a DHCP-OFFER packet from the required specified * parameters. * Builds a DHCP-OFFER packet from the required specified parameters. */ public static ByteBuffer buildOfferPacket(int encap, int transactionId, boolean broadcast, Inet4Address serverIpAddr, Inet4Address relayIp, Inet4Address yourIp, byte[] mac, Integer timeout, Inet4Address netMask, Inet4Address bcAddr, List<Inet4Address> gateways, List<Inet4Address> dnsServers, Inet4Address dhcpServerIdentifier, String domainName, String hostname, boolean metered, short mtu, String captivePortalUrl) { short mtu, String captivePortalUrl, Integer ipv6OnlyWaitTime) { DhcpPacket pkt = new DhcpOfferPacket( transactionId, (short) 0, broadcast, serverIpAddr, relayIp, INADDR_ANY /* clientIp */, yourIp, mac); Loading @@ -1424,9 +1454,26 @@ public abstract class DhcpPacket { if (metered) { pkt.mVendorInfo = VENDOR_INFO_ANDROID_METERED; } if (ipv6OnlyWaitTime != null) { pkt.mIpv6OnlyWaitTime = ipv6OnlyWaitTime; } return pkt.buildPacket(encap, DHCP_CLIENT, DHCP_SERVER); } /** * Builds a DHCP-OFFER packet from the required specified parameters. */ public static ByteBuffer buildOfferPacket(int encap, int transactionId, boolean broadcast, Inet4Address serverIpAddr, Inet4Address relayIp, Inet4Address yourIp, byte[] mac, Integer timeout, Inet4Address netMask, Inet4Address bcAddr, List<Inet4Address> gateways, List<Inet4Address> dnsServers, Inet4Address dhcpServerIdentifier, String domainName, String hostname, boolean metered, short mtu, String captivePortalUrl) { return buildOfferPacket(encap, transactionId, broadcast, serverIpAddr, relayIp, yourIp, mac, timeout, netMask, bcAddr, gateways, dnsServers, dhcpServerIdentifier, domainName, hostname, metered, mtu, captivePortalUrl, null /* V6ONLY_WAIT */); } /** * Builds a DHCP-ACK packet from the required specified parameters. */ Loading @@ -1435,7 +1482,7 @@ public abstract class DhcpPacket { Inet4Address requestClientIp, byte[] mac, Integer timeout, Inet4Address netMask, Inet4Address bcAddr, List<Inet4Address> gateways, List<Inet4Address> dnsServers, Inet4Address dhcpServerIdentifier, String domainName, String hostname, boolean metered, short mtu, boolean rapidCommit, String captivePortalUrl) { short mtu, boolean rapidCommit, String captivePortalUrl, Integer ipv6OnlyWaitTime) { DhcpPacket pkt = new DhcpAckPacket( transactionId, (short) 0, broadcast, serverIpAddr, relayIp, requestClientIp, yourIp, mac, rapidCommit); Loading @@ -1452,9 +1499,27 @@ public abstract class DhcpPacket { if (metered) { pkt.mVendorInfo = VENDOR_INFO_ANDROID_METERED; } if (ipv6OnlyWaitTime != null) { pkt.mIpv6OnlyWaitTime = ipv6OnlyWaitTime; } return pkt.buildPacket(encap, DHCP_CLIENT, DHCP_SERVER); } /** * Builds a DHCP-ACK packet from the required specified parameters. */ public static ByteBuffer buildAckPacket(int encap, int transactionId, boolean broadcast, Inet4Address serverIpAddr, Inet4Address relayIp, Inet4Address yourIp, Inet4Address requestClientIp, byte[] mac, Integer timeout, Inet4Address netMask, Inet4Address bcAddr, List<Inet4Address> gateways, List<Inet4Address> dnsServers, Inet4Address dhcpServerIdentifier, String domainName, String hostname, boolean metered, short mtu, boolean rapidCommit, String captivePortalUrl) { return buildAckPacket(encap, transactionId, broadcast, serverIpAddr, relayIp, yourIp, requestClientIp, mac, timeout, netMask, bcAddr, gateways, dnsServers, dhcpServerIdentifier, domainName, hostname, metered, mtu, rapidCommit, captivePortalUrl, null /* V6ONLY_WAIT */); } /** * Builds a DHCP-NAK packet from the required specified parameters. */ Loading src/android/net/ip/IpClient.java +4 −1 Original line number Diff line number Diff line Loading @@ -2169,7 +2169,8 @@ public class IpClient extends StateMachine { // a) initial address acquisition succeeds, // b) renew succeeds or is NAK'd, // c) rebind succeeds or is NAK'd, or // c) the lease expires, // d) the lease expires, or // e) the IPv6-only preferred option is enabled and entering Ipv6OnlyWaitState. // // but never when initial address acquisition fails. The latter // condition is now governed by the provisioning timeout. Loading @@ -2183,6 +2184,8 @@ public class IpClient extends StateMachine { case DhcpClient.DHCP_FAILURE: handleIPv4Failure(); break; case DhcpClient.DHCP_IPV6_ONLY: break; default: logError("Unknown CMD_POST_DHCP_ACTION status: %s", msg.arg1); } Loading Loading
src/android/net/dhcp/DhcpAckPacket.java +5 −4 Original line number Diff line number Diff line Loading @@ -46,10 +46,11 @@ public class DhcpAckPacket extends DhcpPacket { dnsServers += dnsServer.toString() + " "; } return s + " ACK: your new IP " + mYourIp + ", netmask " + mSubnetMask + ", gateways " + mGateways + dnsServers + ", lease time " + mLeaseTime; return s + " ACK: your new IP " + mYourIp + ", netmask " + mSubnetMask + ", gateways " + mGateways + dnsServers + ", lease time " + mLeaseTime + (mIpv6OnlyWaitTime != null ? ", V6ONLY_WAIT " + mIpv6OnlyWaitTime : ""); } /** Loading
src/android/net/dhcp/DhcpClient.java +86 −13 Original line number Diff line number Diff line Loading @@ -20,6 +20,7 @@ import static android.net.dhcp.DhcpPacket.DHCP_BROADCAST_ADDRESS; import static android.net.dhcp.DhcpPacket.DHCP_CAPTIVE_PORTAL; import static android.net.dhcp.DhcpPacket.DHCP_DNS_SERVER; import static android.net.dhcp.DhcpPacket.DHCP_DOMAIN_NAME; import static android.net.dhcp.DhcpPacket.DHCP_IPV6_ONLY_PREFERRED; import static android.net.dhcp.DhcpPacket.DHCP_LEASE_TIME; import static android.net.dhcp.DhcpPacket.DHCP_MTU; import static android.net.dhcp.DhcpPacket.DHCP_REBINDING_TIME; Loading @@ -31,6 +32,7 @@ import static android.net.dhcp.DhcpPacket.INADDR_ANY; import static android.net.dhcp.DhcpPacket.INADDR_BROADCAST; import static android.net.dhcp.DhcpPacket.INFINITE_LEASE; import static android.net.util.NetworkStackUtils.DHCP_INIT_REBOOT_VERSION; import static android.net.util.NetworkStackUtils.DHCP_IPV6_ONLY_PREFERRED_VERSION; import static android.net.util.NetworkStackUtils.DHCP_IP_CONFLICT_DETECT_VERSION; import static android.net.util.NetworkStackUtils.DHCP_RAPID_COMMIT_VERSION; import static android.net.util.NetworkStackUtils.closeSocketQuietly; Loading Loading @@ -104,6 +106,7 @@ import com.android.networkstack.apishim.common.ShimUtils; import com.android.networkstack.arp.ArpPacket; import com.android.networkstack.metrics.IpProvisioningMetrics; import java.io.ByteArrayOutputStream; import java.io.FileDescriptor; import java.io.IOException; import java.net.Inet4Address; Loading Loading @@ -244,6 +247,7 @@ public class DhcpClient extends StateMachine { /* Message.arg1 arguments to CMD_POST_DHCP_ACTION notification */ public static final int DHCP_SUCCESS = 1; public static final int DHCP_FAILURE = 2; public static final int DHCP_IPV6_ONLY = 3; // Internal messages. private static final int PRIVATE_BASE = IpClient.DHCPCLIENT_CMD_BASE + 100; Loading Loading @@ -287,13 +291,18 @@ public class DhcpClient extends StateMachine { @NonNull private byte[] getRequestedParams() { // Set an initial size large enough for all optional parameters that we might request. final int numOptionalParams = 2; final ByteArrayOutputStream params = new ByteArrayOutputStream(DEFAULT_REQUESTED_PARAMS.length + numOptionalParams); params.write(DEFAULT_REQUESTED_PARAMS, 0, DEFAULT_REQUESTED_PARAMS.length); if (isCapportApiEnabled()) { final byte[] params = Arrays.copyOf(DEFAULT_REQUESTED_PARAMS, DEFAULT_REQUESTED_PARAMS.length + 1); params[params.length - 1] = DHCP_CAPTIVE_PORTAL; return params; params.write(DHCP_CAPTIVE_PORTAL); } return DEFAULT_REQUESTED_PARAMS; if (isIPv6OnlyPreferredModeEnabled()) { params.write(DHCP_IPV6_ONLY_PREFERRED); } return params.toByteArray(); } private static boolean isCapportApiEnabled() { Loading Loading @@ -338,10 +347,10 @@ public class DhcpClient extends StateMachine { private int mConflictCount; private long mLastAssignedIpv4AddressExpiry; private Dependencies mDependencies; @NonNull private final NetworkStackIpMemoryStore mIpMemoryStore; @Nullable private DhcpPacketHandler mDhcpPacketHandler; @NonNull private final NetworkStackIpMemoryStore mIpMemoryStore; @Nullable private final String mHostname; Loading @@ -349,6 +358,10 @@ public class DhcpClient extends StateMachine { private long mLastInitEnterTime; private long mLastBoundExitTime; // 32-bit unsigned integer used to indicate the number of milliseconds the DHCP client should // disable DHCPv4. private long mIpv6OnlyWaitTimeMs; // States. private State mStoppedState = new StoppedState(); private State mDhcpState = new DhcpState(); Loading @@ -370,6 +383,7 @@ public class DhcpClient extends StateMachine { new WaitBeforeObtainingConfigurationState(mObtainingConfigurationState); private State mIpAddressConflictDetectingState = new IpAddressConflictDetectingState(); private State mDhcpDecliningState = new DhcpDecliningState(); private State mIpv6OnlyWaitState = new Ipv6OnlyWaitState(); private WakeupMessage makeWakeupMessage(String cmdName, int cmd) { cmdName = DhcpClient.class.getSimpleName() + "." + mIfaceName + "." + cmdName; Loading Loading @@ -470,6 +484,7 @@ public class DhcpClient extends StateMachine { addState(mDhcpSelectingState, mDhcpState); addState(mDhcpRequestingState, mDhcpState); addState(mIpAddressConflictDetectingState, mDhcpState); addState(mIpv6OnlyWaitState, mDhcpState); addState(mDhcpHaveLeaseState, mDhcpState); addState(mConfiguringInterfaceState, mDhcpHaveLeaseState); addState(mDhcpBoundState, mDhcpHaveLeaseState); Loading Loading @@ -544,6 +559,14 @@ public class DhcpClient extends StateMachine { false /* defaultEnabled */); } /** * check whether or not to support IPv6-only preferred option. */ public boolean isIPv6OnlyPreferredModeEnabled() { return mDependencies.isFeatureEnabled(mContext, DHCP_IPV6_ONLY_PREFERRED_VERSION, false /* defaultEnabled */); } private void recordMetricEnabledFeatures() { if (isDhcpLeaseCacheEnabled()) mMetrics.setDhcpEnabledFeature(DhcpFeature.DF_INITREBOOT); if (isDhcpRapidCommitEnabled()) mMetrics.setDhcpEnabledFeature(DhcpFeature.DF_RAPIDCOMMIT); Loading Loading @@ -605,6 +628,13 @@ public class DhcpClient extends StateMachine { } } private byte[] getOptionsToSkip() { final ByteArrayOutputStream optionsToSkip = new ByteArrayOutputStream(2); if (!isCapportApiEnabled()) optionsToSkip.write(DHCP_CAPTIVE_PORTAL); if (!isIPv6OnlyPreferredModeEnabled()) optionsToSkip.write(DHCP_IPV6_ONLY_PREFERRED); return optionsToSkip.toByteArray(); } private class DhcpPacketHandler extends PacketReader { private FileDescriptor mPacketSock; Loading @@ -615,10 +645,8 @@ public class DhcpClient extends StateMachine { @Override protected void handlePacket(byte[] recvbuf, int length) { try { final byte[] optionsToSkip = isCapportApiEnabled() ? new byte[0] : new byte[] { DHCP_CAPTIVE_PORTAL }; final DhcpPacket packet = DhcpPacket.decodeFullPacket(recvbuf, length, DhcpPacket.ENCAP_L2, optionsToSkip); DhcpPacket.ENCAP_L2, getOptionsToSkip()); if (DBG) Log.d(TAG, "Received packet: " + packet); sendMessage(CMD_RECEIVED_PACKET, packet); } catch (DhcpPacket.ParseException e) { Loading Loading @@ -944,10 +972,11 @@ public class DhcpClient extends StateMachine { // This is part of the initial configuration because it is passed in on startup and // never updated. // TODO: decide what to do about L2 key changes while the client is connected. @Nullable public final String l2Key; public final boolean isPreconnectionEnabled; public Configuration(String l2Key, boolean isPreconnectionEnabled) { public Configuration(@Nullable final String l2Key, final boolean isPreconnectionEnabled) { this.l2Key = l2Key; this.isPreconnectionEnabled = isPreconnectionEnabled; } Loading Loading @@ -1052,7 +1081,7 @@ public class DhcpClient extends StateMachine { } abstract class TimeoutState extends LoggingState { protected int mTimeout = 0; protected long mTimeout = 0; @Override public void enter() { Loading Loading @@ -1223,6 +1252,15 @@ public class DhcpClient extends StateMachine { } } private boolean maybeTransitionToIpv6OnlyWaitState(@NonNull final DhcpPacket packet) { if (!isIPv6OnlyPreferredModeEnabled()) return false; if (packet.getIpv6OnlyWaitTimeMillis() == DhcpPacket.V6ONLY_PREFERRED_ABSENCE) return false; mIpv6OnlyWaitTimeMs = packet.getIpv6OnlyWaitTimeMillis(); transitionTo(mIpv6OnlyWaitState); return true; } private void receiveOfferOrAckPacket(final DhcpPacket packet, final boolean acceptRapidCommit) { if (!isValidPacket(packet)) return; Loading @@ -1230,6 +1268,9 @@ public class DhcpClient extends StateMachine { // 2. received the DHCPACK packet from DHCP Servers that support Rapid // Commit option, process it by following RFC4039. if (packet instanceof DhcpOfferPacket) { if (maybeTransitionToIpv6OnlyWaitState(packet)) { return; } mOffer = packet.toDhcpResults(); if (mOffer != null) { Log.d(TAG, "Got pending lease: " + mOffer); Loading Loading @@ -1362,7 +1403,10 @@ public class DhcpClient extends StateMachine { protected void receivePacket(DhcpPacket packet) { if (!isValidPacket(packet)) return; if ((packet instanceof DhcpAckPacket)) { DhcpResults results = packet.toDhcpResults(); if (maybeTransitionToIpv6OnlyWaitState(packet)) { return; } final DhcpResults results = packet.toDhcpResults(); if (results != null) { confirmDhcpLease(packet, results); transitionTo(isDhcpIpConflictDetectEnabled() Loading Loading @@ -1762,6 +1806,9 @@ public class DhcpClient extends StateMachine { protected void receivePacket(DhcpPacket packet) { if (!isValidPacket(packet)) return; if ((packet instanceof DhcpAckPacket)) { if (maybeTransitionToIpv6OnlyWaitState(packet)) { return; } final DhcpResults results = packet.toDhcpResults(); if (results != null) { if (!mDhcpLease.ipAddress.equals(results.ipAddress)) { Loading Loading @@ -1905,6 +1952,32 @@ public class DhcpClient extends StateMachine { } } // This state is used for IPv6-only preferred mode defined in the draft-ietf-dhc-v6only. // For IPv6-only capable host, it will forgo obtaining an IPv4 address for V6ONLY_WAIT // period if the network indicates that it can provide IPv6 connectivity by replying // with a valid IPv6-only preferred option in the DHCPOFFER or DHCPACK. class Ipv6OnlyWaitState extends TimeoutState { @Override public void enter() { mTimeout = mIpv6OnlyWaitTimeMs; super.enter(); // Restore power save and suspend optimization if it was disabled before. if (mRegisteredForPreDhcpNotification) { mController.sendMessage(CMD_POST_DHCP_ACTION, DHCP_IPV6_ONLY, 0, null); } } @Override public void exit() { mIpv6OnlyWaitTimeMs = 0; } protected void timeout() { startInitRebootOrInit(); } } private void logState(String name, int durationMs) { final DhcpClientEvent event = new DhcpClientEvent.Builder() .setMsg(name) Loading
src/android/net/dhcp/DhcpOfferPacket.java +6 −3 Original line number Diff line number Diff line Loading @@ -47,9 +47,12 @@ public class DhcpOfferPacket extends DhcpPacket { } } return s + " OFFER, ip " + mYourIp + ", mask " + mSubnetMask + dnsServers + ", gateways " + mGateways + " lease time " + mLeaseTime + ", domain " + mDomainName; return s + " OFFER, ip " + mYourIp + ", mask " + mSubnetMask + dnsServers + ", gateways " + mGateways + ", lease time " + mLeaseTime + ", domain " + mDomainName + (mIpv6OnlyWaitTime != null ? ", V6ONLY_WAIT " + mIpv6OnlyWaitTime : ""); } /** Loading
src/android/net/dhcp/DhcpPacket.java +69 −4 Original line number Diff line number Diff line Loading @@ -88,6 +88,10 @@ public abstract class DhcpPacket { public static final int HWADDR_LEN = 16; public static final int MAX_OPTION_LEN = 255; // The lower boundary for V6ONLY_WAIT. public static final long MIN_V6ONLY_WAIT_MS = 300_000; public static final long V6ONLY_PREFERRED_ABSENCE = -1L; /** * The minimum and maximum MTU that we are prepared to use. We set the minimum to the minimum * IPv6 MTU because the IPv6 stack enters unusual codepaths when the link MTU drops below 1280, Loading Loading @@ -307,6 +311,16 @@ public abstract class DhcpPacket { public static final byte DHCP_RAPID_COMMIT = 80; protected boolean mRapidCommit; /** * DHCP IPv6-Only Preferred Option(draft-ietf-dhc-v6only). * Indicate that a host supports an IPv6-only mode and willing to forgo obtaining an IPv4 * address for V6ONLY_WAIT period if the network provides IPv6 connectivity. V6ONLY_WAIT * is 32-bit unsigned integer, so the Integer value cannot be used as-is. */ public static final byte DHCP_IPV6_ONLY_PREFERRED = (byte) 108; @VisibleForTesting(otherwise = VisibleForTesting.PROTECTED) public Integer mIpv6OnlyWaitTime; public static final byte DHCP_CAPTIVE_PORTAL = (byte) 114; protected String mCaptivePortalUrl; Loading Loading @@ -789,6 +803,9 @@ public abstract class DhcpPacket { if (mMtu != null && Short.toUnsignedInt(mMtu) >= IPV4_MIN_MTU) { addTlv(buf, DHCP_MTU, mMtu); } if (mIpv6OnlyWaitTime != null) { addTlv(buf, DHCP_IPV6_ONLY_PREFERRED, (int) Integer.toUnsignedLong(mIpv6OnlyWaitTime)); } addTlv(buf, DHCP_CAPTIVE_PORTAL, mCaptivePortalUrl); } Loading Loading @@ -942,6 +959,7 @@ public abstract class DhcpPacket { Integer leaseTime = null; Integer T1 = null; Integer T2 = null; Integer ipv6OnlyWaitTime = null; // dhcp options byte dhcpType = (byte) 0xFF; Loading Loading @@ -1204,6 +1222,10 @@ public abstract class DhcpPacket { expectedLen = optionLen; captivePortalUrl = readAsciiString(packet, optionLen, true); break; case DHCP_IPV6_ONLY_PREFERRED: expectedLen = 4; ipv6OnlyWaitTime = Integer.valueOf(packet.getInt()); break; default: expectedLen = skipOption(packet, optionLen); } Loading Loading @@ -1292,6 +1314,7 @@ public abstract class DhcpPacket { newPacket.mVendorId = vendorId; newPacket.mVendorInfo = vendorInfo; newPacket.mCaptivePortalUrl = captivePortalUrl; newPacket.mIpv6OnlyWaitTime = ipv6OnlyWaitTime; if ((optionOverload & OPTION_OVERLOAD_SNAME) == 0) { newPacket.mServerHostName = serverHostName; } else { Loading Loading @@ -1384,6 +1407,14 @@ public abstract class DhcpPacket { } } /** * Returns the IPv6-only wait time, in milliseconds, or -1 if the option is not present. */ public long getIpv6OnlyWaitTimeMillis() { if (mIpv6OnlyWaitTime == null) return V6ONLY_PREFERRED_ABSENCE; return Math.max(MIN_V6ONLY_WAIT_MS, Integer.toUnsignedLong(mIpv6OnlyWaitTime) * 1000); } /** * Builds a DHCP-DISCOVER packet from the required specified * parameters. Loading @@ -1399,15 +1430,14 @@ public abstract class DhcpPacket { } /** * Builds a DHCP-OFFER packet from the required specified * parameters. * Builds a DHCP-OFFER packet from the required specified parameters. */ public static ByteBuffer buildOfferPacket(int encap, int transactionId, boolean broadcast, Inet4Address serverIpAddr, Inet4Address relayIp, Inet4Address yourIp, byte[] mac, Integer timeout, Inet4Address netMask, Inet4Address bcAddr, List<Inet4Address> gateways, List<Inet4Address> dnsServers, Inet4Address dhcpServerIdentifier, String domainName, String hostname, boolean metered, short mtu, String captivePortalUrl) { short mtu, String captivePortalUrl, Integer ipv6OnlyWaitTime) { DhcpPacket pkt = new DhcpOfferPacket( transactionId, (short) 0, broadcast, serverIpAddr, relayIp, INADDR_ANY /* clientIp */, yourIp, mac); Loading @@ -1424,9 +1454,26 @@ public abstract class DhcpPacket { if (metered) { pkt.mVendorInfo = VENDOR_INFO_ANDROID_METERED; } if (ipv6OnlyWaitTime != null) { pkt.mIpv6OnlyWaitTime = ipv6OnlyWaitTime; } return pkt.buildPacket(encap, DHCP_CLIENT, DHCP_SERVER); } /** * Builds a DHCP-OFFER packet from the required specified parameters. */ public static ByteBuffer buildOfferPacket(int encap, int transactionId, boolean broadcast, Inet4Address serverIpAddr, Inet4Address relayIp, Inet4Address yourIp, byte[] mac, Integer timeout, Inet4Address netMask, Inet4Address bcAddr, List<Inet4Address> gateways, List<Inet4Address> dnsServers, Inet4Address dhcpServerIdentifier, String domainName, String hostname, boolean metered, short mtu, String captivePortalUrl) { return buildOfferPacket(encap, transactionId, broadcast, serverIpAddr, relayIp, yourIp, mac, timeout, netMask, bcAddr, gateways, dnsServers, dhcpServerIdentifier, domainName, hostname, metered, mtu, captivePortalUrl, null /* V6ONLY_WAIT */); } /** * Builds a DHCP-ACK packet from the required specified parameters. */ Loading @@ -1435,7 +1482,7 @@ public abstract class DhcpPacket { Inet4Address requestClientIp, byte[] mac, Integer timeout, Inet4Address netMask, Inet4Address bcAddr, List<Inet4Address> gateways, List<Inet4Address> dnsServers, Inet4Address dhcpServerIdentifier, String domainName, String hostname, boolean metered, short mtu, boolean rapidCommit, String captivePortalUrl) { short mtu, boolean rapidCommit, String captivePortalUrl, Integer ipv6OnlyWaitTime) { DhcpPacket pkt = new DhcpAckPacket( transactionId, (short) 0, broadcast, serverIpAddr, relayIp, requestClientIp, yourIp, mac, rapidCommit); Loading @@ -1452,9 +1499,27 @@ public abstract class DhcpPacket { if (metered) { pkt.mVendorInfo = VENDOR_INFO_ANDROID_METERED; } if (ipv6OnlyWaitTime != null) { pkt.mIpv6OnlyWaitTime = ipv6OnlyWaitTime; } return pkt.buildPacket(encap, DHCP_CLIENT, DHCP_SERVER); } /** * Builds a DHCP-ACK packet from the required specified parameters. */ public static ByteBuffer buildAckPacket(int encap, int transactionId, boolean broadcast, Inet4Address serverIpAddr, Inet4Address relayIp, Inet4Address yourIp, Inet4Address requestClientIp, byte[] mac, Integer timeout, Inet4Address netMask, Inet4Address bcAddr, List<Inet4Address> gateways, List<Inet4Address> dnsServers, Inet4Address dhcpServerIdentifier, String domainName, String hostname, boolean metered, short mtu, boolean rapidCommit, String captivePortalUrl) { return buildAckPacket(encap, transactionId, broadcast, serverIpAddr, relayIp, yourIp, requestClientIp, mac, timeout, netMask, bcAddr, gateways, dnsServers, dhcpServerIdentifier, domainName, hostname, metered, mtu, rapidCommit, captivePortalUrl, null /* V6ONLY_WAIT */); } /** * Builds a DHCP-NAK packet from the required specified parameters. */ Loading
src/android/net/ip/IpClient.java +4 −1 Original line number Diff line number Diff line Loading @@ -2169,7 +2169,8 @@ public class IpClient extends StateMachine { // a) initial address acquisition succeeds, // b) renew succeeds or is NAK'd, // c) rebind succeeds or is NAK'd, or // c) the lease expires, // d) the lease expires, or // e) the IPv6-only preferred option is enabled and entering Ipv6OnlyWaitState. // // but never when initial address acquisition fails. The latter // condition is now governed by the provisioning timeout. Loading @@ -2183,6 +2184,8 @@ public class IpClient extends StateMachine { case DhcpClient.DHCP_FAILURE: handleIPv4Failure(); break; case DhcpClient.DHCP_IPV6_ONLY: break; default: logError("Unknown CMD_POST_DHCP_ACTION status: %s", msg.arg1); } Loading