diff options
author | Remi NGUYEN VAN <reminv@google.com> | 2019-10-28 17:04:55 +0900 |
---|---|---|
committer | Xiao Ma <xiaom@google.com> | 2020-02-19 21:54:01 +0900 |
commit | 2a7389299668893d0532838af5f9210f0884604a (patch) | |
tree | ba7578961ae0e0bc5684b9b1825e9cbcc4dce5bd /src | |
parent | 21907e1cdc3dc9f86c4446d7e111a6637aefcec4 (diff) |
Add captive portal info to DhcpClient output
Requesting the captive portal option is flagged off by default.
The URL it provides will be used to support the captive portal API; see
RFC7710bis.
Bug: 139269711
Test: atest NetworkStackTests NetworkStackNextTests
Test: atest NetworkStackIntegrationTests NetworkStackNextIntegrationTests
Change-Id: I783466e0e60f364e79cd76af3fe43a7862d35cf2
Diffstat (limited to 'src')
-rw-r--r-- | src/android/net/dhcp/DhcpClient.java | 32 | ||||
-rw-r--r-- | src/android/net/dhcp/DhcpPacket.java | 64 | ||||
-rw-r--r-- | src/android/net/dhcp/DhcpServer.java | 7 | ||||
-rw-r--r-- | src/android/net/ip/IpClient.java | 11 | ||||
-rw-r--r-- | src/android/net/util/ConnectivityPacketSummary.java | 14 |
5 files changed, 108 insertions, 20 deletions
diff --git a/src/android/net/dhcp/DhcpClient.java b/src/android/net/dhcp/DhcpClient.java index fbcd0f6..41e54a4 100644 --- a/src/android/net/dhcp/DhcpClient.java +++ b/src/android/net/dhcp/DhcpClient.java @@ -17,6 +17,7 @@ package android.net.dhcp; 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_LEASE_TIME; @@ -95,6 +96,7 @@ import com.android.internal.util.StateMachine; import com.android.internal.util.TrafficStatsConstants; import com.android.internal.util.WakeupMessage; import com.android.networkstack.R; +import com.android.networkstack.apishim.CaptivePortalDataShimImpl; import com.android.networkstack.apishim.SocketUtilsShimImpl; import com.android.networkstack.arp.ArpPacket; @@ -260,8 +262,9 @@ public class DhcpClient extends StateMachine { private static final SparseArray<String> sMessageNames = MessageUtils.findMessageNames(sMessageClasses); - // DHCP parameters that we request. - /* package */ static final byte[] REQUESTED_PARAMS = new byte[] { + // DHCP parameters that we request by default. + @VisibleForTesting + /* package */ static final byte[] DEFAULT_REQUESTED_PARAMS = new byte[] { DHCP_SUBNET_MASK, DHCP_ROUTER, DHCP_DNS_SERVER, @@ -274,6 +277,21 @@ public class DhcpClient extends StateMachine { DHCP_VENDOR_INFO, }; + @NonNull + private byte[] getRequestedParams() { + if (isCapportApiEnabled()) { + final byte[] params = Arrays.copyOf(DEFAULT_REQUESTED_PARAMS, + DEFAULT_REQUESTED_PARAMS.length + 1); + params[params.length - 1] = DHCP_CAPTIVE_PORTAL; + return params; + } + return DEFAULT_REQUESTED_PARAMS; + } + + private static boolean isCapportApiEnabled() { + return CaptivePortalDataShimImpl.isSupported(); + } + // DHCP flag that means "yes, we support unicast." private static final boolean DO_UNICAST = false; @@ -556,8 +574,10 @@ 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); + DhcpPacket.ENCAP_L2, optionsToSkip); if (DBG) Log.d(TAG, "Received packet: " + packet); sendMessage(CMD_RECEIVED_PACKET, packet); } catch (DhcpPacket.ParseException e) { @@ -649,7 +669,7 @@ public class DhcpClient extends StateMachine { private boolean sendDiscoverPacket() { final ByteBuffer packet = DhcpPacket.buildDiscoverPacket( DhcpPacket.ENCAP_L2, mTransactionId, getSecs(), mHwAddr, - DO_UNICAST, REQUESTED_PARAMS, isDhcpRapidCommitEnabled(), mHostname); + DO_UNICAST, getRequestedParams(), isDhcpRapidCommitEnabled(), mHostname); return transmitPacket(packet, "DHCPDISCOVER", DhcpPacket.ENCAP_L2, INADDR_BROADCAST); } @@ -663,7 +683,7 @@ public class DhcpClient extends StateMachine { final ByteBuffer packet = DhcpPacket.buildRequestPacket( encap, mTransactionId, getSecs(), clientAddress, DO_UNICAST, mHwAddr, requestedAddress, - serverAddress, REQUESTED_PARAMS, mHostname); + serverAddress, getRequestedParams(), mHostname); String serverStr = (serverAddress != null) ? serverAddress.getHostAddress() : null; String description = "DHCPREQUEST ciaddr=" + clientAddress.getHostAddress() + " request=" + requestedAddress.getHostAddress() + @@ -1267,7 +1287,7 @@ public class DhcpClient extends StateMachine { final Layer2PacketParcelable l2Packet = new Layer2PacketParcelable(); final ByteBuffer packet = DhcpPacket.buildDiscoverPacket( DhcpPacket.ENCAP_L2, mTransactionId, getSecs(), mHwAddr, - DO_UNICAST, REQUESTED_PARAMS, true /* rapid commit */, mHostname); + DO_UNICAST, getRequestedParams(), true /* rapid commit */, mHostname); l2Packet.dstMacAddress = MacAddress.fromBytes(DhcpPacket.ETHER_BROADCAST); l2Packet.payload = Arrays.copyOf(packet.array(), packet.limit()); diff --git a/src/android/net/dhcp/DhcpPacket.java b/src/android/net/dhcp/DhcpPacket.java index 9eca0b5..19af74c 100644 --- a/src/android/net/dhcp/DhcpPacket.java +++ b/src/android/net/dhcp/DhcpPacket.java @@ -307,6 +307,10 @@ public abstract class DhcpPacket { protected static final byte DHCP_RAPID_COMMIT = 80; protected boolean mRapidCommit; + @VisibleForTesting + public static final byte DHCP_CAPTIVE_PORTAL = (byte) 114; + protected String mCaptivePortalUrl; + /** * DHCP zero-length option code: pad */ @@ -785,6 +789,7 @@ public abstract class DhcpPacket { if (mMtu != null && Short.toUnsignedInt(mMtu) >= IPV4_MIN_MTU) { addTlv(buf, DHCP_MTU, mMtu); } + addTlv(buf, DHCP_CAPTIVE_PORTAL, mCaptivePortalUrl); } /** @@ -871,6 +876,23 @@ public abstract class DhcpPacket { } } + private static int skipOption(ByteBuffer packet, int optionLen) + throws BufferUnderflowException { + int expectedLen = 0; + for (int i = 0; i < optionLen; i++) { + expectedLen++; + packet.get(); + } + return expectedLen; + } + + private static boolean shouldSkipOption(byte optionType, byte[] optionsToSkip) { + for (byte option : optionsToSkip) { + if (option == optionType) return true; + } + return false; + } + /** * Creates a concrete DhcpPacket from the supplied ByteBuffer. The * buffer may have an L2 encapsulation (which is the full EthernetII @@ -881,8 +903,8 @@ public abstract class DhcpPacket { * in object fields. */ @VisibleForTesting - static DhcpPacket decodeFullPacket(ByteBuffer packet, int pktType) throws ParseException - { + static DhcpPacket decodeFullPacket(ByteBuffer packet, int pktType, byte[] optionsToSkip) + throws ParseException { // bootp parameters int transactionId; short secs; @@ -900,6 +922,7 @@ public abstract class DhcpPacket { String vendorId = null; String vendorInfo = null; boolean rapidCommit = false; + String captivePortalUrl = null; byte[] expectedParams = null; String hostName = null; String domainName = null; @@ -1080,6 +1103,11 @@ public abstract class DhcpPacket { int optionLen = packet.get() & 0xFF; int expectedLen = 0; + if (shouldSkipOption(optionType, optionsToSkip)) { + skipOption(packet, optionLen); + continue; + } + switch(optionType) { case DHCP_SUBNET_MASK: netMask = readIpAddress(packet); @@ -1172,12 +1200,12 @@ public abstract class DhcpPacket { expectedLen = 0; rapidCommit = true; break; + case DHCP_CAPTIVE_PORTAL: + expectedLen = optionLen; + captivePortalUrl = readAsciiString(packet, optionLen, true); + break; default: - // ignore any other parameters - for (int i = 0; i < optionLen; i++) { - expectedLen++; - byte throwaway = packet.get(); - } + expectedLen = skipOption(packet, optionLen); } if (expectedLen != optionLen) { @@ -1263,6 +1291,7 @@ public abstract class DhcpPacket { newPacket.mT2 = T2; newPacket.mVendorId = vendorId; newPacket.mVendorInfo = vendorInfo; + newPacket.mCaptivePortalUrl = captivePortalUrl; if ((optionOverload & OPTION_OVERLOAD_SNAME) == 0) { newPacket.mServerHostName = serverHostName; } else { @@ -1274,11 +1303,11 @@ public abstract class DhcpPacket { /** * Parse a packet from an array of bytes, stopping at the given length. */ - public static DhcpPacket decodeFullPacket(byte[] packet, int length, int pktType) - throws ParseException { + public static DhcpPacket decodeFullPacket(byte[] packet, int length, int pktType, + byte[] optionsToSkip) throws ParseException { ByteBuffer buffer = ByteBuffer.wrap(packet, 0, length).order(ByteOrder.BIG_ENDIAN); try { - return decodeFullPacket(buffer, pktType); + return decodeFullPacket(buffer, pktType, optionsToSkip); } catch (ParseException e) { throw e; } catch (Exception e) { @@ -1287,6 +1316,14 @@ public abstract class DhcpPacket { } /** + * Parse a packet from an array of bytes, stopping at the given length. + */ + public static DhcpPacket decodeFullPacket(byte[] packet, int length, int pktType) + throws ParseException { + return decodeFullPacket(packet, length, pktType, new byte[0]); + } + + /** * Construct a DhcpResults object from a DHCP reply packet. */ public DhcpResults toDhcpResults() { @@ -1328,6 +1365,7 @@ public abstract class DhcpPacket { results.leaseDuration = (mLeaseTime != null) ? mLeaseTime : INFINITE_LEASE; results.mtu = (mMtu != null && MIN_MTU <= mMtu && mMtu <= MAX_MTU) ? mMtu : 0; results.serverHostName = mServerHostName; + results.captivePortalApiUrl = mCaptivePortalUrl; return results; } @@ -1369,7 +1407,7 @@ public abstract class DhcpPacket { 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) { + short mtu, String captivePortalUrl) { DhcpPacket pkt = new DhcpOfferPacket( transactionId, (short) 0, broadcast, serverIpAddr, relayIp, INADDR_ANY /* clientIp */, yourIp, mac); @@ -1382,6 +1420,7 @@ public abstract class DhcpPacket { pkt.mSubnetMask = netMask; pkt.mBroadcastAddress = bcAddr; pkt.mMtu = mtu; + pkt.mCaptivePortalUrl = captivePortalUrl; if (metered) { pkt.mVendorInfo = VENDOR_INFO_ANDROID_METERED; } @@ -1396,7 +1435,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) { + short mtu, boolean rapidCommit, String captivePortalUrl) { DhcpPacket pkt = new DhcpAckPacket( transactionId, (short) 0, broadcast, serverIpAddr, relayIp, requestClientIp, yourIp, mac, rapidCommit); @@ -1409,6 +1448,7 @@ public abstract class DhcpPacket { pkt.mServerIdentifier = dhcpServerIdentifier; pkt.mBroadcastAddress = bcAddr; pkt.mMtu = mtu; + pkt.mCaptivePortalUrl = captivePortalUrl; if (metered) { pkt.mVendorInfo = VENDOR_INFO_ANDROID_METERED; } diff --git a/src/android/net/dhcp/DhcpServer.java b/src/android/net/dhcp/DhcpServer.java index 9e54a69..9c5b3c6 100644 --- a/src/android/net/dhcp/DhcpServer.java +++ b/src/android/net/dhcp/DhcpServer.java @@ -529,7 +529,9 @@ public class DhcpServer extends IDhcpServer.Stub { broadcastAddr, new ArrayList<>(mServingParams.defaultRouters), new ArrayList<>(mServingParams.dnsServers), mServingParams.getServerInet4Addr(), null /* domainName */, hostname, - mServingParams.metered, (short) mServingParams.linkMtu); + mServingParams.metered, (short) mServingParams.linkMtu, + // TODO (b/144402437): advertise the URL if known + null /* captivePortalApiUrl */); return transmitOfferOrAckPacket(offerPacket, request, lease, clientMac, broadcastFlag); } @@ -549,7 +551,8 @@ public class DhcpServer extends IDhcpServer.Stub { new ArrayList<>(mServingParams.dnsServers), mServingParams.getServerInet4Addr(), null /* domainName */, hostname, mServingParams.metered, (short) mServingParams.linkMtu, - packet.mRapidCommit && mDhcpRapidCommitEnabled); + // TODO (b/144402437): advertise the URL if known + packet.mRapidCommit && mDhcpRapidCommitEnabled, null /* captivePortalApiUrl */); return transmitOfferOrAckPacket(ackPacket, packet, lease, clientMac, broadcastFlag); } diff --git a/src/android/net/ip/IpClient.java b/src/android/net/ip/IpClient.java index 47f955a..173f136 100644 --- a/src/android/net/ip/IpClient.java +++ b/src/android/net/ip/IpClient.java @@ -36,6 +36,7 @@ import android.net.ProvisioningConfigurationParcelable; import android.net.ProxyInfo; import android.net.RouteInfo; import android.net.TcpKeepalivePacketDataParcelable; +import android.net.Uri; import android.net.apf.ApfCapabilities; import android.net.apf.ApfFilter; import android.net.dhcp.DhcpClient; @@ -57,6 +58,7 @@ import android.text.TextUtils; import android.util.LocalLog; import android.util.Log; import android.util.Pair; +import android.util.Patterns; import android.util.SparseArray; import androidx.annotation.NonNull; @@ -1192,6 +1194,15 @@ public class IpClient extends StateMachine { if (mDhcpResults.mtu != 0) { newLp.setMtu(mDhcpResults.mtu); } + + final String capportUrl = mDhcpResults.captivePortalApiUrl; + // Uri.parse does no syntax check; do a simple regex check to eliminate garbage. + // If the URL is still incorrect data fetching will fail later, which is fine. + if (capportUrl != null && Patterns.WEB_URL.matcher(capportUrl).matches()) { + NetworkInformationShimImpl.newInstance() + .setCaptivePortalApiUrl(newLp, Uri.parse(capportUrl)); + } + // TODO: also look at the IPv6 RA (netlink) for captive portal URL } // [4] Add in TCP buffer sizes and HTTP Proxy config, if available. diff --git a/src/android/net/util/ConnectivityPacketSummary.java b/src/android/net/util/ConnectivityPacketSummary.java index 08c3f60..4d04911 100644 --- a/src/android/net/util/ConnectivityPacketSummary.java +++ b/src/android/net/util/ConnectivityPacketSummary.java @@ -82,12 +82,26 @@ public class ConnectivityPacketSummary { private final ByteBuffer mPacket; private final String mSummary; + /** + * Create a string summary of a received packet. + * @param hwaddr MacAddress of the receiving device. + * @param buffer Buffer of the packet. Length is assumed to be the buffer length. + * @return A summary of the packet. + */ public static String summarize(MacAddress hwaddr, byte[] buffer) { return summarize(hwaddr, buffer, buffer.length); } // Methods called herein perform some but by no means all error checking. // They may throw runtime exceptions on malformed packets. + + /** + * Create a string summary of a received packet. + * @param macAddr MacAddress of the receiving device. + * @param buffer Buffer of the packet. + * @param length Length of the packet. + * @return A summary of the packet. + */ public static String summarize(MacAddress macAddr, byte[] buffer, int length) { if ((macAddr == null) || (buffer == null)) return null; length = Math.min(length, buffer.length); |