Merge "Initial CL for testing IkeSession creation"
diff --git a/tests/cts/net/ipsec/Android.bp b/tests/cts/net/ipsec/Android.bp
index f1f120b..16bdb05 100644
--- a/tests/cts/net/ipsec/Android.bp
+++ b/tests/cts/net/ipsec/Android.bp
@@ -33,6 +33,7 @@
"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 6fc7cb3..c767b78 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 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.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 @@
@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 @@
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 @@
*/
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 @@
* @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 @@
*/
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 @@
* @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 c3e3ba3..0000000
--- 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 0000000..ed67dd1
--- /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 0000000..deba8fd
--- /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.
+ *
+ * <p>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<IkeSessionConfiguration> mFutureIkeConfig =
+ new CompletableFuture<>();
+ private CompletableFuture<Boolean> mFutureOnClosedCall = new CompletableFuture<>();
+ private CompletableFuture<IkeException> mFutureOnClosedException =
+ new CompletableFuture<>();
+
+ private int mOnErrorExceptionsCount = 0;
+ private ArrayTrackRecord<IkeProtocolException> 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<ChildSessionConfiguration> mFutureChildConfig =
+ new CompletableFuture<>();
+ private CompletableFuture<Boolean> mFutureOnClosedCall = new CompletableFuture<>();
+ private CompletableFuture<IkeException> mFutureOnClosedException =
+ new CompletableFuture<>();
+
+ private int mCreatedIpSecTransformCount = 0;
+ private int mDeletedIpSecTransformCount = 0;
+ private ArrayTrackRecord<IpSecTransformCallRecord> mCreatedIpSecTransformsTrackRecord =
+ new ArrayTrackRecord<>();
+ private ArrayTrackRecord<IpSecTransformCallRecord> 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 bc2bec6..f07c710 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 @@
/** 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 0000000..5a8258d
--- /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 71450ea..cb1d826 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 @@
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<byte[]> mPackets = new ArrayList<>();
private final ParcelFileDescriptor mTunFd;
- private final List<byte[]> mPackets = new ArrayList<>();
private final Thread mReaderThread;
public TunUtils(ParcelFileDescriptor tunFd) {
@@ -107,7 +107,7 @@
return Arrays.copyOf(inBytes, bytesRead);
}
- private byte[] getFirstMatchingPacket(Predicate<byte[]> verifier, int startIndex) {
+ byte[] getFirstMatchingPacket(Predicate<byte[]> verifier, int startIndex) {
synchronized (mPackets) {
for (int i = startIndex; i < mPackets.size(); i++) {
byte[] pkt = mPackets.get(i);
@@ -198,7 +198,7 @@
}
}
- 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;
}