diff --git a/tests/cts/net/ipsec/Android.bp b/tests/cts/net/ipsec/Android.bp index f1f120b32b..16bdb0548a 100644 --- a/tests/cts/net/ipsec/Android.bp +++ b/tests/cts/net/ipsec/Android.bp @@ -33,6 +33,7 @@ android_test { "androidx.test.ext.junit", "compatibility-device-util-axt", "ctstestrunner-axt", + "net-tests-utils", ], platform_apis: true, diff --git a/tests/cts/net/ipsec/src/android/net/ipsec/ike/cts/IkeSessionParamsTest.java b/tests/cts/net/ipsec/src/android/net/ipsec/ike/cts/IkeSessionParamsTest.java index 6fc7cb3634..c767b78120 100644 --- a/tests/cts/net/ipsec/src/android/net/ipsec/ike/cts/IkeSessionParamsTest.java +++ b/tests/cts/net/ipsec/src/android/net/ipsec/ike/cts/IkeSessionParamsTest.java @@ -46,6 +46,7 @@ import androidx.test.ext.junit.runners.AndroidJUnit4; import com.android.internal.net.ipsec.ike.testutils.CertUtils; +import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -63,7 +64,7 @@ import java.util.Set; import java.util.concurrent.TimeUnit; @RunWith(AndroidJUnit4.class) -public final class IkeSessionParamsTest extends IkeSessionParamsTestBase { +public final class IkeSessionParamsTest extends IkeSessionTestBase { private static final int HARD_LIFETIME_SECONDS = (int) TimeUnit.HOURS.toSeconds(20L); private static final int SOFT_LIFETIME_SECONDS = (int) TimeUnit.HOURS.toSeconds(10L); private static final int DPD_DELAY_SECONDS = (int) TimeUnit.MINUTES.toSeconds(10L); @@ -105,6 +106,9 @@ public final class IkeSessionParamsTest extends IkeSessionParamsTestBase { @Before public void setUp() throws Exception { + // This address is never used except for setting up the test network + setUpTestNetwork(IPV4_ADDRESS_LOCAL); + mServerCaCert = CertUtils.createCertFromPemFile("server-a-self-signed-ca.pem"); mClientEndCert = CertUtils.createCertFromPemFile("client-a-end-cert.pem"); mClientIntermediateCaCertOne = @@ -114,6 +118,11 @@ public final class IkeSessionParamsTest extends IkeSessionParamsTestBase { mClientPrivateKey = CertUtils.createRsaPrivateKeyFromKeyFile("client-a-private-key.key"); } + @After + public void tearDown() throws Exception { + tearDownTestNetwork(); + } + private static EapSessionConfig.Builder createEapOnlySafeMethodsBuilder() { return new EapSessionConfig.Builder() .setEapIdentity(EAP_IDENTITY) @@ -131,7 +140,7 @@ public final class IkeSessionParamsTest extends IkeSessionParamsTestBase { */ private IkeSessionParams.Builder createIkeParamsBuilderMinimum() { return new IkeSessionParams.Builder(sContext) - .setNetwork(sTunNetwork) + .setNetwork(mTunNetwork) .setServerHostname(IPV4_ADDRESS_REMOTE.getHostAddress()) .addSaProposal(SA_PROPOSAL) .setLocalIdentification(LOCAL_ID) @@ -145,7 +154,7 @@ public final class IkeSessionParamsTest extends IkeSessionParamsTestBase { * @see #createIkeParamsBuilderMinimum */ private void verifyIkeParamsMinimum(IkeSessionParams sessionParams) { - assertEquals(sTunNetwork, sessionParams.getNetwork()); + assertEquals(mTunNetwork, sessionParams.getNetwork()); assertEquals(IPV4_ADDRESS_REMOTE.getHostAddress(), sessionParams.getServerHostname()); assertEquals(Arrays.asList(SA_PROPOSAL), sessionParams.getSaProposals()); assertEquals(LOCAL_ID, sessionParams.getLocalIdentification()); @@ -268,7 +277,7 @@ public final class IkeSessionParamsTest extends IkeSessionParamsTestBase { */ private IkeSessionParams.Builder createIkeParamsBuilderMinimumWithoutAuth() { return new IkeSessionParams.Builder(sContext) - .setNetwork(sTunNetwork) + .setNetwork(mTunNetwork) .setServerHostname(IPV4_ADDRESS_REMOTE.getHostAddress()) .addSaProposal(SA_PROPOSAL) .setLocalIdentification(LOCAL_ID) @@ -282,7 +291,7 @@ public final class IkeSessionParamsTest extends IkeSessionParamsTestBase { * @see #createIkeParamsBuilderMinimumWithoutAuth */ private void verifyIkeParamsMinimumWithoutAuth(IkeSessionParams sessionParams) { - assertEquals(sTunNetwork, sessionParams.getNetwork()); + assertEquals(mTunNetwork, sessionParams.getNetwork()); assertEquals(IPV4_ADDRESS_REMOTE.getHostAddress(), sessionParams.getServerHostname()); assertEquals(Arrays.asList(SA_PROPOSAL), sessionParams.getSaProposals()); assertEquals(LOCAL_ID, sessionParams.getLocalIdentification()); diff --git a/tests/cts/net/ipsec/src/android/net/ipsec/ike/cts/IkeSessionParamsTestBase.java b/tests/cts/net/ipsec/src/android/net/ipsec/ike/cts/IkeSessionParamsTestBase.java deleted file mode 100644 index c3e3ba353c..0000000000 --- a/tests/cts/net/ipsec/src/android/net/ipsec/ike/cts/IkeSessionParamsTestBase.java +++ /dev/null @@ -1,85 +0,0 @@ -/* - * Copyright (C) 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.net.ipsec.ike.cts; - -import android.content.Context; -import android.net.ConnectivityManager; -import android.net.LinkAddress; -import android.net.Network; -import android.net.TestNetworkInterface; -import android.net.TestNetworkManager; -import android.net.ipsec.ike.cts.TestNetworkUtils.TestNetworkCallback; -import android.os.Binder; -import android.os.IBinder; -import android.os.ParcelFileDescriptor; -import android.platform.test.annotations.AppModeFull; - -import androidx.test.InstrumentationRegistry; -import androidx.test.runner.AndroidJUnit4; - -import org.junit.AfterClass; -import org.junit.BeforeClass; -import org.junit.runner.RunWith; - -@RunWith(AndroidJUnit4.class) -@AppModeFull(reason = "MANAGE_TEST_NETWORKS permission can't be granted to instant apps") -abstract class IkeSessionParamsTestBase extends IkeTestBase { - // Static state to reduce setup/teardown - static ConnectivityManager sCM; - static TestNetworkManager sTNM; - static ParcelFileDescriptor sTunFd; - static TestNetworkCallback sTunNetworkCallback; - static Network sTunNetwork; - - static Context sContext = InstrumentationRegistry.getContext(); - static IBinder sBinder = new Binder(); - - // This method is guaranteed to run in subclasses and will run before subclasses' @BeforeClass - // methods. - @BeforeClass - public static void setUpTestNetworkBeforeClass() throws Exception { - InstrumentationRegistry.getInstrumentation() - .getUiAutomation() - .adoptShellPermissionIdentity(); - sCM = (ConnectivityManager) sContext.getSystemService(Context.CONNECTIVITY_SERVICE); - sTNM = (TestNetworkManager) sContext.getSystemService(Context.TEST_NETWORK_SERVICE); - - TestNetworkInterface testIface = - sTNM.createTunInterface( - new LinkAddress[] {new LinkAddress(IPV4_ADDRESS_LOCAL, IP4_PREFIX_LEN)}); - - sTunFd = testIface.getFileDescriptor(); - sTunNetworkCallback = - TestNetworkUtils.setupAndGetTestNetwork( - sCM, sTNM, testIface.getInterfaceName(), sBinder); - sTunNetwork = sTunNetworkCallback.getNetworkBlocking(); - } - - // This method is guaranteed to run in subclasses and will run after subclasses' @AfterClass - // methods. - @AfterClass - public static void tearDownTestNetworkAfterClass() throws Exception { - sCM.unregisterNetworkCallback(sTunNetworkCallback); - - sTNM.teardownTestNetwork(sTunNetwork); - sTunFd.close(); - - InstrumentationRegistry.getInstrumentation() - .getUiAutomation() - .dropShellPermissionIdentity(); - } -} diff --git a/tests/cts/net/ipsec/src/android/net/ipsec/ike/cts/IkeSessionPskTest.java b/tests/cts/net/ipsec/src/android/net/ipsec/ike/cts/IkeSessionPskTest.java new file mode 100644 index 0000000000..ed67dd1bd7 --- /dev/null +++ b/tests/cts/net/ipsec/src/android/net/ipsec/ike/cts/IkeSessionPskTest.java @@ -0,0 +1,258 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.net.ipsec.ike.cts; + +import static android.net.ipsec.ike.IkeSessionConfiguration.EXTENSION_TYPE_FRAGMENTATION; +import static android.net.ipsec.ike.exceptions.IkeProtocolException.ERROR_TYPE_NO_PROPOSAL_CHOSEN; +import static android.system.OsConstants.AF_INET; +import static android.system.OsConstants.AF_INET6; + +import static com.android.internal.util.HexDump.hexStringToByteArray; + +import static org.junit.Assert.assertArrayEquals; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + +import android.net.ipsec.ike.ChildSessionConfiguration; +import android.net.ipsec.ike.IkeFqdnIdentification; +import android.net.ipsec.ike.IkeSession; +import android.net.ipsec.ike.IkeSessionConfiguration; +import android.net.ipsec.ike.IkeSessionConnectionInfo; +import android.net.ipsec.ike.IkeSessionParams; +import android.net.ipsec.ike.TunnelModeChildSessionParams; +import android.net.ipsec.ike.exceptions.IkeException; +import android.net.ipsec.ike.exceptions.IkeProtocolException; +import android.platform.test.annotations.AppModeFull; + +import androidx.test.ext.junit.runners.AndroidJUnit4; + +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.net.InetAddress; +import java.util.Arrays; + +@RunWith(AndroidJUnit4.class) +@AppModeFull(reason = "MANAGE_TEST_NETWORKS permission can't be granted to instant apps") +public class IkeSessionPskTest extends IkeSessionTestBase { + // Test vectors for success workflow + private static final String SUCCESS_IKE_INIT_RESP = + "46B8ECA1E0D72A18B45427679F9245D421202220000000000000015022000030" + + "0000002C010100040300000C0100000C800E0080030000080300000203000008" + + "0200000200000008040000022800008800020000A7AA3435D088EC1A2B7C2A47" + + "1FA1B85F1066C9B2006E7C353FB5B5FDBC2A88347ED2C6F5B7A265D03AE34039" + + "6AAC0145CFCC93F8BDB219DDFF22A603B8856A5DC59B6FAB7F17C5660CF38670" + + "8794FC72F273ADEB7A4F316519794AED6F8AB61F95DFB360FAF18C6C8CABE471" + + "6E18FE215348C2E582171A57FC41146B16C4AFE429000024A634B61C0E5C90C6" + + "8D8818B0955B125A9B1DF47BBD18775710792E651083105C2900001C00004004" + + "406FA3C5685A16B9B72C7F2EEE9993462C619ABE2900001C00004005AF905A87" + + "0A32222AA284A7070585601208A282F0290000080000402E290000100000402F" + + "00020003000400050000000800004014"; + private static final String SUCCESS_IKE_AUTH_RESP = + "46B8ECA1E0D72A18B45427679F9245D42E20232000000001000000EC240000D0" + + "0D06D37198F3F0962DE8170D66F1A9008267F98CDD956D984BDCED2FC7FAF84A" + + "A6664EF25049B46B93C9ED420488E0C172AA6635BF4011C49792EF2B88FE7190" + + "E8859FEEF51724FD20C46E7B9A9C3DC4708EF7005707A18AB747C903ABCEAC5C" + + "6ECF5A5FC13633DCE3844A920ED10EF202F115DBFBB5D6D2D7AB1F34EB08DE7C" + + "A54DCE0A3A582753345CA2D05A0EFDB9DC61E81B2483B7D13EEE0A815D37252C" + + "23D2F29E9C30658227D2BB0C9E1A481EAA80BC6BE9006BEDC13E925A755A0290" + + "AEC4164D29997F52ED7DCC2E"; + private static final String SUCCESS_CREATE_CHILD_RESP = + "46B8ECA1E0D72A18B45427679F9245D42E20242000000002000000CC210000B0" + + "484565D4AF6546274674A8DE339E9C9584EE2326AB9260F41C4D0B6C5B02D1D" + + "2E8394E3CDE3094895F2ACCABCDCA8E82960E5196E9622BD13745FC8D6A2BED" + + "E561FF5D9975421BC463C959A3CBA3478256B6D278159D99B512DDF56AC1658" + + "63C65A986F395FE8B1476124B91F83FD7865304EB95B22CA4DD9601DA7A2533" + + "ABF4B36EB1B8CD09522F6A600032316C74E562E6756D9D49D945854E2ABDC4C" + + "3AF36305353D60D40B58BE44ABF82"; + private static final String SUCCESS_DELETE_CHILD_RESP = + "46B8ECA1E0D72A18B45427679F9245D42E202520000000030000004C2A000030" + + "0C5CEB882DBCA65CE32F4C53909335F1365C91C555316C5E9D9FB553F7AA916" + + "EF3A1D93460B7FABAF0B4B854"; + private static final String SUCCESS_DELETE_IKE_RESP = + "46B8ECA1E0D72A18B45427679F9245D42E202520000000040000004C00000030" + + "9352D71100777B00ABCC6BD7DBEA697827FFAAA48DF9A54D1D68161939F5DC8" + + "6743A7CEB2BE34AC00095A5B8"; + + private static final long IKE_INIT_SPI = Long.parseLong("46B8ECA1E0D72A18", 16); + + private static final TunnelModeChildSessionParams CHILD_PARAMS = + new TunnelModeChildSessionParams.Builder() + .addSaProposal(SaProposalTest.buildChildSaProposalWithNormalModeCipher()) + .addSaProposal(SaProposalTest.buildChildSaProposalWithCombinedModeCipher()) + .addInternalAddressRequest(AF_INET) + .addInternalAddressRequest(AF_INET6) + .build(); + + private IkeSessionParams createIkeSessionParams(InetAddress mRemoteAddress) { + return new IkeSessionParams.Builder(sContext) + .setNetwork(mTunNetwork) + .setServerHostname(mRemoteAddress.getHostAddress()) + .addSaProposal(SaProposalTest.buildIkeSaProposalWithNormalModeCipher()) + .addSaProposal(SaProposalTest.buildIkeSaProposalWithCombinedModeCipher()) + .setLocalIdentification(new IkeFqdnIdentification(LOCAL_HOSTNAME)) + .setRemoteIdentification(new IkeFqdnIdentification(REMOTE_HOSTNAME)) + .setAuthPsk(IKE_PSK) + .build(); + } + + private IkeSession openIkeSession(IkeSessionParams ikeParams) { + return new IkeSession( + sContext, + ikeParams, + CHILD_PARAMS, + mUserCbExecutor, + mIkeSessionCallback, + mFirstChildSessionCallback); + } + + @Test + public void testIkeSessionSetupAndManageChildSas() throws Exception { + // Open IKE Session + IkeSession ikeSession = openIkeSession(createIkeSessionParams(mRemoteAddress)); + int expectedMsgId = 0; + mTunUtils.awaitReqAndInjectResp( + IKE_INIT_SPI, + expectedMsgId++, + false /* expectedUseEncap */, + hexStringToByteArray(SUCCESS_IKE_INIT_RESP)); + + mTunUtils.awaitReqAndInjectResp( + IKE_INIT_SPI, + expectedMsgId++, + true /* expectedUseEncap */, + hexStringToByteArray(SUCCESS_IKE_AUTH_RESP)); + + // Verify opening IKE Session + IkeSessionConfiguration ikeConfig = mIkeSessionCallback.awaitIkeConfig(); + assertNotNull(ikeConfig); + assertEquals(EXPECTED_REMOTE_APP_VERSION_EMPTY, ikeConfig.getRemoteApplicationVersion()); + assertTrue(ikeConfig.getRemoteVendorIds().isEmpty()); + assertTrue(ikeConfig.getPcscfServers().isEmpty()); + assertTrue(ikeConfig.isIkeExtensionEnabled(EXTENSION_TYPE_FRAGMENTATION)); + + IkeSessionConnectionInfo ikeConnectInfo = ikeConfig.getIkeSessionConnectionInfo(); + assertNotNull(ikeConnectInfo); + assertEquals(mLocalAddress, ikeConnectInfo.getLocalAddress()); + assertEquals(mRemoteAddress, ikeConnectInfo.getRemoteAddress()); + assertEquals(mTunNetwork, ikeConnectInfo.getNetwork()); + + // Verify opening first Child Session + ChildSessionConfiguration firstChildConfig = mFirstChildSessionCallback.awaitChildConfig(); + assertNotNull(firstChildConfig); + assertEquals( + Arrays.asList(EXPECTED_INBOUND_TS), firstChildConfig.getInboundTrafficSelectors()); + assertEquals(Arrays.asList(DEFAULT_V4_TS), firstChildConfig.getOutboundTrafficSelectors()); + assertEquals( + Arrays.asList(EXPECTED_INTERNAL_LINK_ADDR), + firstChildConfig.getInternalAddresses()); + assertTrue(firstChildConfig.getInternalSubnets().isEmpty()); + assertTrue(firstChildConfig.getInternalDnsServers().isEmpty()); + assertTrue(firstChildConfig.getInternalDhcpServers().isEmpty()); + + // Open additional Child Session + TestChildSessionCallback additionalChildCb = new TestChildSessionCallback(); + ikeSession.openChildSession(CHILD_PARAMS, additionalChildCb); + mTunUtils.awaitReqAndInjectResp( + IKE_INIT_SPI, + expectedMsgId++, + true /* expectedUseEncap */, + hexStringToByteArray(SUCCESS_CREATE_CHILD_RESP)); + + // Verify opening additional Child Session + ChildSessionConfiguration additionalChildConfig = additionalChildCb.awaitChildConfig(); + assertNotNull(additionalChildConfig); + assertEquals( + Arrays.asList(EXPECTED_INBOUND_TS), firstChildConfig.getInboundTrafficSelectors()); + assertEquals(Arrays.asList(DEFAULT_V4_TS), firstChildConfig.getOutboundTrafficSelectors()); + assertTrue(additionalChildConfig.getInternalAddresses().isEmpty()); + assertTrue(firstChildConfig.getInternalSubnets().isEmpty()); + assertTrue(firstChildConfig.getInternalDnsServers().isEmpty()); + assertTrue(firstChildConfig.getInternalDhcpServers().isEmpty()); + + // Close additional Child Session + ikeSession.closeChildSession(additionalChildCb); + mTunUtils.awaitReqAndInjectResp( + IKE_INIT_SPI, + expectedMsgId++, + true /* expectedUseEncap */, + hexStringToByteArray(SUCCESS_DELETE_CHILD_RESP)); + + additionalChildCb.awaitOnClosed(); + + // Close IKE Session + ikeSession.close(); + mTunUtils.awaitReqAndInjectResp( + IKE_INIT_SPI, + expectedMsgId++, + true /* expectedUseEncap */, + hexStringToByteArray(SUCCESS_DELETE_IKE_RESP)); + + mFirstChildSessionCallback.awaitOnClosed(); + mIkeSessionCallback.awaitOnClosed(); + + // TODO: verify IpSecTransform pair is created and deleted + } + + @Test + public void testIkeSessionKill() throws Exception { + // Open IKE Session + IkeSession ikeSession = openIkeSession(createIkeSessionParams(mRemoteAddress)); + int expectedMsgId = 0; + mTunUtils.awaitReqAndInjectResp( + IKE_INIT_SPI, + expectedMsgId++, + false /* expectedUseEncap */, + hexStringToByteArray(SUCCESS_IKE_INIT_RESP)); + + mTunUtils.awaitReqAndInjectResp( + IKE_INIT_SPI, + expectedMsgId++, + true /* expectedUseEncap */, + hexStringToByteArray(SUCCESS_IKE_AUTH_RESP)); + + ikeSession.kill(); + + mFirstChildSessionCallback.awaitOnClosed(); + mIkeSessionCallback.awaitOnClosed(); + } + + @Test + public void testIkeInitFail() throws Exception { + String ikeInitFailRespHex = + "46B8ECA1E0D72A180000000000000000292022200000000000000024000000080000000E"; + + // Open IKE Session + IkeSession ikeSession = openIkeSession(createIkeSessionParams(mRemoteAddress)); + int expectedMsgId = 0; + mTunUtils.awaitReqAndInjectResp( + IKE_INIT_SPI, + expectedMsgId++, + false /* expectedUseEncap */, + hexStringToByteArray(ikeInitFailRespHex)); + + IkeException exception = mIkeSessionCallback.awaitOnClosedException(); + assertNotNull(exception); + assertTrue(exception instanceof IkeProtocolException); + IkeProtocolException protocolException = (IkeProtocolException) exception; + assertEquals(ERROR_TYPE_NO_PROPOSAL_CHOSEN, protocolException.getErrorType()); + assertArrayEquals(EXPECTED_PROTOCOL_ERROR_DATA_NONE, protocolException.getErrorData()); + } + + // TODO(b/148689509): Verify rekey process and handling IKE_AUTH failure +} diff --git a/tests/cts/net/ipsec/src/android/net/ipsec/ike/cts/IkeSessionTestBase.java b/tests/cts/net/ipsec/src/android/net/ipsec/ike/cts/IkeSessionTestBase.java new file mode 100644 index 0000000000..deba8fd985 --- /dev/null +++ b/tests/cts/net/ipsec/src/android/net/ipsec/ike/cts/IkeSessionTestBase.java @@ -0,0 +1,374 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.net.ipsec.ike.cts; + +import static android.app.AppOpsManager.OP_MANAGE_IPSEC_TUNNELS; + +import android.annotation.NonNull; +import android.app.AppOpsManager; +import android.content.Context; +import android.net.ConnectivityManager; +import android.net.InetAddresses; +import android.net.IpSecTransform; +import android.net.LinkAddress; +import android.net.Network; +import android.net.TestNetworkInterface; +import android.net.TestNetworkManager; +import android.net.annotations.PolicyDirection; +import android.net.ipsec.ike.ChildSessionCallback; +import android.net.ipsec.ike.ChildSessionConfiguration; +import android.net.ipsec.ike.IkeSessionCallback; +import android.net.ipsec.ike.IkeSessionConfiguration; +import android.net.ipsec.ike.IkeTrafficSelector; +import android.net.ipsec.ike.cts.TestNetworkUtils.TestNetworkCallback; +import android.net.ipsec.ike.exceptions.IkeException; +import android.net.ipsec.ike.exceptions.IkeProtocolException; +import android.os.Binder; +import android.os.ParcelFileDescriptor; +import android.platform.test.annotations.AppModeFull; +import android.util.Log; + +import androidx.test.InstrumentationRegistry; +import androidx.test.ext.junit.runners.AndroidJUnit4; + +import com.android.compatibility.common.util.SystemUtil; +import com.android.testutils.ArrayTrackRecord; + +import org.junit.After; +import org.junit.AfterClass; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.runner.RunWith; + +import java.net.Inet4Address; +import java.net.InetAddress; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.Executor; +import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; + +/** + * Package private base class for testing IkeSessionParams and IKE exchanges. + * + *
Subclasses MUST explicitly call #setUpTestNetwork and #tearDownTestNetwork to be able to use
+ * the test network
+ */
+@RunWith(AndroidJUnit4.class)
+@AppModeFull(reason = "MANAGE_TEST_NETWORKS permission can't be granted to instant apps")
+abstract class IkeSessionTestBase extends IkeTestBase {
+ // Package-wide common expected results that will be shared by all IKE/Child SA creation tests
+ static final String EXPECTED_REMOTE_APP_VERSION_EMPTY = "";
+ static final byte[] EXPECTED_PROTOCOL_ERROR_DATA_NONE = new byte[0];
+ static final InetAddress EXPECTED_INTERNAL_ADDR =
+ InetAddresses.parseNumericAddress("198.51.100.10");
+ static final LinkAddress EXPECTED_INTERNAL_LINK_ADDR =
+ new LinkAddress(EXPECTED_INTERNAL_ADDR, IP4_PREFIX_LEN);
+ static final IkeTrafficSelector EXPECTED_INBOUND_TS =
+ new IkeTrafficSelector(
+ MIN_PORT, MAX_PORT, EXPECTED_INTERNAL_ADDR, EXPECTED_INTERNAL_ADDR);
+
+ // Static state to reduce setup/teardown
+ static Context sContext = InstrumentationRegistry.getContext();
+ static ConnectivityManager sCM =
+ (ConnectivityManager) sContext.getSystemService(Context.CONNECTIVITY_SERVICE);
+ static TestNetworkManager sTNM;
+
+ private static final int TIMEOUT_MS = 500;
+
+ // Constants to be used for providing different IP addresses for each tests
+ private static final byte IP_ADDR_LAST_BYTE_MAX = (byte) 100;
+ private static final byte[] INITIAL_AVAILABLE_IP4_ADDR_LOCAL =
+ InetAddresses.parseNumericAddress("192.0.2.1").getAddress();
+ private static final byte[] INITIAL_AVAILABLE_IP4_ADDR_REMOTE =
+ InetAddresses.parseNumericAddress("198.51.100.1").getAddress();
+ private static final byte[] NEXT_AVAILABLE_IP4_ADDR_LOCAL = INITIAL_AVAILABLE_IP4_ADDR_LOCAL;
+ private static final byte[] NEXT_AVAILABLE_IP4_ADDR_REMOTE = INITIAL_AVAILABLE_IP4_ADDR_REMOTE;
+
+ ParcelFileDescriptor mTunFd;
+ TestNetworkCallback mTunNetworkCallback;
+ Network mTunNetwork;
+ IkeTunUtils mTunUtils;
+
+ InetAddress mLocalAddress;
+ InetAddress mRemoteAddress;
+
+ Executor mUserCbExecutor;
+ TestIkeSessionCallback mIkeSessionCallback;
+ TestChildSessionCallback mFirstChildSessionCallback;
+
+ // This method is guaranteed to run in subclasses and will run before subclasses' @BeforeClass
+ // methods.
+ @BeforeClass
+ public static void setUpPermissionBeforeClass() throws Exception {
+ InstrumentationRegistry.getInstrumentation()
+ .getUiAutomation()
+ .adoptShellPermissionIdentity();
+ sTNM = (TestNetworkManager) sContext.getSystemService(Context.TEST_NETWORK_SERVICE);
+
+ // Under normal circumstances, the MANAGE_IPSEC_TUNNELS appop would be auto-granted, and
+ // a standard permission is insufficient. So we shell out the appop, to give us the
+ // right appop permissions.
+ setAppOp(OP_MANAGE_IPSEC_TUNNELS, true);
+ }
+
+ // This method is guaranteed to run in subclasses and will run after subclasses' @AfterClass
+ // methods.
+ @AfterClass
+ public static void tearDownPermissionAfterClass() throws Exception {
+ setAppOp(OP_MANAGE_IPSEC_TUNNELS, false);
+
+ InstrumentationRegistry.getInstrumentation()
+ .getUiAutomation()
+ .dropShellPermissionIdentity();
+ }
+
+ @Before
+ public void setUp() throws Exception {
+ mLocalAddress = getNextAvailableIpv4AddressLocal();
+ mRemoteAddress = getNextAvailableIpv4AddressRemote();
+ setUpTestNetwork(mLocalAddress);
+
+ mUserCbExecutor = Executors.newSingleThreadExecutor();
+ mIkeSessionCallback = new TestIkeSessionCallback();
+ mFirstChildSessionCallback = new TestChildSessionCallback();
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ tearDownTestNetwork();
+
+ resetNextAvailableAddress(NEXT_AVAILABLE_IP4_ADDR_LOCAL, INITIAL_AVAILABLE_IP4_ADDR_LOCAL);
+ resetNextAvailableAddress(
+ NEXT_AVAILABLE_IP4_ADDR_REMOTE, INITIAL_AVAILABLE_IP4_ADDR_REMOTE);
+ }
+
+ void setUpTestNetwork(InetAddress localAddr) throws Exception {
+ int prefixLen = localAddr instanceof Inet4Address ? IP4_PREFIX_LEN : IP4_PREFIX_LEN;
+
+ TestNetworkInterface testIface =
+ sTNM.createTunInterface(new LinkAddress[] {new LinkAddress(localAddr, prefixLen)});
+
+ mTunFd = testIface.getFileDescriptor();
+ mTunNetworkCallback =
+ TestNetworkUtils.setupAndGetTestNetwork(
+ sCM, sTNM, testIface.getInterfaceName(), new Binder());
+ mTunNetwork = mTunNetworkCallback.getNetworkBlocking();
+ mTunUtils = new IkeTunUtils(mTunFd);
+ }
+
+ void tearDownTestNetwork() throws Exception {
+ sCM.unregisterNetworkCallback(mTunNetworkCallback);
+
+ sTNM.teardownTestNetwork(mTunNetwork);
+ mTunFd.close();
+ }
+
+ private static void setAppOp(int appop, boolean allow) {
+ String opName = AppOpsManager.opToName(appop);
+ for (String pkg : new String[] {"com.android.shell", sContext.getPackageName()}) {
+ String cmd =
+ String.format(
+ "appops set %s %s %s",
+ pkg, // Package name
+ opName, // Appop
+ (allow ? "allow" : "deny")); // Action
+ Log.d("IKE", "CTS setAppOp cmd " + cmd);
+
+ String result = SystemUtil.runShellCommand(cmd);
+ }
+ }
+
+ Inet4Address getNextAvailableIpv4AddressLocal() throws Exception {
+ return (Inet4Address)
+ getNextAvailableAddress(
+ NEXT_AVAILABLE_IP4_ADDR_LOCAL,
+ INITIAL_AVAILABLE_IP4_ADDR_LOCAL,
+ false /* isIp6 */);
+ }
+
+ Inet4Address getNextAvailableIpv4AddressRemote() throws Exception {
+ return (Inet4Address)
+ getNextAvailableAddress(
+ NEXT_AVAILABLE_IP4_ADDR_REMOTE,
+ INITIAL_AVAILABLE_IP4_ADDR_REMOTE,
+ false /* isIp6 */);
+ }
+
+ InetAddress getNextAvailableAddress(
+ byte[] nextAddressBytes, byte[] initialAddressBytes, boolean isIp6) throws Exception {
+ int addressLen = isIp6 ? IP6_ADDRESS_LEN : IP4_ADDRESS_LEN;
+
+ synchronized (nextAddressBytes) {
+ if (nextAddressBytes[addressLen - 1] == IP_ADDR_LAST_BYTE_MAX) {
+ resetNextAvailableAddress(nextAddressBytes, initialAddressBytes);
+ }
+
+ InetAddress address = InetAddress.getByAddress(nextAddressBytes);
+ nextAddressBytes[addressLen - 1]++;
+ return address;
+ }
+ }
+
+ private void resetNextAvailableAddress(byte[] nextAddressBytes, byte[] initialAddressBytes) {
+ synchronized (nextAddressBytes) {
+ System.arraycopy(
+ nextAddressBytes, 0, initialAddressBytes, 0, initialAddressBytes.length);
+ }
+ }
+
+ static class TestIkeSessionCallback implements IkeSessionCallback {
+ private CompletableFuture