Add CTS for the liveness checks related functions.
- Test liveness check requests and status callbacks.
Added cts test cases for the newly added liveness check APIs. When
requesting a liveness check for an IkeSession, status callbacks are
notified to the client about the start and result of liveness.
- Test retransmission timeout for liveness purposes for the newly
added on-demand DPD state.
Added cts test cases to test if the on-demand DPD state transmits
packets using the liveness retransmission timeouts and expires.
Bug: 302244230
Test: atest FrameworksIkeTests CtsIkeTestCases
Change-Id: I8f91a694b7d04b133b60d0e2be5498f0cb94ce65
diff --git a/tests/cts/src/android/ipsec/ike/cts/IkeSessionLivenessCheckTest.java b/tests/cts/src/android/ipsec/ike/cts/IkeSessionLivenessCheckTest.java
new file mode 100644
index 0000000..180ec7c
--- /dev/null
+++ b/tests/cts/src/android/ipsec/ike/cts/IkeSessionLivenessCheckTest.java
@@ -0,0 +1,181 @@
+/*
+ * Copyright (C) 2023 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.ipsec.ike.cts;
+
+import static com.android.internal.util.HexDump.hexStringToByteArray;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+
+import android.ipsec.ike.cts.IkeTunUtils.PortPair;
+import android.net.InetAddresses;
+import android.net.LinkAddress;
+import android.net.ipsec.ike.IkeFqdnIdentification;
+import android.net.ipsec.ike.IkeSession;
+import android.net.ipsec.ike.IkeSessionCallback;
+import android.net.ipsec.ike.IkeSessionParams;
+import android.net.ipsec.ike.IkeTrafficSelector;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.net.InetAddress;
+import java.util.ArrayList;
+import java.util.Arrays;
+
+/**
+ * Explicitly test transport mode Child SA so that devices without FEATURE_IPSEC_TUNNELS can be test
+ * covered. Tunnel mode Child SA setup has been tested in IkeSessionPskTest. Rekeying process is
+ * independent from Child SA mode.
+ */
+@RunWith(AndroidJUnit4.class)
+public class IkeSessionLivenessCheckTest extends IkeSessionTestBase {
+
+ // This value is align with the test vectors hex that are generated in an IPv4 environment
+ private static final IkeTrafficSelector TRANSPORT_MODE_INBOUND_TS =
+ new IkeTrafficSelector(
+ MIN_PORT,
+ MAX_PORT,
+ InetAddresses.parseNumericAddress("172.58.35.40"),
+ InetAddresses.parseNumericAddress("172.58.35.40"));
+
+ private byte[] buildInboundPkt(PortPair outPktSrcDestPortPair, String inboundDataHex)
+ throws Exception {
+ // Build inbound packet by flipping the outbound packet addresses and ports
+ return IkeTunUtils.buildIkePacket(
+ mRemoteAddress,
+ mLocalAddress,
+ outPktSrcDestPortPair.dstPort,
+ outPktSrcDestPortPair.srcPort,
+ true /* useEncap */,
+ hexStringToByteArray(inboundDataHex));
+ }
+
+ private IkeSession openIkeSessionWithRemoteAddress(InetAddress remoteAddress) {
+ IkeSessionParams ikeParams =
+ new IkeSessionParams.Builder(sContext)
+ .setNetwork(mTunNetworkContext.tunNetwork)
+ .setServerHostname(remoteAddress.getHostAddress())
+ .addSaProposal(SaProposalTest.buildIkeSaProposalWithNormalModeCipher())
+ .addSaProposal(SaProposalTest.buildIkeSaProposalWithCombinedModeCipher())
+ .setLocalIdentification(new IkeFqdnIdentification(LOCAL_HOSTNAME))
+ .setRemoteIdentification(new IkeFqdnIdentification(REMOTE_HOSTNAME))
+ .setAuthPsk(IKE_PSK)
+ .build();
+ return new IkeSession(
+ sContext,
+ ikeParams,
+ buildTransportModeChildParamsWithTs(
+ TRANSPORT_MODE_INBOUND_TS, TRANSPORT_MODE_OUTBOUND_TS),
+ mUserCbExecutor,
+ mIkeSessionCallback,
+ mFirstChildSessionCallback);
+ }
+
+ @Test
+ public void testIkeLivenessCheck() throws Exception {
+
+ final String ikeInitResp =
+ "46B8ECA1E0D72A1866B5248CF6C7472D21202220000000000000015022000030"
+ + "0000002C010100040300000C0100000C800E0100030000080300000C03000008"
+ + "0200000500000008040000022800008800020000920D3E830E7276908209212D"
+ + "E5A7F2A48706CFEF1BE8CB6E3B173B8B4E0D8C2DC626271FF1B13A88619E569E"
+ + "7B03C3ED2C127390749CDC7CDC711D0A8611E4457FFCBC4F0981B3288FBF58EA"
+ + "3E8B70E27E76AE70117FBBCB753660ADDA37EB5EB3A81BED6A374CCB7E132C2A"
+ + "94BFCE402DC76B19C158B533F6B1F2ABF01ACCC329000024B302CA2FB85B6CF4"
+ + "02313381246E3C53828D787F6DFEA6BD62D6405254AEE6242900001C00004004"
+ + "7A1682B06B58596533D00324886EF1F20EF276032900001C00004005BF633E31"
+ + "F9984B29A62E370BB2770FC09BAEA665290000080000402E290000100000402F"
+ + "00020003000400050000000800004014";
+ final String ikeAuthResp =
+ "46B8ECA1E0D72A1866B5248CF6C7472D2E20232000000001000000F0240000D4"
+ + "10166CA8647F56123DE74C17FA5E256043ABF73216C812EE32EE1BB01EAF4A82"
+ + "DC107AB3ADBFEE0DEA5EEE10BDD5D43178F4C975C7C775D252273BB037283C7F"
+ + "236FE34A6BCE4833816897075DB2055B9FFD66DFA45A0A89A8F70AFB59431EED"
+ + "A20602FB614369D12906D3355CF7298A5D25364ABBCC75A9D88E0E6581449FCD"
+ + "4E361A39E00EFD1FD0A69651F63DB46C12470226AA21BA5EFF48FAF0B6DDF61C"
+ + "B0A69392CE559495EEDB4D1C1D80688434D225D57210A424C213F7C993D8A456"
+ + "38153FBD194C5E247B592D1D048DB4C8";
+ String ikeDpdResp =
+ "46B8ECA1E0D72A1866B5248CF6C7472D2E202520000000020000005000000034"
+ + "DDC1F4421B0377957A0A247B67C14C431567B24CA2849230BA55018717B339C6"
+ + "CB14C18C3B4BB7EA29EBC8556C7C9727";
+ final String deleteIkeReq =
+ "46B8ECA1E0D72A1866B5248CF6C7472D2E20250000000000000000502A000034"
+ + "C6DD62B54C07488D7BB16C5BEF0C0ADD93BCA2AE590CE5E0787B28E4D5DD1DBC"
+ + "F25CA207907B08A7BB5F104879938A01";
+
+ // Open IKE Session
+ IkeSession ikeSession = openIkeSessionWithRemoteAddress(mRemoteAddress);
+ PortPair localRemotePorts = performSetupIkeAndFirstChildBlocking(ikeInitResp, ikeAuthResp);
+
+ // Local request message ID starts from 2 because there is one IKE_INIT message and a single
+ // IKE_AUTH message.
+ int expectedReqMsgId = 2;
+ int expectedRespMsgId = 0;
+
+ verifyIkeSessionSetupBlocking();
+ verifyChildSessionSetupBlocking(
+ mFirstChildSessionCallback,
+ Arrays.asList(TRANSPORT_MODE_INBOUND_TS),
+ Arrays.asList(TRANSPORT_MODE_OUTBOUND_TS),
+ new ArrayList<LinkAddress>());
+ IpSecTransformCallRecord firstTransformRecordA =
+ mFirstChildSessionCallback.awaitNextCreatedIpSecTransform();
+ IpSecTransformCallRecord firstTransformRecordB =
+ mFirstChildSessionCallback.awaitNextCreatedIpSecTransform();
+ verifyCreateIpSecTransformPair(firstTransformRecordA, firstTransformRecordB);
+
+ // request the Liveness check twice
+ ikeSession.requestLivenessCheck();
+ ikeSession.requestLivenessCheck();
+
+ int livenessStatus = mIkeSessionCallback.awaitNextOnLivenessStatus();
+ if (livenessStatus == IkeSessionCallback.LIVENESS_STATUS_ON_DEMAND_STARTED) {
+ assertEquals(
+ IkeSessionCallback.LIVENESS_STATUS_ON_DEMAND_ONGOING,
+ mIkeSessionCallback.awaitNextOnLivenessStatus());
+ } else if (livenessStatus == IkeSessionCallback.LIVENESS_STATUS_BACKGROUND_STARTED) {
+ assertEquals(
+ IkeSessionCallback.LIVENESS_STATUS_BACKGROUND_ONGOING,
+ mIkeSessionCallback.awaitNextOnLivenessStatus());
+ } else {
+ fail("Unexpected status.");
+ }
+
+ mTunNetworkContext.tunUtils.awaitReqAndInjectResp(
+ IKE_DETERMINISTIC_INITIATOR_SPI,
+ expectedReqMsgId,
+ true /* expectedUseEncap */,
+ ikeDpdResp);
+
+ livenessStatus = mIkeSessionCallback.awaitNextOnLivenessStatus();
+ assertEquals(IkeSessionCallback.LIVENESS_STATUS_SUCCESS, livenessStatus);
+
+ // Inject delete IKE request
+ mTunNetworkContext.tunUtils.injectPacket(buildInboundPkt(localRemotePorts, deleteIkeReq));
+ mTunNetworkContext.tunUtils.awaitResp(
+ IKE_DETERMINISTIC_INITIATOR_SPI, expectedRespMsgId++, true /* expectedUseEncap */);
+
+ verifyDeleteIpSecTransformPair(
+ mFirstChildSessionCallback, firstTransformRecordA, firstTransformRecordB);
+ mFirstChildSessionCallback.awaitOnClosed();
+ mIkeSessionCallback.awaitOnClosed();
+ }
+}
diff --git a/tests/cts/src/android/ipsec/ike/cts/IkeSessionParamsTest.java b/tests/cts/src/android/ipsec/ike/cts/IkeSessionParamsTest.java
index 24f5a3f..a759ec5 100644
--- a/tests/cts/src/android/ipsec/ike/cts/IkeSessionParamsTest.java
+++ b/tests/cts/src/android/ipsec/ike/cts/IkeSessionParamsTest.java
@@ -87,6 +87,8 @@
private static final int DPD_DELAY_SECONDS = (int) TimeUnit.MINUTES.toSeconds(10L);
private static final int NATT_KEEPALIVE_DELAY_SECONDS = (int) TimeUnit.MINUTES.toSeconds(5L);
private static final int[] RETRANS_TIMEOUT_MS_LIST = new int[] {500, 500, 500, 500, 500, 500};
+ private static final int[] LIVENESS_RETRANS_TIMEOUT_MS_LIST =
+ new int[] {500, 500, 500, 500, 500, 500};
private static final int DSCP = 8;
@@ -548,4 +550,17 @@
verifyIkeParamsMinimum(sessionParams);
assertEquals(ESP_ENCAP_TYPE_UDP, sessionParams.getEncapType());
}
+
+ @Test
+ public void testSetLivenessRetransmissionTimeouts() throws Exception {
+ IkeSessionParams sessionParams =
+ createIkeParamsBuilderMinimum()
+ .setLivenessRetransmissionTimeoutsMillis(LIVENESS_RETRANS_TIMEOUT_MS_LIST)
+ .build();
+
+ verifyIkeParamsMinimum(sessionParams);
+ assertArrayEquals(
+ LIVENESS_RETRANS_TIMEOUT_MS_LIST,
+ sessionParams.getLivenessRetransmissionTimeoutsMillis());
+ }
}
diff --git a/tests/cts/src/android/ipsec/ike/cts/IkeSessionTestBase.java b/tests/cts/src/android/ipsec/ike/cts/IkeSessionTestBase.java
index be616c3..c1bba54 100644
--- a/tests/cts/src/android/ipsec/ike/cts/IkeSessionTestBase.java
+++ b/tests/cts/src/android/ipsec/ike/cts/IkeSessionTestBase.java
@@ -330,6 +330,9 @@
protected CompletableFuture<IkeException> mFutureOnClosedException =
new CompletableFuture<>();
+ private int mOnLivenessStatusCount = 0;
+ private ArrayTrackRecord<Integer> mOnLivenessStatusTrackRecord = new ArrayTrackRecord<>();
+
@Override
public void onOpened(@NonNull IkeSessionConfiguration sessionConfiguration) {
mFutureIkeConfig.complete(sessionConfiguration);
@@ -353,6 +356,12 @@
mFutureConnectionConfig.complete(connectionInfo);
}
+ @Override
+ public void onLivenessStatusChanged(int livenessStatus) {
+ IkeSessionCallback.super.onLivenessStatusChanged(livenessStatus);
+ mOnLivenessStatusTrackRecord.add(livenessStatus);
+ }
+
public IkeSessionConfiguration awaitIkeConfig() throws Exception {
return mFutureIkeConfig.get(TIMEOUT_MS, TimeUnit.MILLISECONDS);
}
@@ -377,6 +386,15 @@
public IkeSessionConnectionInfo awaitOnIkeSessionConnectionInfoChanged() throws Exception {
return mFutureConnectionConfig.get(TIMEOUT_MS, TimeUnit.MILLISECONDS);
}
+
+ public int awaitNextOnLivenessStatus() throws Exception {
+ return mOnLivenessStatusTrackRecord.poll(
+ (long) TIMEOUT_MS,
+ mOnLivenessStatusCount++,
+ (transform) -> {
+ return true;
+ });
+ }
}
/** Default testing callback for all IKE exchange tests */