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
diff --git a/tests/integration/Android.bp b/tests/integration/Android.bp
index c4f057b..a9eabc8 100644
--- a/tests/integration/Android.bp
+++ b/tests/integration/Android.bp
@@ -27,9 +27,8 @@
visibility: ["//visibility:private"],
}
-android_library {
- name: "NetworkStackIntegrationTestsLib",
- min_sdk_version: "29",
+java_defaults {
+ name: "NetworkStackIntegrationTestsDefaults",
srcs: ["src/**/*.java"],
static_libs: [
"androidx.annotation_annotation",
@@ -37,7 +36,6 @@
"mockito-target-extended-minus-junit4",
"net-tests-utils",
"testables",
- "NetworkStackApiStableLib",
],
libs: [
"android.test.runner",
@@ -48,6 +46,15 @@
visibility: ["//visibility:private"],
}
+android_library {
+ name: "NetworkStackIntegrationTestsLib",
+ defaults: ["NetworkStackIntegrationTestsDefaults"],
+ min_sdk_version: "29",
+ static_libs: [
+ "NetworkStackApiStableLib",
+ ],
+}
+
// Network stack integration tests.
android_test {
name: "NetworkStackIntegrationTests",
@@ -59,6 +66,21 @@
min_sdk_version: "29",
}
+// Network stack next integration tests.
+android_test {
+ name: "NetworkStackNextIntegrationTests",
+ defaults: [
+ "NetworkStackIntegrationTestsDefaults",
+ "NetworkStackIntegrationTestsJniDefaults",
+ ],
+ static_libs: [
+ "NetworkStackApiCurrentLib",
+ ],
+ certificate: "networkstack",
+ platform_apis: true,
+ test_suites: ["device-tests"],
+}
+
// Special version of the network stack tests that includes all tests necessary for code coverage
// purposes. This is currently the union of NetworkStackTests and NetworkStackIntegrationTests.
android_test {
diff --git a/tests/integration/src/android/net/ip/IpClientIntegrationTest.java b/tests/integration/src/android/net/ip/IpClientIntegrationTest.java
index 4bbee9a..b3ca925 100644
--- a/tests/integration/src/android/net/ip/IpClientIntegrationTest.java
+++ b/tests/integration/src/android/net/ip/IpClientIntegrationTest.java
@@ -56,6 +56,8 @@
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
+import static org.junit.Assume.assumeFalse;
+import static org.junit.Assume.assumeTrue;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.argThat;
import static org.mockito.Mockito.doAnswer;
@@ -86,6 +88,7 @@
import android.net.NetworkStackIpMemoryStore;
import android.net.TestNetworkInterface;
import android.net.TestNetworkManager;
+import android.net.Uri;
import android.net.dhcp.DhcpClient;
import android.net.dhcp.DhcpDeclinePacket;
import android.net.dhcp.DhcpDiscoverPacket;
@@ -117,6 +120,7 @@
import androidx.test.runner.AndroidJUnit4;
import com.android.internal.util.StateMachine;
+import com.android.networkstack.apishim.CaptivePortalDataShimImpl;
import com.android.networkstack.apishim.ShimUtils;
import com.android.networkstack.arp.ArpPacket;
import com.android.server.NetworkObserverRegistry;
@@ -146,6 +150,7 @@
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
+import java.util.Objects;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;
@@ -219,6 +224,7 @@
private static final byte[] SERVER_MAC = new byte[] { 0x00, 0x1A, 0x11, 0x22, 0x33, 0x44 };
private static final String TEST_HOST_NAME = "AOSP on Crosshatch";
private static final String TEST_HOST_NAME_TRANSLITERATION = "AOSP-on-Crosshatch";
+ private static final String TEST_CAPTIVE_PORTAL_URL = "https://example.com/capportapi";
private static class TapPacketReader extends PacketReader {
private final ParcelFileDescriptor mTapFd;
@@ -484,7 +490,7 @@
}
private static ByteBuffer buildDhcpOfferPacket(final DhcpPacket packet,
- final Integer leaseTimeSec, final short mtu) {
+ final Integer leaseTimeSec, final short mtu, final String captivePortalUrl) {
return DhcpPacket.buildOfferPacket(DhcpPacket.ENCAP_L2, packet.getTransactionId(),
false /* broadcast */, SERVER_ADDR, INADDR_ANY /* relayIp */,
CLIENT_ADDR /* yourIp */, packet.getClientMac(), leaseTimeSec,
@@ -492,11 +498,12 @@
Collections.singletonList(SERVER_ADDR) /* gateways */,
Collections.singletonList(SERVER_ADDR) /* dnsServers */,
SERVER_ADDR /* dhcpServerIdentifier */, null /* domainName */, HOSTNAME,
- false /* metered */, mtu);
+ false /* metered */, mtu, captivePortalUrl);
}
private static ByteBuffer buildDhcpAckPacket(final DhcpPacket packet,
- final Integer leaseTimeSec, final short mtu, final boolean rapidCommit) {
+ final Integer leaseTimeSec, final short mtu, final boolean rapidCommit,
+ final String captivePortalApiUrl) {
return DhcpPacket.buildAckPacket(DhcpPacket.ENCAP_L2, packet.getTransactionId(),
false /* broadcast */, SERVER_ADDR, INADDR_ANY /* relayIp */,
CLIENT_ADDR /* yourIp */, CLIENT_ADDR /* requestIp */, packet.getClientMac(),
@@ -504,7 +511,7 @@
Collections.singletonList(SERVER_ADDR) /* gateways */,
Collections.singletonList(SERVER_ADDR) /* dnsServers */,
SERVER_ADDR /* dhcpServerIdentifier */, null /* domainName */, HOSTNAME,
- false /* metered */, mtu, rapidCommit);
+ false /* metered */, mtu, rapidCommit, captivePortalApiUrl);
}
private static ByteBuffer buildDhcpNakPacket(final DhcpPacket packet) {
@@ -624,27 +631,35 @@
final Integer leaseTimeSec, final boolean isDhcpLeaseCacheEnabled,
final boolean shouldReplyRapidCommitAck, final int mtu,
final boolean isDhcpIpConflictDetectEnabled,
- final boolean isHostnameConfigurationEnabled, final String hostname)
- throws Exception {
- final List<DhcpPacket> packetList = new ArrayList<DhcpPacket>();
+ final boolean isHostnameConfigurationEnabled, final String hostname,
+ final String captivePortalApiUrl) throws Exception {
startIpClientProvisioning(isDhcpLeaseCacheEnabled, shouldReplyRapidCommitAck,
false /* isPreconnectionEnabled */, isDhcpIpConflictDetectEnabled,
isHostnameConfigurationEnabled, hostname);
+ return handleDhcpPackets(isSuccessLease, leaseTimeSec, shouldReplyRapidCommitAck, mtu,
+ isDhcpIpConflictDetectEnabled, captivePortalApiUrl);
+ }
+ private List<DhcpPacket> handleDhcpPackets(final boolean isSuccessLease,
+ final Integer leaseTimeSec, final boolean shouldReplyRapidCommitAck, final int mtu,
+ final boolean isDhcpIpConflictDetectEnabled, final String captivePortalApiUrl)
+ throws Exception {
+ final List<DhcpPacket> packetList = new ArrayList<>();
DhcpPacket packet;
while ((packet = getNextDhcpPacket()) != null) {
packetList.add(packet);
if (packet instanceof DhcpDiscoverPacket) {
if (shouldReplyRapidCommitAck) {
sendResponse(buildDhcpAckPacket(packet, leaseTimeSec, (short) mtu,
- true /* rapidCommit */));
+ true /* rapidCommit */, captivePortalApiUrl));
} else {
- sendResponse(buildDhcpOfferPacket(packet, leaseTimeSec, (short) mtu));
+ sendResponse(buildDhcpOfferPacket(packet, leaseTimeSec, (short) mtu,
+ captivePortalApiUrl));
}
} else if (packet instanceof DhcpRequestPacket) {
final ByteBuffer byteBuffer = isSuccessLease
? buildDhcpAckPacket(packet, leaseTimeSec, (short) mtu,
- false /* rapidCommit */)
+ false /* rapidCommit */, captivePortalApiUrl)
: buildDhcpNakPacket(packet);
sendResponse(byteBuffer);
} else {
@@ -676,7 +691,8 @@
final boolean isDhcpIpConflictDetectEnabled) throws Exception {
return performDhcpHandshake(isSuccessLease, leaseTimeSec, isDhcpLeaseCacheEnabled,
isDhcpRapidCommitEnabled, mtu, isDhcpIpConflictDetectEnabled,
- false /* isHostnameConfigurationEnabled */, null /* hostname */);
+ false /* isHostnameConfigurationEnabled */, null /* hostname */,
+ null /* captivePortalApiUrl */);
}
private DhcpPacket getNextDhcpPacket() throws ParseException {
@@ -812,12 +828,13 @@
final short mtu = (short) TEST_DEFAULT_MTU;
if (!shouldReplyRapidCommitAck) {
- sendResponse(buildDhcpOfferPacket(packet, TEST_LEASE_DURATION_S, mtu));
+ sendResponse(buildDhcpOfferPacket(packet, TEST_LEASE_DURATION_S, mtu,
+ null /* captivePortalUrl */));
packet = getNextDhcpPacket();
assertTrue(packet instanceof DhcpRequestPacket);
}
sendResponse(buildDhcpAckPacket(packet, TEST_LEASE_DURATION_S, mtu,
- shouldReplyRapidCommitAck));
+ shouldReplyRapidCommitAck, null /* captivePortalUrl */));
if (!shouldAbortPreconnection) {
mIpc.notifyPreconnectionComplete(true /* success */);
@@ -1447,7 +1464,8 @@
TEST_LEASE_DURATION_S, true /* isDhcpLeaseCacheEnabled */,
false /* isDhcpRapidCommitEnabled */, TEST_DEFAULT_MTU,
false /* isDhcpIpConflictDetectEnabled */,
- true /* isHostnameConfigurationEnabled */, TEST_HOST_NAME /* hostname */);
+ true /* isHostnameConfigurationEnabled */, TEST_HOST_NAME /* hostname */,
+ null /* captivePortalApiUrl */);
assertEquals(2, sentPackets.size());
assertHostname(true, TEST_HOST_NAME, TEST_HOST_NAME_TRANSLITERATION, sentPackets);
assertIpMemoryStoreNetworkAttributes(TEST_LEASE_DURATION_S, currentTime, TEST_DEFAULT_MTU);
@@ -1460,7 +1478,8 @@
TEST_LEASE_DURATION_S, true /* isDhcpLeaseCacheEnabled */,
false /* isDhcpRapidCommitEnabled */, TEST_DEFAULT_MTU,
false /* isDhcpIpConflictDetectEnabled */,
- false /* isHostnameConfigurationEnabled */, TEST_HOST_NAME);
+ false /* isHostnameConfigurationEnabled */, TEST_HOST_NAME,
+ null /* captivePortalApiUrl */);
assertEquals(2, sentPackets.size());
assertHostname(false, TEST_HOST_NAME, TEST_HOST_NAME_TRANSLITERATION, sentPackets);
assertIpMemoryStoreNetworkAttributes(TEST_LEASE_DURATION_S, currentTime, TEST_DEFAULT_MTU);
@@ -1473,10 +1492,59 @@
TEST_LEASE_DURATION_S, true /* isDhcpLeaseCacheEnabled */,
false /* isDhcpRapidCommitEnabled */, TEST_DEFAULT_MTU,
false /* isDhcpIpConflictDetectEnabled */,
- true /* isHostnameConfigurationEnabled */, null /* hostname */);
+ true /* isHostnameConfigurationEnabled */, null /* hostname */,
+ null /* captivePortalApiUrl */);
assertEquals(2, sentPackets.size());
assertHostname(true, null /* hostname */, null /* hostnameAfterTransliteration */,
sentPackets);
assertIpMemoryStoreNetworkAttributes(TEST_LEASE_DURATION_S, currentTime, TEST_DEFAULT_MTU);
}
+
+ private void runDhcpClientCaptivePortalApiTest(boolean featureEnabled,
+ boolean serverSendsOption) throws Exception {
+ startIpClientProvisioning(false /* isDhcpLeaseCacheEnabled */,
+ false /* shouldReplyRapidCommitAck */, false /* isPreConnectionEnabled */,
+ false /* isDhcpIpConflictDetectEnabled */);
+ final DhcpPacket discover = getNextDhcpPacket();
+ assertTrue(discover instanceof DhcpDiscoverPacket);
+ assertEquals(featureEnabled, discover.hasRequestedParam(DhcpPacket.DHCP_CAPTIVE_PORTAL));
+
+ // Send Offer and handle Request -> Ack
+ final String serverSentUrl = serverSendsOption ? TEST_CAPTIVE_PORTAL_URL : null;
+ sendResponse(buildDhcpOfferPacket(discover, TEST_LEASE_DURATION_S, (short) TEST_DEFAULT_MTU,
+ serverSentUrl));
+ final int testMtu = 1345;
+ handleDhcpPackets(true /* isSuccessLease */, TEST_LEASE_DURATION_S,
+ false /* isDhcpRapidCommitEnabled */, testMtu,
+ false /* isDhcpIpConflictDetectEnabled */, serverSentUrl);
+
+ final Uri expectedUrl = featureEnabled && serverSendsOption
+ ? Uri.parse(TEST_CAPTIVE_PORTAL_URL) : null;
+ // Wait for LinkProperties containing DHCP-obtained info, such as MTU, and ensure that the
+ // URL is set as expected
+ verify(mCb, timeout(TEST_TIMEOUT_MS)).onLinkPropertiesChange(argThat(lp ->
+ lp.getMtu() == testMtu
+ && Objects.equals(expectedUrl, lp.getCaptivePortalApiUrl())));
+ }
+
+ @Test
+ public void testDhcpClientCaptivePortalApiEnabled() throws Exception {
+ // Only run the test on platforms / builds where the API is enabled
+ assumeTrue(CaptivePortalDataShimImpl.isSupported());
+ runDhcpClientCaptivePortalApiTest(true /* featureEnabled */, true /* serverSendsOption */);
+ }
+
+ @Test
+ public void testDhcpClientCaptivePortalApiEnabled_NoUrl() throws Exception {
+ // Only run the test on platforms / builds where the API is enabled
+ assumeTrue(CaptivePortalDataShimImpl.isSupported());
+ runDhcpClientCaptivePortalApiTest(true /* featureEnabled */, false /* serverSendsOption */);
+ }
+
+ @Test
+ public void testDhcpClientCaptivePortalApiDisabled() throws Exception {
+ // Only run the test on platforms / builds where the API is disabled
+ assumeFalse(CaptivePortalDataShimImpl.isSupported());
+ runDhcpClientCaptivePortalApiTest(false /* featureEnabled */, true /* serverSendsOption */);
+ }
}