From 653d7ebb447e00753779634090381ba1fde5077e Mon Sep 17 00:00:00 2001 From: evitayan Date: Tue, 7 Apr 2020 15:16:08 -0700 Subject: [PATCH] Initial CL for testing IkeSession creation This commit: -Extend TunUtils for processing IKE packets -Add IkeSessionBaseTest containing common functionality for all IkeSession tests -Add end-to-end test for IKEv2 PSK verifying creating IKE SA, creating child SAs and closing sessions -Add basic tests for error scenarios Bug: 148689509 Test: atest CtsIkeTestCases Change-Id: Ie6c18591ffcc883abbf0484d9a59dfda61b33257 --- tests/cts/net/ipsec/Android.bp | 1 + .../ipsec/ike/cts/IkeSessionParamsTest.java | 19 +- .../ike/cts/IkeSessionParamsTestBase.java | 85 ---- .../net/ipsec/ike/cts/IkeSessionPskTest.java | 258 ++++++++++++ .../net/ipsec/ike/cts/IkeSessionTestBase.java | 374 ++++++++++++++++++ .../net/ipsec/ike/cts/IkeTestBase.java | 6 +- .../net/ipsec/ike/cts/IkeTunUtils.java | 243 ++++++++++++ .../android/net/ipsec/ike/cts/TunUtils.java | 20 +- 8 files changed, 904 insertions(+), 102 deletions(-) delete mode 100644 tests/cts/net/ipsec/src/android/net/ipsec/ike/cts/IkeSessionParamsTestBase.java create mode 100644 tests/cts/net/ipsec/src/android/net/ipsec/ike/cts/IkeSessionPskTest.java create mode 100644 tests/cts/net/ipsec/src/android/net/ipsec/ike/cts/IkeSessionTestBase.java create mode 100644 tests/cts/net/ipsec/src/android/net/ipsec/ike/cts/IkeTunUtils.java 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 mFutureIkeConfig = + new CompletableFuture<>(); + private CompletableFuture mFutureOnClosedCall = new CompletableFuture<>(); + private CompletableFuture mFutureOnClosedException = + new CompletableFuture<>(); + + private int mOnErrorExceptionsCount = 0; + private ArrayTrackRecord mOnErrorExceptionsTrackRecord = + new ArrayTrackRecord<>(); + + @Override + public void onOpened(@NonNull IkeSessionConfiguration sessionConfiguration) { + mFutureIkeConfig.complete(sessionConfiguration); + } + + @Override + public void onClosed() { + mFutureOnClosedCall.complete(true /* unused */); + } + + @Override + public void onClosedExceptionally(@NonNull IkeException exception) { + mFutureOnClosedException.complete(exception); + } + + @Override + public void onError(@NonNull IkeProtocolException exception) { + mOnErrorExceptionsTrackRecord.add(exception); + } + + public IkeSessionConfiguration awaitIkeConfig() throws Exception { + return mFutureIkeConfig.get(TIMEOUT_MS, TimeUnit.MILLISECONDS); + } + + public IkeException awaitOnClosedException() throws Exception { + return mFutureOnClosedException.get(TIMEOUT_MS, TimeUnit.MILLISECONDS); + } + + public IkeProtocolException awaitNextOnErrorException() { + return mOnErrorExceptionsTrackRecord.poll( + (long) TIMEOUT_MS, + mOnErrorExceptionsCount++, + (transform) -> { + return true; + }); + } + + public void awaitOnClosed() throws Exception { + mFutureOnClosedCall.get(TIMEOUT_MS, TimeUnit.MILLISECONDS); + } + } + + static class TestChildSessionCallback implements ChildSessionCallback { + private CompletableFuture mFutureChildConfig = + new CompletableFuture<>(); + private CompletableFuture mFutureOnClosedCall = new CompletableFuture<>(); + private CompletableFuture mFutureOnClosedException = + new CompletableFuture<>(); + + private int mCreatedIpSecTransformCount = 0; + private int mDeletedIpSecTransformCount = 0; + private ArrayTrackRecord mCreatedIpSecTransformsTrackRecord = + new ArrayTrackRecord<>(); + private ArrayTrackRecord mDeletedIpSecTransformsTrackRecord = + new ArrayTrackRecord<>(); + + @Override + public void onOpened(@NonNull ChildSessionConfiguration sessionConfiguration) { + mFutureChildConfig.complete(sessionConfiguration); + } + + @Override + public void onClosed() { + mFutureOnClosedCall.complete(true /* unused */); + } + + @Override + public void onClosedExceptionally(@NonNull IkeException exception) { + mFutureOnClosedException.complete(exception); + } + + @Override + public void onIpSecTransformCreated(@NonNull IpSecTransform ipSecTransform, int direction) { + mCreatedIpSecTransformsTrackRecord.add( + new IpSecTransformCallRecord(ipSecTransform, direction)); + } + + @Override + public void onIpSecTransformDeleted(@NonNull IpSecTransform ipSecTransform, int direction) { + mDeletedIpSecTransformsTrackRecord.add( + new IpSecTransformCallRecord(ipSecTransform, direction)); + } + + public ChildSessionConfiguration awaitChildConfig() throws Exception { + return mFutureChildConfig.get(TIMEOUT_MS, TimeUnit.MILLISECONDS); + } + + public IkeException awaitOnClosedException() throws Exception { + return mFutureOnClosedException.get(TIMEOUT_MS, TimeUnit.MILLISECONDS); + } + + public IpSecTransformCallRecord awaitNextCreatedIpSecTransform() { + return mCreatedIpSecTransformsTrackRecord.poll( + (long) TIMEOUT_MS, + mCreatedIpSecTransformCount++, + (transform) -> { + return true; + }); + } + + public IpSecTransformCallRecord awaitNextDeletedIpSecTransform() { + return mDeletedIpSecTransformsTrackRecord.poll( + (long) TIMEOUT_MS, + mDeletedIpSecTransformCount++, + (transform) -> { + return true; + }); + } + + public void awaitOnClosed() throws Exception { + mFutureOnClosedCall.get(TIMEOUT_MS, TimeUnit.MILLISECONDS); + } + } + + /** + * This class represents a created or deleted IpSecTransfrom that is provided by + * ChildSessionCallback + */ + static class IpSecTransformCallRecord { + public final IpSecTransform ipSecTransform; + public final int direction; + + IpSecTransformCallRecord(IpSecTransform ipSecTransform, @PolicyDirection int direction) { + this.ipSecTransform = ipSecTransform; + this.direction = direction; + } + } + + // TODO(b/148689509): Verify IKE Session setup using EAP and digital-signature-based auth + + // TODO(b/148689509): Verify hostname based creation +} diff --git a/tests/cts/net/ipsec/src/android/net/ipsec/ike/cts/IkeTestBase.java b/tests/cts/net/ipsec/src/android/net/ipsec/ike/cts/IkeTestBase.java index bc2bec665f..f07c710ba9 100644 --- a/tests/cts/net/ipsec/src/android/net/ipsec/ike/cts/IkeTestBase.java +++ b/tests/cts/net/ipsec/src/android/net/ipsec/ike/cts/IkeTestBase.java @@ -31,13 +31,15 @@ import java.util.Map; /** Shared parameters and util methods for testing different components of IKE */ abstract class IkeTestBase { - private static final int MIN_PORT = 0; - private static final int MAX_PORT = 65535; + static final int MIN_PORT = 0; + static final int MAX_PORT = 65535; private static final int INBOUND_TS_START_PORT = MIN_PORT; private static final int INBOUND_TS_END_PORT = 65520; private static final int OUTBOUND_TS_START_PORT = 16; private static final int OUTBOUND_TS_END_PORT = MAX_PORT; + static final int IP4_ADDRESS_LEN = 4; + static final int IP6_ADDRESS_LEN = 16; static final int IP4_PREFIX_LEN = 32; static final int IP6_PREFIX_LEN = 64; diff --git a/tests/cts/net/ipsec/src/android/net/ipsec/ike/cts/IkeTunUtils.java b/tests/cts/net/ipsec/src/android/net/ipsec/ike/cts/IkeTunUtils.java new file mode 100644 index 0000000000..5a8258d57b --- /dev/null +++ b/tests/cts/net/ipsec/src/android/net/ipsec/ike/cts/IkeTunUtils.java @@ -0,0 +1,243 @@ +/* + * 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.cts.PacketUtils.BytePayload; +import static android.net.ipsec.ike.cts.PacketUtils.IP4_HDRLEN; +import static android.net.ipsec.ike.cts.PacketUtils.IP6_HDRLEN; +import static android.net.ipsec.ike.cts.PacketUtils.Ip4Header; +import static android.net.ipsec.ike.cts.PacketUtils.Ip6Header; +import static android.net.ipsec.ike.cts.PacketUtils.IpHeader; +import static android.net.ipsec.ike.cts.PacketUtils.Payload; +import static android.net.ipsec.ike.cts.PacketUtils.UDP_HDRLEN; +import static android.net.ipsec.ike.cts.PacketUtils.UdpHeader; +import static android.system.OsConstants.IPPROTO_UDP; + +import static org.junit.Assert.fail; + +import android.os.ParcelFileDescriptor; + +import java.net.Inet4Address; +import java.net.Inet6Address; +import java.net.InetAddress; +import java.nio.ByteBuffer; +import java.util.Arrays; + +public class IkeTunUtils extends TunUtils { + private static final int PORT_LEN = 2; + + private static final int NON_ESP_MARKER_LEN = 4; + private static final byte[] NON_ESP_MARKER = new byte[NON_ESP_MARKER_LEN]; + + private static final int IKE_HEADER_LEN = 28; + private static final int IKE_INIT_SPI_OFFSET = 0; + private static final int IKE_IS_RESP_BYTE_OFFSET = 19; + private static final int IKE_MSG_ID_OFFSET = 20; + + public IkeTunUtils(ParcelFileDescriptor tunFd) { + super(tunFd); + } + + /** + * Await the expected IKE request and inject an IKE response. + * + * @param respIkePkt IKE response packet without IP/UDP headers or NON ESP MARKER. + */ + public byte[] awaitReqAndInjectResp( + long expectedInitIkeSpi, int expectedMsgId, boolean expectedUseEncap, byte[] respIkePkt) + throws Exception { + byte[] request = + awaitIkePacket( + expectedInitIkeSpi, + expectedMsgId, + false /* expectedResp */, + expectedUseEncap); + + // Build response header by flipping address and port + InetAddress srcAddr = getAddress(request, false /* shouldGetSource */); + InetAddress dstAddr = getAddress(request, true /* shouldGetSource */); + int srcPort = getPort(request, false /* shouldGetSource */); + int dstPort = getPort(request, true /* shouldGetSource */); + + byte[] response = + buildIkePacket(srcAddr, dstAddr, srcPort, dstPort, expectedUseEncap, respIkePkt); + injectPacket(response); + return request; + } + + private byte[] awaitIkePacket( + long expectedInitIkeSpi, + int expectedMsgId, + boolean expectedResp, + boolean expectedUseEncap) + throws Exception { + long endTime = System.currentTimeMillis() + TIMEOUT; + int startIndex = 0; + synchronized (mPackets) { + while (System.currentTimeMillis() < endTime) { + byte[] ikePkt = + getFirstMatchingPacket( + (pkt) -> { + return isIke( + pkt, + expectedInitIkeSpi, + expectedMsgId, + expectedResp, + expectedUseEncap); + }, + startIndex); + if (ikePkt != null) { + return ikePkt; // We've found the packet we're looking for. + } + + startIndex = mPackets.size(); + + // Try to prevent waiting too long. If waitTimeout <= 0, we've already hit timeout + long waitTimeout = endTime - System.currentTimeMillis(); + if (waitTimeout > 0) { + mPackets.wait(waitTimeout); + } + } + + String direction = expectedResp ? "response" : "request"; + fail( + "No such IKE " + + direction + + " found with Initiator SPI " + + expectedInitIkeSpi + + " and message ID " + + expectedMsgId); + } + return null; + } + + private static boolean isIke( + byte[] pkt, + long expectedInitIkeSpi, + int expectedMsgId, + boolean expectedResp, + boolean expectedUseEncap) { + int ipProtocolOffset = 0; + int ikeOffset = 0; + if (isIpv6(pkt)) { + // IPv6 UDP expectedUseEncap not supported by kernels; assume non-expectedUseEncap. + ipProtocolOffset = IP6_PROTO_OFFSET; + ikeOffset = IP6_HDRLEN + UDP_HDRLEN; + } else { + // Use default IPv4 header length (assuming no options) + ipProtocolOffset = IP4_PROTO_OFFSET; + ikeOffset = IP4_HDRLEN + UDP_HDRLEN; + + if (expectedUseEncap) { + if (hasNonEspMarker(pkt)) { + ikeOffset += NON_ESP_MARKER_LEN; + } else { + return false; + } + } + } + + return pkt[ipProtocolOffset] == IPPROTO_UDP + && areSpiAndMsgIdEqual( + pkt, ikeOffset, expectedInitIkeSpi, expectedMsgId, expectedResp); + } + + private static boolean hasNonEspMarker(byte[] pkt) { + ByteBuffer buffer = ByteBuffer.wrap(pkt); + int ikeOffset = IP4_HDRLEN + UDP_HDRLEN; + if (buffer.remaining() < ikeOffset) return false; + + buffer.get(new byte[ikeOffset]); // Skip IP and UDP header + byte[] nonEspMarker = new byte[NON_ESP_MARKER_LEN]; + if (buffer.remaining() < NON_ESP_MARKER_LEN) return false; + + buffer.get(nonEspMarker); + return Arrays.equals(NON_ESP_MARKER, nonEspMarker); + } + + private static boolean areSpiAndMsgIdEqual( + byte[] pkt, + int ikeOffset, + long expectedIkeInitSpi, + int expectedMsgId, + boolean expectedResp) { + if (pkt.length <= ikeOffset + IKE_HEADER_LEN) return false; + + ByteBuffer buffer = ByteBuffer.wrap(pkt); + buffer.get(new byte[ikeOffset]); // Skip IP, UDP header (and NON_ESP_MARKER) + + // Check message ID. + buffer.get(new byte[IKE_MSG_ID_OFFSET]); + int msgId = buffer.getInt(); + return expectedMsgId == msgId; + + // TODO: Check SPI and packet direction + } + + private static InetAddress getAddress(byte[] pkt, boolean shouldGetSource) throws Exception { + int ipLen = isIpv6(pkt) ? IP6_ADDR_LEN : IP4_ADDR_LEN; + int srcIpOffset = isIpv6(pkt) ? IP6_ADDR_OFFSET : IP4_ADDR_OFFSET; + int ipOffset = shouldGetSource ? srcIpOffset : srcIpOffset + ipLen; + + ByteBuffer buffer = ByteBuffer.wrap(pkt); + buffer.get(new byte[ipOffset]); + byte[] ipAddrBytes = new byte[ipLen]; + buffer.get(ipAddrBytes); + return InetAddress.getByAddress(ipAddrBytes); + } + + private static int getPort(byte[] pkt, boolean shouldGetSource) { + ByteBuffer buffer = ByteBuffer.wrap(pkt); + int srcPortOffset = isIpv6(pkt) ? IP6_HDRLEN : IP4_HDRLEN; + int portOffset = shouldGetSource ? srcPortOffset : srcPortOffset + PORT_LEN; + + buffer.get(new byte[portOffset]); + return Short.toUnsignedInt(buffer.getShort()); + } + + private static byte[] buildIkePacket( + InetAddress srcAddr, + InetAddress dstAddr, + int srcPort, + int dstPort, + boolean useEncap, + byte[] ikePacket) + throws Exception { + if (useEncap) { + ByteBuffer buffer = ByteBuffer.allocate(NON_ESP_MARKER_LEN + ikePacket.length); + buffer.put(NON_ESP_MARKER); + buffer.put(ikePacket); + ikePacket = buffer.array(); + } + + UdpHeader udpPkt = new UdpHeader(srcPort, dstPort, new BytePayload(ikePacket)); + IpHeader ipPkt = getIpHeader(udpPkt.getProtocolId(), srcAddr, dstAddr, udpPkt); + return ipPkt.getPacketBytes(); + } + + private static IpHeader getIpHeader( + int protocol, InetAddress src, InetAddress dst, Payload payload) { + if ((src instanceof Inet6Address) != (dst instanceof Inet6Address)) { + throw new IllegalArgumentException("Invalid src/dst address combination"); + } + + if (src instanceof Inet6Address) { + return new Ip6Header(protocol, (Inet6Address) src, (Inet6Address) dst, payload); + } else { + return new Ip4Header(protocol, (Inet4Address) src, (Inet4Address) dst, payload); + } + } +} diff --git a/tests/cts/net/ipsec/src/android/net/ipsec/ike/cts/TunUtils.java b/tests/cts/net/ipsec/src/android/net/ipsec/ike/cts/TunUtils.java index 71450ea9c0..cb1d8269d7 100644 --- a/tests/cts/net/ipsec/src/android/net/ipsec/ike/cts/TunUtils.java +++ b/tests/cts/net/ipsec/src/android/net/ipsec/ike/cts/TunUtils.java @@ -47,18 +47,18 @@ public class TunUtils { private static final String TAG = TunUtils.class.getSimpleName(); private static final int DATA_BUFFER_LEN = 4096; - private static final int TIMEOUT = 100; + static final int TIMEOUT = 100; - private static final int IP4_PROTO_OFFSET = 9; - private static final int IP6_PROTO_OFFSET = 6; + static final int IP4_PROTO_OFFSET = 9; + static final int IP6_PROTO_OFFSET = 6; - private static final int IP4_ADDR_OFFSET = 12; - private static final int IP4_ADDR_LEN = 4; - private static final int IP6_ADDR_OFFSET = 8; - private static final int IP6_ADDR_LEN = 16; + static final int IP4_ADDR_OFFSET = 12; + static final int IP4_ADDR_LEN = 4; + static final int IP6_ADDR_OFFSET = 8; + static final int IP6_ADDR_LEN = 16; + final List mPackets = new ArrayList<>(); private final ParcelFileDescriptor mTunFd; - private final List mPackets = new ArrayList<>(); private final Thread mReaderThread; public TunUtils(ParcelFileDescriptor tunFd) { @@ -107,7 +107,7 @@ public class TunUtils { return Arrays.copyOf(inBytes, bytesRead); } - private byte[] getFirstMatchingPacket(Predicate verifier, int startIndex) { + byte[] getFirstMatchingPacket(Predicate verifier, int startIndex) { synchronized (mPackets) { for (int i = startIndex; i < mPackets.size(); i++) { byte[] pkt = mPackets.get(i); @@ -198,7 +198,7 @@ public class TunUtils { } } - private static boolean isIpv6(byte[] pkt) { + static boolean isIpv6(byte[] pkt) { // First nibble shows IP version. 0x60 for IPv6 return (pkt[0] & (byte) 0xF0) == (byte) 0x60; }