Add tests for VPN validation in NetworkMonitor
Test: New tests in this patch
also FrameworksNetTests
Test: atest CtsNetTestCases CtsNetTestCasesLatestSdk
Change-Id: I68aa5201ad94531e5a40999816844faef5531525
This commit is contained in:
committed by
Chiachang Wang
parent
ba61dca57c
commit
2f23fa51d8
@@ -17,15 +17,16 @@
|
||||
package android.net.cts;
|
||||
|
||||
import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET;
|
||||
import static android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED;
|
||||
import static android.net.NetworkCapabilities.TRANSPORT_VPN;
|
||||
import static android.net.cts.util.CtsNetUtils.TestNetworkCallback;
|
||||
|
||||
import static com.android.compatibility.common.util.SystemUtil.runWithShellPermissionIdentity;
|
||||
import static com.android.testutils.TestableNetworkCallbackKt.anyNetwork;
|
||||
|
||||
import static org.junit.Assert.assertArrayEquals;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assert.fail;
|
||||
@@ -40,7 +41,6 @@ import android.net.ConnectivityManager;
|
||||
import android.net.Ikev2VpnProfile;
|
||||
import android.net.IpSecAlgorithm;
|
||||
import android.net.Network;
|
||||
import android.net.NetworkCapabilities;
|
||||
import android.net.NetworkRequest;
|
||||
import android.net.ProxyInfo;
|
||||
import android.net.TestNetworkInterface;
|
||||
@@ -53,8 +53,14 @@ import android.platform.test.annotations.AppModeFull;
|
||||
import androidx.test.InstrumentationRegistry;
|
||||
|
||||
import com.android.internal.util.HexDump;
|
||||
import com.android.networkstack.apishim.Ikev2VpnProfileBuilderShimImpl;
|
||||
import com.android.networkstack.apishim.Ikev2VpnProfileShimImpl;
|
||||
import com.android.networkstack.apishim.common.Ikev2VpnProfileShim;
|
||||
import com.android.networkstack.apishim.common.UnsupportedApiLevelException;
|
||||
import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo;
|
||||
import com.android.testutils.DevSdkIgnoreRunner;
|
||||
import com.android.testutils.RecorderCallback.CallbackEntry;
|
||||
import com.android.testutils.TestableNetworkCallback;
|
||||
|
||||
import org.bouncycastle.x509.X509V1CertificateGenerator;
|
||||
import org.junit.After;
|
||||
@@ -167,6 +173,7 @@ public class Ikev2VpnTest {
|
||||
private static final VpnManager sVpnMgr =
|
||||
(VpnManager) sContext.getSystemService(Context.VPN_MANAGEMENT_SERVICE);
|
||||
private static final CtsNetUtils mCtsNetUtils = new CtsNetUtils(sContext);
|
||||
private static final long TIMEOUT_MS = 15_000;
|
||||
|
||||
private final X509Certificate mServerRootCa;
|
||||
private final CertificateAndKey mUserCertKey;
|
||||
@@ -197,31 +204,32 @@ public class Ikev2VpnTest {
|
||||
}, Manifest.permission.MANAGE_TEST_NETWORKS);
|
||||
}
|
||||
|
||||
private Ikev2VpnProfile buildIkev2VpnProfileCommon(
|
||||
Ikev2VpnProfile.Builder builder, boolean isRestrictedToTestNetworks) throws Exception {
|
||||
private Ikev2VpnProfile buildIkev2VpnProfileCommon(@NonNull Ikev2VpnProfile.Builder builder,
|
||||
boolean isRestrictedToTestNetworks,
|
||||
boolean requiresValidation) throws Exception {
|
||||
if (isRestrictedToTestNetworks) {
|
||||
builder.restrictToTestNetworks();
|
||||
}
|
||||
|
||||
return builder.setBypassable(true)
|
||||
builder.setBypassable(true)
|
||||
.setAllowedAlgorithms(TEST_ALLOWED_ALGORITHMS)
|
||||
.setProxy(TEST_PROXY_INFO)
|
||||
.setMaxMtu(TEST_MTU)
|
||||
.setMetered(false)
|
||||
.build();
|
||||
.setMetered(false);
|
||||
if (TestUtils.shouldTestTApis()) {
|
||||
Ikev2VpnProfileBuilderShimImpl.newInstance().setRequiresInternetValidation(
|
||||
builder, requiresValidation);
|
||||
}
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
private Ikev2VpnProfile buildIkev2VpnProfilePsk(boolean isRestrictedToTestNetworks)
|
||||
throws Exception {
|
||||
return buildIkev2VpnProfilePsk(TEST_SERVER_ADDR_V6, isRestrictedToTestNetworks);
|
||||
}
|
||||
|
||||
private Ikev2VpnProfile buildIkev2VpnProfilePsk(
|
||||
String remote, boolean isRestrictedToTestNetworks) throws Exception {
|
||||
private Ikev2VpnProfile buildIkev2VpnProfilePsk(@NonNull String remote,
|
||||
boolean isRestrictedToTestNetworks, boolean requiresValidation) throws Exception {
|
||||
final Ikev2VpnProfile.Builder builder =
|
||||
new Ikev2VpnProfile.Builder(remote, TEST_IDENTITY).setAuthPsk(TEST_PSK);
|
||||
|
||||
return buildIkev2VpnProfileCommon(builder, isRestrictedToTestNetworks);
|
||||
return buildIkev2VpnProfileCommon(builder, isRestrictedToTestNetworks,
|
||||
requiresValidation);
|
||||
}
|
||||
|
||||
private Ikev2VpnProfile buildIkev2VpnProfileUsernamePassword(boolean isRestrictedToTestNetworks)
|
||||
@@ -230,7 +238,8 @@ public class Ikev2VpnTest {
|
||||
new Ikev2VpnProfile.Builder(TEST_SERVER_ADDR_V6, TEST_IDENTITY)
|
||||
.setAuthUsernamePassword(TEST_USER, TEST_PASSWORD, mServerRootCa);
|
||||
|
||||
return buildIkev2VpnProfileCommon(builder, isRestrictedToTestNetworks);
|
||||
return buildIkev2VpnProfileCommon(builder, isRestrictedToTestNetworks,
|
||||
false /* requiresValidation */);
|
||||
}
|
||||
|
||||
private Ikev2VpnProfile buildIkev2VpnProfileDigitalSignature(boolean isRestrictedToTestNetworks)
|
||||
@@ -240,7 +249,8 @@ public class Ikev2VpnTest {
|
||||
.setAuthDigitalSignature(
|
||||
mUserCertKey.cert, mUserCertKey.key, mServerRootCa);
|
||||
|
||||
return buildIkev2VpnProfileCommon(builder, isRestrictedToTestNetworks);
|
||||
return buildIkev2VpnProfileCommon(builder, isRestrictedToTestNetworks,
|
||||
false /* requiresValidation */);
|
||||
}
|
||||
|
||||
private void checkBasicIkev2VpnProfile(@NonNull Ikev2VpnProfile profile) throws Exception {
|
||||
@@ -254,12 +264,11 @@ public class Ikev2VpnTest {
|
||||
assertFalse(profile.isRestrictedToTestNetworks());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBuildIkev2VpnProfilePsk() throws Exception {
|
||||
public void doTestBuildIkev2VpnProfilePsk(final boolean requiresValidation) throws Exception {
|
||||
assumeTrue(mCtsNetUtils.hasIpsecTunnelsFeature());
|
||||
|
||||
final Ikev2VpnProfile profile =
|
||||
buildIkev2VpnProfilePsk(false /* isRestrictedToTestNetworks */);
|
||||
final Ikev2VpnProfile profile = buildIkev2VpnProfilePsk(TEST_SERVER_ADDR_V6,
|
||||
false /* isRestrictedToTestNetworks */, requiresValidation);
|
||||
|
||||
checkBasicIkev2VpnProfile(profile);
|
||||
assertArrayEquals(TEST_PSK, profile.getPresharedKey());
|
||||
@@ -270,6 +279,22 @@ public class Ikev2VpnTest {
|
||||
assertNull(profile.getServerRootCaCert());
|
||||
assertNull(profile.getRsaPrivateKey());
|
||||
assertNull(profile.getUserCert());
|
||||
final Ikev2VpnProfileShim<Ikev2VpnProfile> shim = Ikev2VpnProfileShimImpl.newInstance();
|
||||
if (TestUtils.shouldTestTApis()) {
|
||||
assertEquals(requiresValidation, shim.isInternetValidationRequired(profile));
|
||||
} else {
|
||||
try {
|
||||
shim.isInternetValidationRequired(profile);
|
||||
fail("Only supported from API level 33");
|
||||
} catch (UnsupportedApiLevelException expected) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBuildIkev2VpnProfilePsk() throws Exception {
|
||||
doTestBuildIkev2VpnProfilePsk(true /* requiresValidation */);
|
||||
doTestBuildIkev2VpnProfilePsk(false /* requiresValidation */);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -316,8 +341,8 @@ public class Ikev2VpnTest {
|
||||
setAppop(AppOpsManager.OP_ACTIVATE_VPN, hasActivateVpn);
|
||||
setAppop(AppOpsManager.OP_ACTIVATE_PLATFORM_VPN, hasActivatePlatformVpn);
|
||||
|
||||
final Ikev2VpnProfile profile =
|
||||
buildIkev2VpnProfilePsk(false /* isRestrictedToTestNetworks */);
|
||||
final Ikev2VpnProfile profile = buildIkev2VpnProfilePsk(TEST_SERVER_ADDR_V6,
|
||||
false /* isRestrictedToTestNetworks */, false /* requiresValidation */);
|
||||
final Intent intent = sVpnMgr.provisionVpnProfile(profile);
|
||||
assertEquals(expectIntent, intent != null);
|
||||
}
|
||||
@@ -360,8 +385,8 @@ public class Ikev2VpnTest {
|
||||
|
||||
setAppop(AppOpsManager.OP_ACTIVATE_PLATFORM_VPN, true);
|
||||
|
||||
final Ikev2VpnProfile profile =
|
||||
buildIkev2VpnProfilePsk(false /* isRestrictedToTestNetworks */);
|
||||
final Ikev2VpnProfile profile = buildIkev2VpnProfilePsk(TEST_SERVER_ADDR_V6,
|
||||
false /* isRestrictedToTestNetworks */, false /* requiresValidation */);
|
||||
assertNull(sVpnMgr.provisionVpnProfile(profile));
|
||||
|
||||
// Verify that deleting the profile works (even without the appop)
|
||||
@@ -394,7 +419,8 @@ public class Ikev2VpnTest {
|
||||
}
|
||||
}
|
||||
|
||||
private void checkStartStopVpnProfileBuildsNetworks(IkeTunUtils tunUtils, boolean testIpv6)
|
||||
private void checkStartStopVpnProfileBuildsNetworks(@NonNull IkeTunUtils tunUtils,
|
||||
boolean testIpv6, boolean requiresValidation)
|
||||
throws Exception {
|
||||
String serverAddr = testIpv6 ? TEST_SERVER_ADDR_V6 : TEST_SERVER_ADDR_V4;
|
||||
String initResp = testIpv6 ? SUCCESSFUL_IKE_INIT_RESP_V6 : SUCCESSFUL_IKE_INIT_RESP_V4;
|
||||
@@ -404,10 +430,15 @@ public class Ikev2VpnTest {
|
||||
// Requires MANAGE_TEST_NETWORKS to provision a test-mode profile.
|
||||
mCtsNetUtils.setAppopPrivileged(AppOpsManager.OP_ACTIVATE_PLATFORM_VPN, true);
|
||||
|
||||
final Ikev2VpnProfile profile =
|
||||
buildIkev2VpnProfilePsk(serverAddr, true /* isRestrictedToTestNetworks */);
|
||||
final Ikev2VpnProfile profile = buildIkev2VpnProfilePsk(serverAddr,
|
||||
true /* isRestrictedToTestNetworks */, requiresValidation);
|
||||
assertNull(sVpnMgr.provisionVpnProfile(profile));
|
||||
|
||||
final TestableNetworkCallback cb = new TestableNetworkCallback(TIMEOUT_MS);
|
||||
final NetworkRequest nr = new NetworkRequest.Builder()
|
||||
.clearCapabilities().addTransportType(TRANSPORT_VPN).build();
|
||||
sCM.registerNetworkCallback(nr, cb);
|
||||
|
||||
sVpnMgr.startProvisionedVpnProfile();
|
||||
|
||||
// Inject IKE negotiation
|
||||
@@ -418,35 +449,49 @@ public class Ikev2VpnTest {
|
||||
HexDump.hexStringToByteArray(authResp));
|
||||
|
||||
// Verify the VPN network came up
|
||||
final NetworkRequest nr = new NetworkRequest.Builder()
|
||||
.clearCapabilities().addTransportType(TRANSPORT_VPN).build();
|
||||
final Network vpnNetwork = cb.expectCallback(CallbackEntry.AVAILABLE, anyNetwork())
|
||||
.getNetwork();
|
||||
|
||||
final TestNetworkCallback cb = new TestNetworkCallback();
|
||||
sCM.requestNetwork(nr, cb);
|
||||
cb.waitForAvailable();
|
||||
final Network vpnNetwork = cb.currentNetwork;
|
||||
assertNotNull(vpnNetwork);
|
||||
cb.expectCapabilitiesThat(vpnNetwork, TIMEOUT_MS, caps -> caps.hasTransport(TRANSPORT_VPN)
|
||||
&& caps.hasCapability(NET_CAPABILITY_INTERNET)
|
||||
&& !caps.hasCapability(NET_CAPABILITY_VALIDATED)
|
||||
&& Process.myUid() == caps.getOwnerUid());
|
||||
cb.expectCallback(CallbackEntry.LINK_PROPERTIES_CHANGED, vpnNetwork);
|
||||
cb.expectCallback(CallbackEntry.BLOCKED_STATUS, vpnNetwork);
|
||||
|
||||
final NetworkCapabilities caps = sCM.getNetworkCapabilities(vpnNetwork);
|
||||
assertTrue(caps.hasTransport(TRANSPORT_VPN));
|
||||
assertTrue(caps.hasCapability(NET_CAPABILITY_INTERNET));
|
||||
assertEquals(Process.myUid(), caps.getOwnerUid());
|
||||
// A VPN that requires validation is initially not validated, while one that doesn't
|
||||
// immediately validate automatically. Because this VPN can't actually access Internet,
|
||||
// the VPN only validates if it doesn't require validation. If the VPN requires validation
|
||||
// but unexpectedly sends this callback, expecting LOST below will fail because the next
|
||||
// callback will be the validated capabilities instead.
|
||||
// In S and below, |requiresValidation| is ignored, so this callback is always sent
|
||||
// regardless of its value.
|
||||
if (!requiresValidation || !TestUtils.shouldTestTApis()) {
|
||||
cb.eventuallyExpect(CallbackEntry.NETWORK_CAPS_UPDATED, TIMEOUT_MS,
|
||||
entry -> ((CallbackEntry.CapabilitiesChanged) entry).getCaps()
|
||||
.hasCapability(NET_CAPABILITY_VALIDATED));
|
||||
}
|
||||
|
||||
sVpnMgr.stopProvisionedVpnProfile();
|
||||
cb.waitForLost();
|
||||
assertEquals(vpnNetwork, cb.lastLostNetwork);
|
||||
// Using expectCallback may cause the test to be flaky since test may receive other
|
||||
// callbacks such as linkproperties change.
|
||||
cb.eventuallyExpect(CallbackEntry.LOST, TIMEOUT_MS,
|
||||
lost -> vpnNetwork.equals(lost.getNetwork()));
|
||||
}
|
||||
|
||||
private class VerifyStartStopVpnProfileTest implements TestNetworkRunnable.Test {
|
||||
private final boolean mTestIpv6Only;
|
||||
private final boolean mRequiresValidation;
|
||||
|
||||
/**
|
||||
* Constructs the test
|
||||
*
|
||||
* @param testIpv6Only if true, builds a IPv6-only test; otherwise builds a IPv4-only test
|
||||
* @param requiresValidation whether this VPN should request platform validation
|
||||
*/
|
||||
VerifyStartStopVpnProfileTest(boolean testIpv6Only) {
|
||||
VerifyStartStopVpnProfileTest(boolean testIpv6Only, boolean requiresValidation) {
|
||||
mTestIpv6Only = testIpv6Only;
|
||||
mRequiresValidation = requiresValidation;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -454,7 +499,8 @@ public class Ikev2VpnTest {
|
||||
throws Exception {
|
||||
final IkeTunUtils tunUtils = new IkeTunUtils(testIface.getFileDescriptor());
|
||||
|
||||
checkStartStopVpnProfileBuildsNetworks(tunUtils, mTestIpv6Only);
|
||||
checkStartStopVpnProfileBuildsNetworks(
|
||||
tunUtils, mTestIpv6Only, mRequiresValidation);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -478,7 +524,10 @@ public class Ikev2VpnTest {
|
||||
|
||||
// Requires shell permission to update appops.
|
||||
runWithShellPermissionIdentity(
|
||||
new TestNetworkRunnable(new VerifyStartStopVpnProfileTest(false)));
|
||||
new TestNetworkRunnable(new VerifyStartStopVpnProfileTest(false, false)));
|
||||
|
||||
runWithShellPermissionIdentity(
|
||||
new TestNetworkRunnable(new VerifyStartStopVpnProfileTest(false, true)));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -487,7 +536,9 @@ public class Ikev2VpnTest {
|
||||
|
||||
// Requires shell permission to update appops.
|
||||
runWithShellPermissionIdentity(
|
||||
new TestNetworkRunnable(new VerifyStartStopVpnProfileTest(true)));
|
||||
new TestNetworkRunnable(new VerifyStartStopVpnProfileTest(true, false)));
|
||||
runWithShellPermissionIdentity(
|
||||
new TestNetworkRunnable(new VerifyStartStopVpnProfileTest(true, true)));
|
||||
}
|
||||
|
||||
private static class CertificateAndKey {
|
||||
|
||||
@@ -16,6 +16,8 @@
|
||||
|
||||
package android.net.cts;
|
||||
|
||||
import static com.android.testutils.DevSdkIgnoreRuleKt.SC_V2;
|
||||
|
||||
import android.os.Build;
|
||||
|
||||
import com.android.modules.utils.build.SdkLevel;
|
||||
@@ -33,4 +35,13 @@ public class TestUtils {
|
||||
public static boolean shouldTestSApis() {
|
||||
return SdkLevel.isAtLeastS() && ConstantsShim.VERSION > Build.VERSION_CODES.R;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether to test T+ APIs. This requires a) that the test be running on an S+ device, and
|
||||
* b) that the code be compiled against shims new enough to access these APIs.
|
||||
*/
|
||||
public static boolean shouldTestTApis() {
|
||||
// TODO: replace SC_V2 with Build.VERSION_CODES.S_V2 when it's available in mainline branch.
|
||||
return SdkLevel.isAtLeastT() && ConstantsShim.VERSION > SC_V2;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -343,6 +343,10 @@ public class NetworkAgentWrapper implements TestableNetworkCallback.HasNetwork {
|
||||
return mNetworkAgent;
|
||||
}
|
||||
|
||||
public NetworkAgentConfig getNetworkAgentConfig() {
|
||||
return mNetworkAgentConfig;
|
||||
}
|
||||
|
||||
public NetworkCapabilities getNetworkCapabilities() {
|
||||
return mNetworkCapabilities;
|
||||
}
|
||||
|
||||
@@ -334,6 +334,7 @@ import com.android.modules.utils.build.SdkLevel;
|
||||
import com.android.net.module.util.ArrayTrackRecord;
|
||||
import com.android.net.module.util.CollectionUtils;
|
||||
import com.android.net.module.util.LocationPermissionChecker;
|
||||
import com.android.networkstack.apishim.NetworkAgentConfigShimImpl;
|
||||
import com.android.server.ConnectivityService.ConnectivityDiagnosticsCallbackInfo;
|
||||
import com.android.server.ConnectivityService.NetworkRequestInfo;
|
||||
import com.android.server.ConnectivityServiceTest.ConnectivityServiceDependencies.ReportedInterfaces;
|
||||
@@ -1370,6 +1371,10 @@ public class ConnectivityServiceTest {
|
||||
return (mMockNetworkAgent == null) ? null : mMockNetworkAgent.getNetwork();
|
||||
}
|
||||
|
||||
public NetworkAgentConfig getNetworkAgentConfig() {
|
||||
return null == mMockNetworkAgent ? null : mMockNetworkAgent.getNetworkAgentConfig();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getActiveVpnType() {
|
||||
return mVpnType;
|
||||
@@ -2932,6 +2937,7 @@ public class ConnectivityServiceTest {
|
||||
@Test
|
||||
public void testRequiresValidation() {
|
||||
assertTrue(NetworkMonitorUtils.isValidationRequired(
|
||||
NetworkAgentConfigShimImpl.newInstance(null),
|
||||
mCm.getDefaultRequest().networkCapabilities));
|
||||
}
|
||||
|
||||
@@ -7899,6 +7905,7 @@ public class ConnectivityServiceTest {
|
||||
// VPN networks do not satisfy the default request and are automatically validated
|
||||
// by NetworkMonitor
|
||||
assertFalse(NetworkMonitorUtils.isValidationRequired(
|
||||
NetworkAgentConfigShimImpl.newInstance(mMockVpn.getNetworkAgentConfig()),
|
||||
mMockVpn.getAgent().getNetworkCapabilities()));
|
||||
mMockVpn.getAgent().setNetworkValid(false /* isStrictMode */);
|
||||
|
||||
@@ -8049,6 +8056,7 @@ public class ConnectivityServiceTest {
|
||||
assertTrue(nc.hasCapability(NET_CAPABILITY_INTERNET));
|
||||
|
||||
assertFalse(NetworkMonitorUtils.isValidationRequired(
|
||||
NetworkAgentConfigShimImpl.newInstance(mMockVpn.getNetworkAgentConfig()),
|
||||
mMockVpn.getAgent().getNetworkCapabilities()));
|
||||
assertTrue(NetworkMonitorUtils.isPrivateDnsValidationRequired(
|
||||
mMockVpn.getAgent().getNetworkCapabilities()));
|
||||
|
||||
Reference in New Issue
Block a user