aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorandroid-build-prod (mdb) <android-build-team-robot@google.com>2020-05-27 20:46:25 +0000
committerandroid-build-prod (mdb) <android-build-team-robot@google.com>2020-05-27 20:46:25 +0000
commit3af91ef562a04671972c67e6dfebd8c3f41c7ddc (patch)
tree1e323e9f3e4c248818245dc3da05816d98641a1e
parentfa195b2982f4e36fd131f8f95905356119237f3e (diff)
parent0ae59f085b7e5f718bc033a5621eee5a99a48bf9 (diff)
downloadike-sdk-release.tar.gz
Snap for 6533464 from 0ae59f085b7e5f718bc033a5621eee5a99a48bf9 to sdk-releaseplatform-tools-30.0.3platform-tools-30.0.2sdk-release
Change-Id: Ibe51bbf114f3364a2a75c0d5f433da84db7dd5b2
-rw-r--r--Android.bp37
-rw-r--r--api/system-lint-baseline.txt75
-rw-r--r--src/java/android/net/ipsec/ike/ChildSaProposal.java19
-rw-r--r--src/java/android/net/ipsec/ike/ChildSessionParams.java16
-rw-r--r--src/java/android/net/ipsec/ike/SaProposal.java55
-rw-r--r--src/java/android/net/ipsec/ike/TunnelModeChildSessionParams.java34
-rw-r--r--src/java/com/android/internal/net/ipsec/ike/AbstractSessionStateMachine.java3
-rw-r--r--src/java/com/android/internal/net/ipsec/ike/ChildSessionStateMachine.java155
-rw-r--r--src/java/com/android/internal/net/ipsec/ike/IkeLocalRequestScheduler.java7
-rw-r--r--src/java/com/android/internal/net/ipsec/ike/IkeSessionStateMachine.java282
-rw-r--r--src/java/com/android/internal/net/ipsec/ike/IkeSocket.java68
-rw-r--r--src/java/com/android/internal/net/ipsec/ike/IkeUdpEncapSocket.java23
-rw-r--r--src/java/com/android/internal/net/ipsec/ike/IkeUdpSocket.java13
-rw-r--r--src/java/com/android/internal/net/ipsec/ike/SaRecord.java70
-rw-r--r--src/java/com/android/internal/net/ipsec/ike/message/IkeCertReqPayload.java97
-rw-r--r--src/java/com/android/internal/net/ipsec/ike/message/IkeConfigPayload.java3
-rw-r--r--src/java/com/android/internal/net/ipsec/ike/message/IkeMessage.java14
-rw-r--r--src/java/com/android/internal/net/ipsec/ike/message/IkeNotifyPayload.java6
-rw-r--r--src/java/com/android/internal/net/ipsec/ike/message/IkePayloadFactory.java3
-rw-r--r--src/java/com/android/internal/net/ipsec/ike/message/IkeSaPayload.java15
-rw-r--r--src/java/com/android/internal/net/ipsec/ike/utils/RandomnessFactory.java5
-rw-r--r--tests/iketests/AndroidManifest.xml2
-rw-r--r--tests/iketests/src/java/android/net/ipsec/ike/SaProposalTest.java33
-rw-r--r--tests/iketests/src/java/com/android/internal/net/ipsec/ike/ChildSessionStateMachineTest.java169
-rw-r--r--tests/iketests/src/java/com/android/internal/net/ipsec/ike/IkeSessionStateMachineTest.java211
-rw-r--r--tests/iketests/src/java/com/android/internal/net/ipsec/ike/IkeSessionTestBase.java14
-rw-r--r--tests/iketests/src/java/com/android/internal/net/ipsec/ike/IkeUdpEncapSocketTest.java22
-rw-r--r--tests/iketests/src/java/com/android/internal/net/ipsec/ike/SaRecordTest.java20
-rw-r--r--tests/iketests/src/java/com/android/internal/net/ipsec/ike/message/IkeCertReqPayloadTest.java71
-rw-r--r--tests/iketests/src/java/com/android/internal/net/ipsec/ike/message/IkeNotifyPayloadTest.java13
30 files changed, 1094 insertions, 461 deletions
diff --git a/Android.bp b/Android.bp
index 8e7717aa..01c07621 100644
--- a/Android.bp
+++ b/Android.bp
@@ -14,8 +14,11 @@
java_sdk_library {
name: "android.net.ipsec.ike",
+ defaults: ["framework-module-defaults"],
installable: true,
- sdk_version: "module_current",
+
+ // ike is used as a shared library.
+ shared_library: true,
aidl: {
local_include_dirs: ["src/java"],
@@ -28,7 +31,6 @@ java_sdk_library {
libs: [
"unsupportedappusage",
- "framework-annotations-lib",
],
api_packages: [
@@ -41,13 +43,13 @@ java_sdk_library {
// being overwritten by the frameworks class copies.
jarjar_rules: "jarjar-rules-shared.txt",
- plugins: ["java_api_finder"],
-
hostdex: true, // for hiddenapi check
apex_available: [
"com.android.ipsec",
"test_com.android.ipsec",
],
+
+ stubs_library_visibility: ["//visibility:public"],
}
filegroup {
@@ -86,30 +88,3 @@ java_library {
// being overwritten by the frameworks class copies.
jarjar_rules: "jarjar-rules-shared.txt",
}
-
-stubs_defaults {
- name: "ike-stubs-defaults",
- srcs: [":ike-api-srcs"],
-}
-
-droidstubs {
- name: "android.net.ipsec.ike.api.sources.module_libs_api",
- defaults: [
- "framework-module-api-defaults-module_libs_api",
- "ike-stubs-defaults",
- ],
-}
-
-droidstubs {
- name: "android.net.ipsec.ike.stubs.sources.module_libs_api",
- defaults: [
- "framework-module-stubs-defaults-module_libs_api",
- "ike-stubs-defaults",
- ],
-}
-
-java_library {
- name: "android.net.ipsec.ike.stubs.module_libs_api",
- srcs: [":android.net.ipsec.ike.stubs.sources.module_libs_api"],
- defaults: ["framework-module-stubs-lib-defaults-module_libs_api"],
-}
diff --git a/api/system-lint-baseline.txt b/api/system-lint-baseline.txt
new file mode 100644
index 00000000..bec76838
--- /dev/null
+++ b/api/system-lint-baseline.txt
@@ -0,0 +1,75 @@
+// Baseline format: 1.0
+BuilderSetStyle: android.net.ipsec.ike.IkeSessionParams.Builder#removeIkeOption(int):
+ Builder methods names should use setFoo() / addFoo() / clearFoo() style: method android.net.ipsec.ike.IkeSessionParams.Builder.removeIkeOption(int)
+
+
+CallbackInterface: android.net.ipsec.ike.ChildSessionCallback:
+ Callbacks must be abstract class instead of interface to enable extension in future API levels: ChildSessionCallback
+CallbackInterface: android.net.ipsec.ike.IkeSessionCallback:
+ Callbacks must be abstract class instead of interface to enable extension in future API levels: IkeSessionCallback
+
+
+MethodNameUnits: android.net.ipsec.ike.IkeSessionParams#getDpdDelaySeconds():
+ Returned time values must be in milliseconds, was `getDpdDelaySeconds`
+
+
+MissingGetterMatchingBuilder: android.net.eap.EapSessionConfig.Builder#setEapMsChapV2Config(String, String):
+ android.net.eap.EapSessionConfig does not declare a `getEapMsChapV2Config()` method matching method android.net.eap.EapSessionConfig.Builder.setEapMsChapV2Config(String,String)
+MissingGetterMatchingBuilder: android.net.ipsec.ike.ChildSaProposal.Builder#addDhGroup(int):
+ android.net.ipsec.ike.ChildSaProposal does not declare a `getDhGroups()` method matching method android.net.ipsec.ike.ChildSaProposal.Builder.addDhGroup(int)
+MissingGetterMatchingBuilder: android.net.ipsec.ike.ChildSaProposal.Builder#addEncryptionAlgorithm(int, int):
+ android.net.ipsec.ike.ChildSaProposal does not declare a `getEncryptionAlgorithms()` method matching method android.net.ipsec.ike.ChildSaProposal.Builder.addEncryptionAlgorithm(int,int)
+MissingGetterMatchingBuilder: android.net.ipsec.ike.ChildSaProposal.Builder#addIntegrityAlgorithm(int):
+ android.net.ipsec.ike.ChildSaProposal does not declare a `getIntegrityAlgorithms()` method matching method android.net.ipsec.ike.ChildSaProposal.Builder.addIntegrityAlgorithm(int)
+MissingGetterMatchingBuilder: android.net.ipsec.ike.IkeSaProposal.Builder#addDhGroup(int):
+ android.net.ipsec.ike.IkeSaProposal does not declare a `getDhGroups()` method matching method android.net.ipsec.ike.IkeSaProposal.Builder.addDhGroup(int)
+MissingGetterMatchingBuilder: android.net.ipsec.ike.IkeSaProposal.Builder#addEncryptionAlgorithm(int, int):
+ android.net.ipsec.ike.IkeSaProposal does not declare a `getEncryptionAlgorithms()` method matching method android.net.ipsec.ike.IkeSaProposal.Builder.addEncryptionAlgorithm(int,int)
+MissingGetterMatchingBuilder: android.net.ipsec.ike.IkeSaProposal.Builder#addIntegrityAlgorithm(int):
+ android.net.ipsec.ike.IkeSaProposal does not declare a `getIntegrityAlgorithms()` method matching method android.net.ipsec.ike.IkeSaProposal.Builder.addIntegrityAlgorithm(int)
+MissingGetterMatchingBuilder: android.net.ipsec.ike.IkeSessionParams.Builder#addIkeOption(int):
+ android.net.ipsec.ike.IkeSessionParams does not declare a `getIkeOptions()` method matching method android.net.ipsec.ike.IkeSessionParams.Builder.addIkeOption(int)
+MissingGetterMatchingBuilder: android.net.ipsec.ike.IkeSessionParams.Builder#addPcscfServerRequest(int):
+ android.net.ipsec.ike.IkeSessionParams does not declare a `getPcscfServerRequests()` method matching method android.net.ipsec.ike.IkeSessionParams.Builder.addPcscfServerRequest(int)
+MissingGetterMatchingBuilder: android.net.ipsec.ike.IkeSessionParams.Builder#addPcscfServerRequest(java.net.InetAddress):
+ android.net.ipsec.ike.IkeSessionParams does not declare a `getPcscfServerRequests()` method matching method android.net.ipsec.ike.IkeSessionParams.Builder.addPcscfServerRequest(java.net.InetAddress)
+MissingGetterMatchingBuilder: android.net.ipsec.ike.IkeSessionParams.Builder#setAuthDigitalSignature(java.security.cert.X509Certificate, java.security.cert.X509Certificate, java.security.PrivateKey):
+ android.net.ipsec.ike.IkeSessionParams does not declare a `getAuthDigitalSignature()` method matching method android.net.ipsec.ike.IkeSessionParams.Builder.setAuthDigitalSignature(java.security.cert.X509Certificate,java.security.cert.X509Certificate,java.security.PrivateKey)
+MissingGetterMatchingBuilder: android.net.ipsec.ike.IkeSessionParams.Builder#setAuthDigitalSignature(java.security.cert.X509Certificate, java.security.cert.X509Certificate, java.util.List<java.security.cert.X509Certificate>, java.security.PrivateKey):
+ android.net.ipsec.ike.IkeSessionParams does not declare a `getAuthDigitalSignature()` method matching method android.net.ipsec.ike.IkeSessionParams.Builder.setAuthDigitalSignature(java.security.cert.X509Certificate,java.security.cert.X509Certificate,java.util.List<java.security.cert.X509Certificate>,java.security.PrivateKey)
+MissingGetterMatchingBuilder: android.net.ipsec.ike.IkeSessionParams.Builder#setAuthEap(java.security.cert.X509Certificate, android.net.eap.EapSessionConfig):
+ android.net.ipsec.ike.IkeSessionParams does not declare a `getAuthEap()` method matching method android.net.ipsec.ike.IkeSessionParams.Builder.setAuthEap(java.security.cert.X509Certificate,android.net.eap.EapSessionConfig)
+MissingGetterMatchingBuilder: android.net.ipsec.ike.IkeSessionParams.Builder#setAuthPsk(byte[]):
+ android.net.ipsec.ike.IkeSessionParams does not declare a `getAuthPsk()` method matching method android.net.ipsec.ike.IkeSessionParams.Builder.setAuthPsk(byte[])
+MissingGetterMatchingBuilder: android.net.ipsec.ike.IkeSessionParams.Builder#setLifetimeSeconds(int, int):
+ android.net.ipsec.ike.IkeSessionParams does not declare a `getLifetimeSeconds()` method matching method android.net.ipsec.ike.IkeSessionParams.Builder.setLifetimeSeconds(int,int)
+MissingGetterMatchingBuilder: android.net.ipsec.ike.TransportModeChildSessionParams.Builder#addInboundTrafficSelectors(android.net.ipsec.ike.IkeTrafficSelector):
+ android.net.ipsec.ike.TransportModeChildSessionParams does not declare a `getInboundTrafficSelectorss()` method matching method android.net.ipsec.ike.TransportModeChildSessionParams.Builder.addInboundTrafficSelectors(android.net.ipsec.ike.IkeTrafficSelector)
+MissingGetterMatchingBuilder: android.net.ipsec.ike.TransportModeChildSessionParams.Builder#addOutboundTrafficSelectors(android.net.ipsec.ike.IkeTrafficSelector):
+ android.net.ipsec.ike.TransportModeChildSessionParams does not declare a `getOutboundTrafficSelectorss()` method matching method android.net.ipsec.ike.TransportModeChildSessionParams.Builder.addOutboundTrafficSelectors(android.net.ipsec.ike.IkeTrafficSelector)
+MissingGetterMatchingBuilder: android.net.ipsec.ike.TransportModeChildSessionParams.Builder#addSaProposal(android.net.ipsec.ike.ChildSaProposal):
+ android.net.ipsec.ike.TransportModeChildSessionParams does not declare a `getSaProposals()` method matching method android.net.ipsec.ike.TransportModeChildSessionParams.Builder.addSaProposal(android.net.ipsec.ike.ChildSaProposal)
+MissingGetterMatchingBuilder: android.net.ipsec.ike.TransportModeChildSessionParams.Builder#setLifetimeSeconds(int, int):
+ android.net.ipsec.ike.TransportModeChildSessionParams does not declare a `getLifetimeSeconds()` method matching method android.net.ipsec.ike.TransportModeChildSessionParams.Builder.setLifetimeSeconds(int,int)
+MissingGetterMatchingBuilder: android.net.ipsec.ike.TunnelModeChildSessionParams.Builder#addInboundTrafficSelectors(android.net.ipsec.ike.IkeTrafficSelector):
+ android.net.ipsec.ike.TunnelModeChildSessionParams does not declare a `getInboundTrafficSelectorss()` method matching method android.net.ipsec.ike.TunnelModeChildSessionParams.Builder.addInboundTrafficSelectors(android.net.ipsec.ike.IkeTrafficSelector)
+MissingGetterMatchingBuilder: android.net.ipsec.ike.TunnelModeChildSessionParams.Builder#addInternalAddressRequest(int):
+ android.net.ipsec.ike.TunnelModeChildSessionParams does not declare a `getInternalAddressRequests()` method matching method android.net.ipsec.ike.TunnelModeChildSessionParams.Builder.addInternalAddressRequest(int)
+MissingGetterMatchingBuilder: android.net.ipsec.ike.TunnelModeChildSessionParams.Builder#addInternalAddressRequest(java.net.Inet4Address):
+ android.net.ipsec.ike.TunnelModeChildSessionParams does not declare a `getInternalAddressRequests()` method matching method android.net.ipsec.ike.TunnelModeChildSessionParams.Builder.addInternalAddressRequest(java.net.Inet4Address)
+MissingGetterMatchingBuilder: android.net.ipsec.ike.TunnelModeChildSessionParams.Builder#addInternalAddressRequest(java.net.Inet6Address, int):
+ android.net.ipsec.ike.TunnelModeChildSessionParams does not declare a `getInternalAddressRequests()` method matching method android.net.ipsec.ike.TunnelModeChildSessionParams.Builder.addInternalAddressRequest(java.net.Inet6Address,int)
+MissingGetterMatchingBuilder: android.net.ipsec.ike.TunnelModeChildSessionParams.Builder#addInternalDhcpServerRequest(int):
+ android.net.ipsec.ike.TunnelModeChildSessionParams does not declare a `getInternalDhcpServerRequests()` method matching method android.net.ipsec.ike.TunnelModeChildSessionParams.Builder.addInternalDhcpServerRequest(int)
+MissingGetterMatchingBuilder: android.net.ipsec.ike.TunnelModeChildSessionParams.Builder#addInternalDnsServerRequest(int):
+ android.net.ipsec.ike.TunnelModeChildSessionParams does not declare a `getInternalDnsServerRequests()` method matching method android.net.ipsec.ike.TunnelModeChildSessionParams.Builder.addInternalDnsServerRequest(int)
+MissingGetterMatchingBuilder: android.net.ipsec.ike.TunnelModeChildSessionParams.Builder#addOutboundTrafficSelectors(android.net.ipsec.ike.IkeTrafficSelector):
+ android.net.ipsec.ike.TunnelModeChildSessionParams does not declare a `getOutboundTrafficSelectorss()` method matching method android.net.ipsec.ike.TunnelModeChildSessionParams.Builder.addOutboundTrafficSelectors(android.net.ipsec.ike.IkeTrafficSelector)
+MissingGetterMatchingBuilder: android.net.ipsec.ike.TunnelModeChildSessionParams.Builder#addSaProposal(android.net.ipsec.ike.ChildSaProposal):
+ android.net.ipsec.ike.TunnelModeChildSessionParams does not declare a `getSaProposals()` method matching method android.net.ipsec.ike.TunnelModeChildSessionParams.Builder.addSaProposal(android.net.ipsec.ike.ChildSaProposal)
+MissingGetterMatchingBuilder: android.net.ipsec.ike.TunnelModeChildSessionParams.Builder#setLifetimeSeconds(int, int):
+ android.net.ipsec.ike.TunnelModeChildSessionParams does not declare a `getLifetimeSeconds()` method matching method android.net.ipsec.ike.TunnelModeChildSessionParams.Builder.setLifetimeSeconds(int,int)
+
+
+MissingNullability: android.net.ipsec.ike.IkeSessionParams#getRetransmissionTimeoutsMillis():
+ Missing nullability on method `getRetransmissionTimeoutsMillis` return
diff --git a/src/java/android/net/ipsec/ike/ChildSaProposal.java b/src/java/android/net/ipsec/ike/ChildSaProposal.java
index 4d3ea284..7318659a 100644
--- a/src/java/android/net/ipsec/ike/ChildSaProposal.java
+++ b/src/java/android/net/ipsec/ike/ChildSaProposal.java
@@ -107,6 +107,25 @@ public final class ChildSaProposal extends SaProposal {
&& isTransformSelectedFrom(mEsns, ((ChildSaProposal) reqProposal).mEsns);
}
+ /** @hide */
+ public boolean isNegotiatedFromExceptDhGroup(SaProposal saProposal) {
+ return getProtocolId() == saProposal.getProtocolId()
+ && isTransformSelectedFrom(
+ getEncryptionTransforms(), saProposal.getEncryptionTransforms())
+ && isTransformSelectedFrom(
+ getIntegrityTransforms(), saProposal.getIntegrityTransforms())
+ && isTransformSelectedFrom(mEsns, ((ChildSaProposal) saProposal).mEsns);
+ }
+
+ /** @hide */
+ public ChildSaProposal getCopyWithAdditionalDhTransform(int dhGroup) {
+ return new ChildSaProposal(
+ getEncryptionTransforms(),
+ getIntegrityTransforms(),
+ new DhGroupTransform[] {new DhGroupTransform(dhGroup)},
+ getEsnTransforms());
+ }
+
/**
* This class is used to incrementally construct a ChildSaProposal. ChildSaProposal instances
* are immutable once built.
diff --git a/src/java/android/net/ipsec/ike/ChildSessionParams.java b/src/java/android/net/ipsec/ike/ChildSessionParams.java
index e34d1d6d..0221491c 100644
--- a/src/java/android/net/ipsec/ike/ChildSessionParams.java
+++ b/src/java/android/net/ipsec/ike/ChildSessionParams.java
@@ -35,6 +35,17 @@ import java.util.concurrent.TimeUnit;
* <p>Note that references to negotiated configurations will be held, and the same parameters will
* be reused during rekey. This includes SA Proposals, lifetimes and traffic selectors.
*
+ * <p>IKE library will send out KE payload only if user has configured one or more DH groups. The KE
+ * payload in a request will use the first DH group from the first user provided SA proposal (or the
+ * peer selected SA proposal if it's a rekey request). The KE payload in a response will depend on
+ * the SA proposal negotiation result.
+ *
+ * <p>When requesting the first Child Session in IKE AUTH, IKE library will not propose any DH group
+ * even if user has configured it, as per RFC 7296. When rekeying this child session, IKE library
+ * will accept DH groups that are configured in its ChildSessionParams. If after rekeying user needs
+ * to have the same DH group as that of the IKE Session, then they need to explicitly set the same
+ * DH Group in ChildSessionParams.
+ *
* @see {@link TunnelModeChildSessionParams} and {@link TransportModeChildSessionParams}
* @hide
*/
@@ -238,8 +249,9 @@ public abstract class ChildSessionParams {
break;
case IkeTrafficSelector.TRAFFIC_SELECTOR_TYPE_IPV6_ADDR_RANGE:
startAddress = InetAddresses.parseNumericAddress("::");
- endAddress = InetAddresses.parseNumericAddress(
- "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff");
+ endAddress =
+ InetAddresses.parseNumericAddress(
+ "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff");
break;
default:
throw new IllegalArgumentException("Invalid Traffic Selector type: " + tsType);
diff --git a/src/java/android/net/ipsec/ike/SaProposal.java b/src/java/android/net/ipsec/ike/SaProposal.java
index fa6fbf17..d5eb8c81 100644
--- a/src/java/android/net/ipsec/ike/SaProposal.java
+++ b/src/java/android/net/ipsec/ike/SaProposal.java
@@ -228,6 +228,11 @@ public abstract class SaProposal {
* Check if the current SaProposal from the SA responder is consistent with the selected
* reqProposal from the SA initiator.
*
+ * <p>As per RFC 7296, The accepted cryptographic suite MUST contain exactly one transform of
+ * each type included in the proposal. But for interoperability reason, IKE library allows
+ * exceptions when the accepted suite or the request proposal has a NONE value transform.
+ * Currently only IntegrityTransform and DhGroupTransform have NONE value transform ID defined.
+ *
* @param reqProposal selected SaProposal from SA initiator
* @return if current SaProposal from SA responder is consistent with the selected reqProposal
* from SA initiator.
@@ -236,11 +241,16 @@ public abstract class SaProposal {
public boolean isNegotiatedFrom(SaProposal reqProposal) {
return this.mProtocolId == reqProposal.mProtocolId
&& isTransformSelectedFrom(mEncryptionAlgorithms, reqProposal.mEncryptionAlgorithms)
- && isTransformSelectedFrom(mIntegrityAlgorithms, reqProposal.mIntegrityAlgorithms)
- && isTransformSelectedFrom(mDhGroups, reqProposal.mDhGroups);
+ && isIntegrityTransformSelectedFrom(
+ mIntegrityAlgorithms, reqProposal.mIntegrityAlgorithms)
+ && isDhGroupTransformSelectedFrom(mDhGroups, reqProposal.mDhGroups);
}
- /** Package private */
+ /**
+ * Check if the response transform can be selected from the request transforms
+ *
+ * <p>Package private
+ */
static boolean isTransformSelectedFrom(Transform[] selected, Transform[] selectFrom) {
// If the selected proposal has multiple transforms with the same type, the responder MUST
// choose a single one.
@@ -253,6 +263,45 @@ public abstract class SaProposal {
return Arrays.asList(selectFrom).contains(selected[0]);
}
+ /**
+ * Check if the response integrity transform can be selected from the request integrity
+ * transforms.
+ *
+ * <p>For interoperability reason, it is allowed to do not include integrity transform in the
+ * response proposal when the request proposal has a NONE value integrity transform; and it is
+ * also allowed to have a NONE value integrity transform when the request proposal does not have
+ * integrity transforms.
+ */
+ private static boolean isIntegrityTransformSelectedFrom(
+ IntegrityTransform[] selected, IntegrityTransform[] selectFrom) {
+ if (selected.length == 0) {
+ selected = new IntegrityTransform[] {new IntegrityTransform(INTEGRITY_ALGORITHM_NONE)};
+ }
+ if (selectFrom.length == 0) {
+ selectFrom =
+ new IntegrityTransform[] {new IntegrityTransform(INTEGRITY_ALGORITHM_NONE)};
+ }
+ return isTransformSelectedFrom(selected, selectFrom);
+ }
+
+ /**
+ * Check if the response DH group can be selected from the request DH groups
+ *
+ * <p>For interoperability reason, it is allowed to do not include DH group in the response
+ * proposal when the request proposal has a NONE value DH group; and it is also allowed to have
+ * a NONE value DH group when the request proposal does not have DH groups.
+ */
+ private static boolean isDhGroupTransformSelectedFrom(
+ DhGroupTransform[] selected, DhGroupTransform[] selectFrom) {
+ if (selected.length == 0) {
+ selected = new DhGroupTransform[] {new DhGroupTransform(DH_GROUP_NONE)};
+ }
+ if (selectFrom.length == 0) {
+ selectFrom = new DhGroupTransform[] {new DhGroupTransform(DH_GROUP_NONE)};
+ }
+ return isTransformSelectedFrom(selected, selectFrom);
+ }
+
/** @hide */
@IkePayload.ProtocolId
public int getProtocolId() {
diff --git a/src/java/android/net/ipsec/ike/TunnelModeChildSessionParams.java b/src/java/android/net/ipsec/ike/TunnelModeChildSessionParams.java
index 10a23dbe..c8d31f6b 100644
--- a/src/java/android/net/ipsec/ike/TunnelModeChildSessionParams.java
+++ b/src/java/android/net/ipsec/ike/TunnelModeChildSessionParams.java
@@ -95,29 +95,10 @@ public final class TunnelModeChildSessionParams extends ChildSessionParams {
}
/** Represents an IPv4 DHCP server request */
- public interface ConfigRequestIpv4DhcpServer extends TunnelModeChildConfigRequest {
- /**
- * Retrieves the requested IPv4 DHCP server address
- *
- * @return The requested DHCP server address, or null if no specific DHCP server was
- * requested
- * @hide
- */
- @Nullable
- Inet4Address getAddress();
- }
+ public interface ConfigRequestIpv4DhcpServer extends TunnelModeChildConfigRequest {}
/** Represents an IPv4 DNS Server request */
- public interface ConfigRequestIpv4DnsServer extends TunnelModeChildConfigRequest {
- /**
- * Retrieves the requested IPv4 DNS server address
- *
- * @return The requested DNS server address, or null if no specific DNS server was requested
- * @hide
- */
- @Nullable
- Inet4Address getAddress();
- }
+ public interface ConfigRequestIpv4DnsServer extends TunnelModeChildConfigRequest {}
/** Represents an IPv4 Netmask request */
public interface ConfigRequestIpv4Netmask extends TunnelModeChildConfigRequest {}
@@ -141,16 +122,7 @@ public final class TunnelModeChildSessionParams extends ChildSessionParams {
}
/** Represents an IPv6 DNS Server request */
- public interface ConfigRequestIpv6DnsServer extends TunnelModeChildConfigRequest {
- /**
- * Retrieves the requested IPv6 DNS server address
- *
- * @return The requested DNS server address, or null if no specific DNS server was requested
- * @hide
- */
- @Nullable
- Inet6Address getAddress();
- }
+ public interface ConfigRequestIpv6DnsServer extends TunnelModeChildConfigRequest {}
/** This class can be used to incrementally construct a {@link TunnelModeChildSessionParams}. */
public static final class Builder extends ChildSessionParams.Builder {
diff --git a/src/java/com/android/internal/net/ipsec/ike/AbstractSessionStateMachine.java b/src/java/com/android/internal/net/ipsec/ike/AbstractSessionStateMachine.java
index 458f2610..1ed89b97 100644
--- a/src/java/com/android/internal/net/ipsec/ike/AbstractSessionStateMachine.java
+++ b/src/java/com/android/internal/net/ipsec/ike/AbstractSessionStateMachine.java
@@ -78,6 +78,9 @@ abstract class AbstractSessionStateMachine extends StateMachine {
// Use a value greater than the retransmit-failure timeout.
static final long REKEY_DELETE_TIMEOUT_MS = TimeUnit.SECONDS.toMillis(180L);
+ // Default delay time for retrying a request
+ static final long RETRY_INTERVAL_MS = TimeUnit.SECONDS.toMillis(15L);
+
protected final Executor mUserCbExecutor;
private final String mLogTag;
diff --git a/src/java/com/android/internal/net/ipsec/ike/ChildSessionStateMachine.java b/src/java/com/android/internal/net/ipsec/ike/ChildSessionStateMachine.java
index 8697ab50..fd500b8f 100644
--- a/src/java/com/android/internal/net/ipsec/ike/ChildSessionStateMachine.java
+++ b/src/java/com/android/internal/net/ipsec/ike/ChildSessionStateMachine.java
@@ -79,7 +79,6 @@ import com.android.internal.net.ipsec.ike.crypto.IkeMacPrf;
import com.android.internal.net.ipsec.ike.exceptions.InvalidKeException;
import com.android.internal.net.ipsec.ike.exceptions.InvalidSyntaxException;
import com.android.internal.net.ipsec.ike.exceptions.NoValidProposalChosenException;
-import com.android.internal.net.ipsec.ike.exceptions.TemporaryFailureException;
import com.android.internal.net.ipsec.ike.exceptions.TsUnacceptableException;
import com.android.internal.net.ipsec.ike.message.IkeConfigPayload;
import com.android.internal.net.ipsec.ike.message.IkeConfigPayload.ConfigAttribute;
@@ -104,8 +103,10 @@ import java.net.InetAddress;
import java.security.GeneralSecurityException;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
+import java.util.Set;
import java.util.concurrent.Executor;
/**
@@ -221,6 +222,7 @@ public class ChildSessionStateMachine extends AbstractSessionStateMachine {
@VisibleForTesting final State mRekeyChildRemoteCreate = new RekeyChildRemoteCreate();
@VisibleForTesting final State mRekeyChildLocalDelete = new RekeyChildLocalDelete();
@VisibleForTesting final State mRekeyChildRemoteDelete = new RekeyChildRemoteDelete();
+ @VisibleForTesting boolean mIsFirstChild;
/**
* Builds a new uninitialized ChildSessionStateMachine
@@ -361,6 +363,7 @@ public class ChildSessionStateMachine extends AbstractSessionStateMachine {
this.mUdpEncapSocket = udpEncapSocket;
this.mIkePrf = ikePrf;
this.mSkD = skD;
+ mIsFirstChild = true;
int spi = registerProvisionalChildAndGetSpi(respPayloads);
sendMessage(
@@ -391,6 +394,7 @@ public class ChildSessionStateMachine extends AbstractSessionStateMachine {
this.mUdpEncapSocket = udpEncapSocket;
this.mIkePrf = ikePrf;
this.mSkD = skD;
+ mIsFirstChild = false;
sendMessage(CMD_LOCAL_REQUEST_CREATE_CHILD);
}
@@ -744,12 +748,9 @@ public class ChildSessionStateMachine extends AbstractSessionStateMachine {
mSkD,
mChildSessionParams.isTransportMode(),
true /*isLocalInit*/,
- rekeyLocalRequest,
buildSaLifetimeAlarmSched(
createChildResult.respSpi.getSpi()));
- mChildSmCallback.scheduleLocalRequest(rekeyLocalRequest, getRekeyTimeout());
-
ChildSessionConfiguration sessionConfig =
buildChildSessionConfigFromResp(createChildResult, respPayloads);
executeUserCallback(
@@ -772,9 +773,12 @@ public class ChildSessionStateMachine extends AbstractSessionStateMachine {
// TODO: Initiate deletion
mChildSmCallback.onChildSaDeleted(createChildResult.respSpi.getSpi());
+ handleChildFatalError(e);
+ } finally {
+ // In the successful case the transform in ChildSaRecord has taken ownership
+ // of the SPI (in IpSecService), and will keep it alive.
createChildResult.initSpi.close();
createChildResult.respSpi.close();
- handleChildFatalError(e);
}
break;
case CREATE_STATUS_CHILD_ERROR_INVALID_MSG:
@@ -881,19 +885,21 @@ public class ChildSessionStateMachine extends AbstractSessionStateMachine {
/** Initial state of ChildSessionStateMachine. */
class Initial extends CreateChildLocalCreateBase {
+ List<IkePayload> mRequestPayloads;
+
@Override
public boolean processStateMessage(Message message) {
switch (message.what) {
case CMD_HANDLE_FIRST_CHILD_EXCHANGE:
FirstChildNegotiationData childNegotiationData =
(FirstChildNegotiationData) message.obj;
- List<IkePayload> reqPayloads = childNegotiationData.requestPayloads;
+ mRequestPayloads = childNegotiationData.requestPayloads;
List<IkePayload> respPayloads = childNegotiationData.responsePayloads;
// Negotiate Child SA. The exchangeType has been validated in
// IkeSessionStateMachine. Won't validate it again here.
validateAndBuildChild(
- reqPayloads,
+ mRequestPayloads,
respPayloads,
EXCHANGE_TYPE_IKE_AUTH,
EXCHANGE_TYPE_IKE_AUTH,
@@ -918,6 +924,11 @@ public class ChildSessionStateMachine extends AbstractSessionStateMachine {
return NOT_HANDLED;
}
}
+
+ @Override
+ public void exitState() {
+ CreateChildSaHelper.releaseSpiResources(mRequestPayloads);
+ }
}
/**
@@ -973,6 +984,11 @@ public class ChildSessionStateMachine extends AbstractSessionStateMachine {
return NOT_HANDLED;
}
}
+
+ @Override
+ public void exitState() {
+ CreateChildSaHelper.releaseSpiResources(mRequestPayloads);
+ }
}
/**
@@ -1264,13 +1280,18 @@ public class ChildSessionStateMachine extends AbstractSessionStateMachine {
@Override
public void enterState() {
try {
+ ChildSaProposal saProposal = mSaProposal;
+ if (mIsFirstChild) {
+ saProposal = addDhGroupsFromChildSessionParamsIfAbsent();
+ }
+
// Build request with negotiated proposal and TS.
mRequestPayloads =
CreateChildSaHelper.getRekeyChildCreateReqPayloads(
mRandomFactory,
mIpSecSpiGenerator,
mLocalAddress,
- mSaProposal,
+ saProposal,
mLocalTs,
mRemoteTs,
mCurrentChildSaRecord.getLocalSpi(),
@@ -1282,8 +1303,7 @@ public class ChildSessionStateMachine extends AbstractSessionStateMachine {
ChildSessionStateMachine.this);
} catch (SpiUnavailableException | ResourceUnavailableException e) {
loge("Fail to assign Child SPI. Schedule a retry for rekey Child");
- mChildSmCallback.scheduleRetryLocalRequest(
- (ChildLocalRequest) mCurrentChildSaRecord.getFutureRekeyEvent());
+ mCurrentChildSaRecord.rescheduleRekey(RETRY_INTERVAL_MS);
transitionTo(mIdle);
}
}
@@ -1307,8 +1327,8 @@ public class ChildSessionStateMachine extends AbstractSessionStateMachine {
switch (createChildResult.status) {
case CREATE_STATUS_OK:
try {
- // Do not need to update the negotiated proposal and TS because they
- // are not changed.
+ // Do not need to update TS because they are not changed.
+ mSaProposal = createChildResult.negotiatedProposal;
ChildLocalRequest rekeyLocalRequest = makeRekeyLocalRequest();
@@ -1328,13 +1348,9 @@ public class ChildSessionStateMachine extends AbstractSessionStateMachine {
mSkD,
mChildSessionParams.isTransportMode(),
true /*isLocalInit*/,
- rekeyLocalRequest,
buildSaLifetimeAlarmSched(
createChildResult.respSpi.getSpi()));
- mChildSmCallback.scheduleLocalRequest(
- rekeyLocalRequest, getRekeyTimeout());
-
executeUserCallback(
() -> {
mUserCallback.onIpSecTransformCreated(
@@ -1354,6 +1370,9 @@ public class ChildSessionStateMachine extends AbstractSessionStateMachine {
| IOException e) {
// #makeChildSaRecord failed
handleProcessRespOrSaCreationFailAndQuit(resp.registeredSpi, e);
+ } finally {
+ // In the successful case the transform in ChildSaRecord has taken
+ // ownership of the SPI (in IpSecService), and will keep it alive.
createChildResult.initSpi.close();
createChildResult.respSpi.close();
}
@@ -1363,18 +1382,10 @@ public class ChildSessionStateMachine extends AbstractSessionStateMachine {
resp.registeredSpi, createChildResult.exception);
break;
case CREATE_STATUS_CHILD_ERROR_RCV_NOTIFY:
- if (createChildResult.exception instanceof TemporaryFailureException) {
- loge(
- "Received TEMPORARY_FAILURE for rekey Child. Retry has"
- + "already been scheduled by IKE Session.");
- } else {
- loge(
- "Received error notification for rekey Child. Schedule a"
- + " retry");
- mChildSmCallback.scheduleRetryLocalRequest(
- (ChildLocalRequest)
- mCurrentChildSaRecord.getFutureRekeyEvent());
- }
+ loge(
+ "Received error notification for rekey Child. Schedule a"
+ + " retry");
+ mCurrentChildSaRecord.rescheduleRekey(RETRY_INTERVAL_MS);
transitionTo(mIdle);
break;
@@ -1403,6 +1414,34 @@ public class ChildSessionStateMachine extends AbstractSessionStateMachine {
}
handleChildFatalError(exception);
}
+
+ @Override
+ public void exitState() {
+ CreateChildSaHelper.releaseSpiResources(mRequestPayloads);
+ }
+ }
+
+ private ChildSaProposal addDhGroupsFromChildSessionParamsIfAbsent() {
+ // DH groups are excluded for the first child. Add dh groups from child session params in
+ // this case.
+ if (mSaProposal.getDhGroups().size() != 0) {
+ return mSaProposal;
+ }
+
+ Set<DhGroupTransform> dhGroupSet = new LinkedHashSet<>();
+ for (SaProposal saProposal : mChildSessionParams.getSaProposals()) {
+ if (!mSaProposal.isNegotiatedFromExceptDhGroup(saProposal)) continue;
+ dhGroupSet.addAll(Arrays.asList(saProposal.getDhGroupTransforms()));
+ }
+
+ DhGroupTransform[] dhGroups = new DhGroupTransform[dhGroupSet.size()];
+ dhGroupSet.toArray(dhGroups);
+
+ return new ChildSaProposal(
+ mSaProposal.getEncryptionTransforms(),
+ mSaProposal.getIntegrityTransforms(),
+ dhGroups,
+ mSaProposal.getEsnTransforms());
}
/**
@@ -1448,7 +1487,21 @@ public class ChildSessionStateMachine extends AbstractSessionStateMachine {
IkeSaPayload reqSaPayload =
IkePayload.getPayloadForTypeInProvidedList(
PAYLOAD_TYPE_SA, IkeSaPayload.class, reqPayloads);
- byte respProposalNumber = reqSaPayload.getNegotiatedProposalNumber(mSaProposal);
+
+ IkeKePayload reqKePayload =
+ IkePayload.getPayloadForTypeInProvidedList(
+ PAYLOAD_TYPE_KE, IkeKePayload.class, reqPayloads);
+
+ ChildSaProposal saProposal = mSaProposal;
+
+ // Try accepting a DH group requested during remote rekey for both first and
+ // additional Child Sessions even if it is different from the previously negotiated
+ // proposal.
+ if (reqKePayload != null && isKePayloadAcceptable(reqKePayload)) {
+ saProposal = mSaProposal.getCopyWithAdditionalDhTransform(reqKePayload.dhGroup);
+ }
+
+ byte respProposalNumber = reqSaPayload.getNegotiatedProposalNumber(saProposal);
respPayloads =
CreateChildSaHelper.getRekeyChildCreateRespPayloads(
@@ -1456,7 +1509,7 @@ public class ChildSessionStateMachine extends AbstractSessionStateMachine {
mIpSecSpiGenerator,
mLocalAddress,
respProposalNumber,
- mSaProposal,
+ saProposal,
mLocalTs,
mRemoteTs,
mCurrentChildSaRecord.getLocalSpi(),
@@ -1483,8 +1536,8 @@ public class ChildSessionStateMachine extends AbstractSessionStateMachine {
switch (createChildResult.status) {
case CREATE_STATUS_OK:
try {
- // Do not need to update the negotiated proposal and TS
- // because they are not changed.
+ // Do not need to update TS because they are not changed.
+ mSaProposal = createChildResult.negotiatedProposal;
ChildLocalRequest rekeyLocalRequest = makeRekeyLocalRequest();
@@ -1504,12 +1557,9 @@ public class ChildSessionStateMachine extends AbstractSessionStateMachine {
mSkD,
mChildSessionParams.isTransportMode(),
false /*isLocalInit*/,
- rekeyLocalRequest,
buildSaLifetimeAlarmSched(
createChildResult.initSpi.getSpi()));
- mChildSmCallback.scheduleLocalRequest(rekeyLocalRequest, getRekeyTimeout());
-
mChildSmCallback.onChildSaCreated(
mRemoteInitNewChildSaRecord.getRemoteSpi(),
ChildSessionStateMachine.this);
@@ -1537,12 +1587,14 @@ public class ChildSessionStateMachine extends AbstractSessionStateMachine {
| SpiUnavailableException
| IOException e) {
// #makeChildSaRecord failed.
- createChildResult.initSpi.close();
- createChildResult.respSpi.close();
-
handleCreationFailureAndBackToIdle(
new NoValidProposalChosenException(
"Error in Child SA creation", e));
+ } finally {
+ // In the successful case the transform in ChildSaRecord has taken ownership
+ // of the SPI (in IpSecService), and will keep it alive.
+ createChildResult.initSpi.close();
+ createChildResult.respSpi.close();
}
break;
case CREATE_STATUS_CHILD_ERROR_INVALID_MSG:
@@ -1568,6 +1620,20 @@ public class ChildSessionStateMachine extends AbstractSessionStateMachine {
}
}
+ private boolean isKePayloadAcceptable(IkeKePayload reqKePayload) {
+ ChildSaProposal proposal =
+ mSaProposal.getCopyWithAdditionalDhTransform(reqKePayload.dhGroup);
+
+ // Verify if this proposal is accepted by user
+ for (SaProposal saProposal : mChildSessionParams.getSaProposals()) {
+ if (proposal.isNegotiatedFrom(saProposal)) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
private void handleCreationFailureAndBackToIdle(IkeProtocolException e) {
loge("Received invalid Rekey Child request. Reject with error notification", e);
@@ -2005,6 +2071,19 @@ public class ChildSessionStateMachine extends AbstractSessionStateMachine {
return hasExpectedRekeyNotify;
}
+ public static void releaseSpiResources(List<IkePayload> reqPayloads) {
+ if (reqPayloads == null) {
+ return;
+ }
+
+ IkeSaPayload saPayload =
+ IkePayload.getPayloadForTypeInProvidedList(
+ IkePayload.PAYLOAD_TYPE_SA, IkeSaPayload.class, reqPayloads);
+ if (saPayload != null) {
+ saPayload.releaseChildSpiResourcesIfExists();
+ }
+ }
+
/** Validate the received payload list and negotiate Child SA. */
private static CreateChildResult validateAndNegotiateChild(
List<IkePayload> reqPayloads,
diff --git a/src/java/com/android/internal/net/ipsec/ike/IkeLocalRequestScheduler.java b/src/java/com/android/internal/net/ipsec/ike/IkeLocalRequestScheduler.java
index e86df232..f8008fbe 100644
--- a/src/java/com/android/internal/net/ipsec/ike/IkeLocalRequestScheduler.java
+++ b/src/java/com/android/internal/net/ipsec/ike/IkeLocalRequestScheduler.java
@@ -64,15 +64,18 @@ public final class IkeLocalRequestScheduler {
* Notifies the scheduler that the caller is ready for a new procedure
*
* <p>Synchronously triggers the call to onNewProcedureReady.
+ *
+ * @return whether or not a new procedure was scheduled.
*/
- public void readyForNextProcedure() {
+ public boolean readyForNextProcedure() {
while (!mRequestQueue.isEmpty()) {
LocalRequest request = mRequestQueue.poll();
if (!request.isCancelled()) {
mConsumer.onNewProcedureReady(request);
- return;
+ return true;
}
}
+ return false;
}
/**
diff --git a/src/java/com/android/internal/net/ipsec/ike/IkeSessionStateMachine.java b/src/java/com/android/internal/net/ipsec/ike/IkeSessionStateMachine.java
index 87eab421..1a3bf18b 100644
--- a/src/java/com/android/internal/net/ipsec/ike/IkeSessionStateMachine.java
+++ b/src/java/com/android/internal/net/ipsec/ike/IkeSessionStateMachine.java
@@ -22,6 +22,7 @@ import static android.net.ipsec.ike.exceptions.IkeProtocolException.ERROR_TYPE_I
import static android.net.ipsec.ike.exceptions.IkeProtocolException.ERROR_TYPE_NO_ADDITIONAL_SAS;
import static android.net.ipsec.ike.exceptions.IkeProtocolException.ERROR_TYPE_TEMPORARY_FAILURE;
import static android.net.ipsec.ike.exceptions.IkeProtocolException.ErrorType;
+import static android.os.PowerManager.PARTIAL_WAKE_LOCK;
import static com.android.internal.net.ipsec.ike.message.IkeConfigPayload.CONFIG_TYPE_REPLY;
import static com.android.internal.net.ipsec.ike.message.IkeHeader.EXCHANGE_TYPE_INFORMATIONAL;
@@ -34,9 +35,14 @@ import static com.android.internal.net.ipsec.ike.message.IkeNotifyPayload.NOTIFY
import static com.android.internal.net.ipsec.ike.message.IkeNotifyPayload.NOTIFY_TYPE_NAT_DETECTION_DESTINATION_IP;
import static com.android.internal.net.ipsec.ike.message.IkeNotifyPayload.NOTIFY_TYPE_NAT_DETECTION_SOURCE_IP;
import static com.android.internal.net.ipsec.ike.message.IkeNotifyPayload.NOTIFY_TYPE_REKEY_SA;
+import static com.android.internal.net.ipsec.ike.message.IkePayload.PAYLOAD_TYPE_AUTH;
import static com.android.internal.net.ipsec.ike.message.IkePayload.PAYLOAD_TYPE_CP;
import static com.android.internal.net.ipsec.ike.message.IkePayload.PAYLOAD_TYPE_DELETE;
+import static com.android.internal.net.ipsec.ike.message.IkePayload.PAYLOAD_TYPE_EAP;
import static com.android.internal.net.ipsec.ike.message.IkePayload.PAYLOAD_TYPE_NOTIFY;
+import static com.android.internal.net.ipsec.ike.message.IkePayload.PAYLOAD_TYPE_SA;
+import static com.android.internal.net.ipsec.ike.message.IkePayload.PAYLOAD_TYPE_TS_INITIATOR;
+import static com.android.internal.net.ipsec.ike.message.IkePayload.PAYLOAD_TYPE_TS_RESPONDER;
import static com.android.internal.net.ipsec.ike.message.IkePayload.PAYLOAD_TYPE_VENDOR;
import static com.android.internal.net.ipsec.ike.utils.IkeAlarmReceiver.ACTION_DELETE_CHILD;
import static com.android.internal.net.ipsec.ike.utils.IkeAlarmReceiver.ACTION_DELETE_IKE;
@@ -74,6 +80,7 @@ import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
+import android.os.PowerManager;
import android.os.SystemClock;
import android.system.ErrnoException;
import android.system.Os;
@@ -125,7 +132,6 @@ import com.android.internal.net.ipsec.ike.message.IkeNotifyPayload;
import com.android.internal.net.ipsec.ike.message.IkePayload;
import com.android.internal.net.ipsec.ike.message.IkeSaPayload;
import com.android.internal.net.ipsec.ike.message.IkeSaPayload.IkeProposal;
-import com.android.internal.net.ipsec.ike.message.IkeTsPayload;
import com.android.internal.net.ipsec.ike.message.IkeVendorPayload;
import com.android.internal.net.ipsec.ike.utils.IkeAlarmReceiver;
import com.android.internal.net.ipsec.ike.utils.IkeSecurityParameterIndex;
@@ -214,9 +220,6 @@ public class IkeSessionStateMachine extends AbstractSessionStateMachine {
// Default fragment size in bytes.
@VisibleForTesting static final int DEFAULT_FRAGMENT_SIZE = 1280;
- // Default delay time for retrying a request
- @VisibleForTesting static final long RETRY_INTERVAL_MS = TimeUnit.SECONDS.toMillis(15L);
-
// Close IKE Session when all responses during this time were TEMPORARY_FAILURE(s). This
// indicates that something has gone wrong, and we are out of sync.
@VisibleForTesting
@@ -368,6 +371,9 @@ public class IkeSessionStateMachine extends AbstractSessionStateMachine {
*/
private final IpSecSpiGenerator mIpSecSpiGenerator;
+ /** Ensures that the system does not go to sleep in the middle of an exchange. */
+ private final PowerManager.WakeLock mBusyWakeLock;
+
@VisibleForTesting
@GuardedBy("mChildCbToSessions")
final HashMap<ChildSessionCallback, ChildSessionStateMachine> mChildCbToSessions =
@@ -495,6 +501,10 @@ public class IkeSessionStateMachine extends AbstractSessionStateMachine {
// callback instance in the future
}
+ PowerManager pm = context.getSystemService(PowerManager.class);
+ mBusyWakeLock = pm.newWakeLock(PARTIAL_WAKE_LOCK, TAG + "mBusyWakeLock");
+ mBusyWakeLock.setReferenceCounted(false);
+
mIkeSessionId = sIkeSessionIdGenerator.getAndIncrement();
sIkeAlarmReceiver.registerIkeSession(mIkeSessionId, getHandler());
@@ -551,6 +561,7 @@ public class IkeSessionStateMachine extends AbstractSessionStateMachine {
sendMessageAtFrontOfQueue(CMD_EXECUTE_LOCAL_REQ, localReq);
});
+ mBusyWakeLock.acquire();
start();
}
@@ -731,15 +742,13 @@ public class IkeSessionStateMachine extends AbstractSessionStateMachine {
}
}
- /** Schedule retry when a request got rejected by TEMPORARY_FAILURE. */
- public void handleTempFailure(LocalRequest localRequest) {
- logd(
- "TempFailureHandler: Receive TEMPORARY FAILURE. Reschedule request: "
- + localRequest.procedureType);
-
- // TODO: Support customized delay time when this is a rekey request and SA is going to
- // expire soon.
- scheduleRetry(localRequest);
+ /**
+ * Schedule temporary failure timeout.
+ *
+ * <p>Caller of this method is responsible for scheduling retry of the rejected request.
+ */
+ public void handleTempFailure() {
+ logd("TempFailureHandler: Receive TEMPORARY FAILURE");
if (!mTempFailureReceived) {
sendEmptyMessageDelayed(TEMP_FAILURE_RETRY_TIMEOUT, TEMP_FAILURE_RETRY_TIMEOUT_MS);
@@ -773,8 +782,6 @@ public class IkeSessionStateMachine extends AbstractSessionStateMachine {
// IkeSaRecord is created. Calling this method at the end of exchange will double-register
// the SPI but it is safe because the key and value are not changed.
mIkeSocket.registerIke(record.getLocalSpi(), this);
-
- scheduleRekeySession(record.getFutureRekeyEvent());
}
@VisibleForTesting
@@ -944,6 +951,10 @@ public class IkeSessionStateMachine extends AbstractSessionStateMachine {
}
}
+ // Release IPsec SPIs if IKE Session is terminated before receiving the IKE AUTH response
+ // that contains the first child SA proposal
+ CreateChildSaHelper.releaseSpiResources(mFirstChildReqList);
+
if (mIkeNattKeepalive != null) {
mIkeNattKeepalive.stop();
}
@@ -963,6 +974,8 @@ public class IkeSessionStateMachine extends AbstractSessionStateMachine {
}
// TODO: Remove the stored ikeSessionCallback
}
+
+ mBusyWakeLock.release();
}
private void closeAllSaRecords(boolean expectSaClosed) {
@@ -1065,7 +1078,9 @@ public class IkeSessionStateMachine extends AbstractSessionStateMachine {
@Override
public void enterState() {
- mScheduler.readyForNextProcedure();
+ if (!mScheduler.readyForNextProcedure()) {
+ mBusyWakeLock.release();
+ }
if (mDpdIntent == null) {
long remoteIkeSpi = mCurrentIkeSaRecord.getRemoteSpi();
@@ -1097,6 +1112,8 @@ public class IkeSessionStateMachine extends AbstractSessionStateMachine {
// #exitState is guaranteed to be invoked when quit() or quitNow() is called
mAlarmManager.cancel(mDpdIntent);
logd("DPD Alarm canceled");
+
+ mBusyWakeLock.acquire();
}
@Override
@@ -1200,7 +1217,8 @@ public class IkeSessionStateMachine extends AbstractSessionStateMachine {
return obtainMessage(CMD_ALARM_FIRED, mIkeSessionId, localRequestType, spiBundle);
}
- private SaLifetimeAlarmScheduler buildSaLifetimeAlarmScheduler(long remoteSpi) {
+ @VisibleForTesting
+ SaLifetimeAlarmScheduler buildSaLifetimeAlarmScheduler(long remoteSpi) {
PendingIntent deleteSaIntent =
buildIkeAlarmIntent(
mContext,
@@ -1470,14 +1488,12 @@ public class IkeSessionStateMachine extends AbstractSessionStateMachine {
// Software keepalive alarm is fired
mIkeNattKeepalive.onAlarmFired();
return;
- case CMD_LOCAL_REQUEST_DELETE_CHILD:
- // Child SA (identified by remoteChildSpi) has hit its hard lifetime
+ case CMD_LOCAL_REQUEST_DELETE_CHILD: // Hits hard lifetime; fall through
+ case CMD_LOCAL_REQUEST_REKEY_CHILD: // Hits soft lifetime
enqueueChildLocalRequest(message);
return;
- case CMD_LOCAL_REQUEST_DELETE_IKE:
- // IKE SA hits its hard lifetime
- enqueueIkeLocalRequest(message);
- return;
+ case CMD_LOCAL_REQUEST_DELETE_IKE: // Hits hard lifetime; fall through
+ case CMD_LOCAL_REQUEST_REKEY_IKE: // Hits soft lifetime; fall through
case CMD_LOCAL_REQUEST_DPD:
// IKE Session has not received any protectd IKE packet for the whole DPD delay
enqueueIkeLocalRequest(message);
@@ -2215,7 +2231,9 @@ public class IkeSessionStateMachine extends AbstractSessionStateMachine {
@Override
protected void handleTempFailure() {
- mTempFailHandler.handleTempFailure(mLocalRequestOngoing);
+ // The ChildSessionStateMachine will be responsible for rescheduling the rejected
+ // request.
+ mTempFailHandler.handleTempFailure();
}
private void transitionToIdleIfAllProceduresDone() {
@@ -2694,7 +2712,6 @@ public class IkeSessionStateMachine extends AbstractSessionStateMachine {
mIkePrf,
mIkeIntegrity == null ? 0 : mIkeIntegrity.getKeyLength(),
mIkeCipher.getKeyLength(),
- new IkeLocalRequest(CMD_LOCAL_REQUEST_REKEY_IKE),
buildSaLifetimeAlarmScheduler(mRemoteIkeSpiResource.getSpi()));
addIkeSaRecord(mCurrentIkeSaRecord);
@@ -2882,12 +2899,8 @@ public class IkeSessionStateMachine extends AbstractSessionStateMachine {
if (respSaPayload == null
|| respKePayload == null
- || natSourcePayloads.isEmpty()
- || natDestPayload == null
|| !hasNoncePayload) {
- throw new InvalidSyntaxException(
- "SA, KE, Nonce, Notify-NAT-Detection-Source, or"
- + " Notify-NAT-Detection-Destination payload missing.");
+ throw new InvalidSyntaxException("SA, KE, or Nonce payload missing.");
}
IkeSaPayload reqSaPayload =
@@ -2915,6 +2928,20 @@ public class IkeSessionStateMachine extends AbstractSessionStateMachine {
throw new InvalidSyntaxException("Received KE payload with mismatched DH group.");
}
+ if (mRemoteAddress instanceof Inet4Address) {
+ handleNatDetection(respMsg, natSourcePayloads, natDestPayload);
+ }
+ }
+
+ private void handleNatDetection(
+ IkeMessage respMsg,
+ List<IkeNotifyPayload> natSourcePayloads,
+ IkeNotifyPayload natDestPayload)
+ throws InvalidSyntaxException, IOException {
+ if (natSourcePayloads.isEmpty() || natDestPayload == null) {
+ throw new InvalidSyntaxException("NAT detection notifications missing.");
+ }
+
// NAT detection
long initIkeSpi = respMsg.ikeHeader.ikeInitiatorSpi;
long respIkeSpi = respMsg.ikeHeader.ikeResponderSpi;
@@ -2952,7 +2979,8 @@ public class IkeSessionStateMachine extends AbstractSessionStateMachine {
IkeUdpEncapSocket.getIkeUdpEncapSocket(
mIkeSessionParams.getNetwork(),
mIpSecManager,
- IkeSessionStateMachine.this);
+ IkeSessionStateMachine.this,
+ getHandler().getLooper());
switchToIkeSocket(initIkeSpi, newSocket);
} catch (ErrnoException | IOException | ResourceUnavailableException e) {
handleIkeFatalError(e);
@@ -3070,50 +3098,26 @@ public class IkeSessionStateMachine extends AbstractSessionStateMachine {
protected List<IkePayload> extractChildPayloadsFromMessage(IkeMessage ikeMessage)
throws InvalidSyntaxException {
- IkeSaPayload saPayload =
- ikeMessage.getPayloadForType(IkePayload.PAYLOAD_TYPE_SA, IkeSaPayload.class);
- IkeTsPayload tsInitPayload =
- ikeMessage.getPayloadForType(
- IkePayload.PAYLOAD_TYPE_TS_INITIATOR, IkeTsPayload.class);
- IkeTsPayload tsRespPayload =
- ikeMessage.getPayloadForType(
- IkePayload.PAYLOAD_TYPE_TS_RESPONDER, IkeTsPayload.class);
-
- List<IkeNotifyPayload> notifyPayloads =
- ikeMessage.getPayloadListForType(
- IkePayload.PAYLOAD_TYPE_NOTIFY, IkeNotifyPayload.class);
-
- IkeConfigPayload configPayload =
- ikeMessage.getPayloadForType(
- IkePayload.PAYLOAD_TYPE_CP, IkeConfigPayload.class);
-
- boolean hasErrorNotify = false;
List<IkePayload> list = new LinkedList<>();
- for (IkeNotifyPayload payload : notifyPayloads) {
- if (payload.isNewChildSaNotify()) {
- list.add(payload);
- if (payload.isErrorNotify()) {
- hasErrorNotify = true;
- }
+ for (IkePayload payload : ikeMessage.ikePayloadList) {
+ switch (payload.payloadType) {
+ case PAYLOAD_TYPE_SA: // fall through
+ case PAYLOAD_TYPE_TS_INITIATOR: // fall through
+ case PAYLOAD_TYPE_TS_RESPONDER: // fall through
+ case PAYLOAD_TYPE_CP:
+ list.add(payload);
+ break;
+ case PAYLOAD_TYPE_NOTIFY:
+ if (((IkeNotifyPayload) payload).isNewChildSaNotify()) {
+ list.add(payload);
+ }
+ break;
+ default:
+ // Ignore payloads unrelated with Child negotiation
}
}
- // If there is no error notification, SA, TS-initiator and TS-responder MUST all be
- // included in this message.
- if (!hasErrorNotify
- && (saPayload == null || tsInitPayload == null || tsRespPayload == null)) {
- throw new InvalidSyntaxException(
- "SA, TS-Initiator or TS-Responder payload is missing.");
- }
-
- list.add(saPayload);
- list.add(tsInitPayload);
- list.add(tsRespPayload);
-
- if (configPayload != null) {
- list.add(configPayload);
- }
-
+ // Payload validation is done in ChildSessionStateMachine
return list;
}
@@ -3194,12 +3198,11 @@ public class IkeSessionStateMachine extends AbstractSessionStateMachine {
"Expected EXCHANGE_TYPE_IKE_AUTH but received: " + exchangeType);
}
+ validateIkeAuthResp(ikeMessage);
+
List<IkePayload> childReqList =
extractChildPayloadsFromMessage(mRetransmitter.getMessage());
-
if (mUseEap) {
- validateIkeAuthRespWithEapPayload(ikeMessage);
-
// childReqList needed after EAP completed, so persist to IkeSessionStateMachine
// state.
mFirstChildReqList = childReqList;
@@ -3207,12 +3210,12 @@ public class IkeSessionStateMachine extends AbstractSessionStateMachine {
IkeEapPayload ikeEapPayload =
ikeMessage.getPayloadForType(
IkePayload.PAYLOAD_TYPE_EAP, IkeEapPayload.class);
-
+ if (ikeEapPayload == null) {
+ throw new AuthenticationFailedException("Missing EAP payload");
+ }
deferMessage(obtainMessage(CMD_EAP_START_EAP_AUTH, ikeEapPayload));
transitionTo(mCreateIkeLocalIkeAuthInEap);
} else {
- validateIkeAuthRespWithChildPayloads(ikeMessage);
-
notifyIkeSessionSetup(ikeMessage);
performFirstChildNegotiation(
@@ -3325,40 +3328,12 @@ public class IkeSessionStateMachine extends AbstractSessionStateMachine {
return buildIkeAuthReqMessage(payloadList);
}
- private void validateIkeAuthRespWithEapPayload(IkeMessage respMsg)
- throws IkeProtocolException {
- IkeEapPayload ikeEapPayload =
- respMsg.getPayloadForType(IkePayload.PAYLOAD_TYPE_EAP, IkeEapPayload.class);
- if (ikeEapPayload == null) {
- throw new AuthenticationFailedException("Missing EAP payload");
- }
-
- // TODO: check that we don't receive any ChildSaRespPayloads here
-
- List<IkePayload> nonEapPayloads = new LinkedList<>();
- nonEapPayloads.addAll(respMsg.ikePayloadList);
- nonEapPayloads.remove(ikeEapPayload);
- validateIkeAuthResp(nonEapPayloads);
- }
-
- private void validateIkeAuthRespWithChildPayloads(IkeMessage respMsg)
- throws IkeProtocolException {
- // Extract and validate existence of payloads for first Child SA setup.
- List<IkePayload> childSaRespPayloads = extractChildPayloadsFromMessage(respMsg);
-
- List<IkePayload> nonChildPayloads = new LinkedList<>();
- nonChildPayloads.addAll(respMsg.ikePayloadList);
- nonChildPayloads.removeAll(childSaRespPayloads);
-
- validateIkeAuthResp(nonChildPayloads);
- }
-
- private void validateIkeAuthResp(List<IkePayload> payloadList) throws IkeProtocolException {
+ private void validateIkeAuthResp(IkeMessage authResp) throws IkeProtocolException {
// Validate IKE Authentication
IkeAuthPayload authPayload = null;
List<IkeCertPayload> certPayloads = new LinkedList<>();
- for (IkePayload payload : payloadList) {
+ for (IkePayload payload : authResp.ikePayloadList) {
switch (payload.payloadType) {
case IkePayload.PAYLOAD_TYPE_ID_RESPONDER:
mRespIdPayload = (IkeIdPayload) payload;
@@ -3380,7 +3355,18 @@ public class IkeSessionStateMachine extends AbstractSessionStateMachine {
case IkePayload.PAYLOAD_TYPE_NOTIFY:
IkeNotifyPayload notifyPayload = (IkeNotifyPayload) payload;
if (notifyPayload.isErrorNotify()) {
- throw notifyPayload.validateAndBuildIkeException();
+ if (notifyPayload.isNewChildSaNotify()
+ && authResp.getPayloadForType(
+ PAYLOAD_TYPE_AUTH, IkeAuthPayload.class)
+ != null) {
+ // If error is for creating Child and Auth payload is included, try
+ // to do authentication first and let ChildSessionStateMachine
+ // handle the error later.
+ continue;
+ } else {
+ throw notifyPayload.validateAndBuildIkeException();
+ }
+
} else {
// Unknown and unexpected status notifications are ignored as per
// RFC7296.
@@ -3390,6 +3376,12 @@ public class IkeSessionStateMachine extends AbstractSessionStateMachine {
+ notifyPayload.notifyType);
}
break;
+ case PAYLOAD_TYPE_SA: // Will be handled separately; fall through
+ case PAYLOAD_TYPE_CP: // Will be handled separately; fall through
+ case PAYLOAD_TYPE_TS_INITIATOR: // Will be handled separately; fall through
+ case PAYLOAD_TYPE_TS_RESPONDER: // Will be handled separately; fall through
+ case PAYLOAD_TYPE_EAP: // Will be handled separately
+ break;
default:
logw(
"Received unexpected payload in IKE AUTH response. Payload"
@@ -3675,18 +3667,11 @@ public class IkeSessionStateMachine extends AbstractSessionStateMachine {
"Expected EXCHANGE_TYPE_IKE_AUTH but received: " + exchangeType);
}
- // Extract and validate existence of payloads for first Child SA setup.
- List<IkePayload> childSaRespPayloads = extractChildPayloadsFromMessage(ikeMessage);
-
- List<IkePayload> nonChildPayloads = new LinkedList<>();
- nonChildPayloads.addAll(ikeMessage.ikePayloadList);
- nonChildPayloads.removeAll(childSaRespPayloads);
-
- validateIkeAuthRespPostEap(nonChildPayloads);
-
+ validateIkeAuthRespPostEap(ikeMessage);
notifyIkeSessionSetup(ikeMessage);
- performFirstChildNegotiation(mFirstChildReqList, childSaRespPayloads);
+ performFirstChildNegotiation(
+ mFirstChildReqList, extractChildPayloadsFromMessage(ikeMessage));
} catch (IkeProtocolException e) {
// Notify the remote because they may have set up the IKE SA.
sendEncryptedIkeMessage(buildIkeDeleteReq(mCurrentIkeSaRecord));
@@ -3703,11 +3688,10 @@ public class IkeSessionStateMachine extends AbstractSessionStateMachine {
handleIkeFatalError(ikeException);
}
- private void validateIkeAuthRespPostEap(List<IkePayload> payloadList)
- throws IkeProtocolException {
+ private void validateIkeAuthRespPostEap(IkeMessage authResp) throws IkeProtocolException {
IkeAuthPayload authPayload = null;
- for (IkePayload payload : payloadList) {
+ for (IkePayload payload : authResp.ikePayloadList) {
switch (payload.payloadType) {
case IkePayload.PAYLOAD_TYPE_AUTH:
authPayload = (IkeAuthPayload) payload;
@@ -3715,7 +3699,18 @@ public class IkeSessionStateMachine extends AbstractSessionStateMachine {
case IkePayload.PAYLOAD_TYPE_NOTIFY:
IkeNotifyPayload notifyPayload = (IkeNotifyPayload) payload;
if (notifyPayload.isErrorNotify()) {
- throw notifyPayload.validateAndBuildIkeException();
+ if (notifyPayload.isNewChildSaNotify()
+ && authResp.getPayloadForType(
+ PAYLOAD_TYPE_AUTH, IkeAuthPayload.class)
+ != null) {
+ // If error is for creating Child and Auth payload is included, try
+ // to do authentication first and let ChildSessionStateMachine
+ // handle the error later.
+ continue;
+ } else {
+ throw notifyPayload.validateAndBuildIkeException();
+ }
+
} else {
// Unknown and unexpected status notifications are ignored as per
// RFC7296.
@@ -3725,6 +3720,11 @@ public class IkeSessionStateMachine extends AbstractSessionStateMachine {
+ notifyPayload.notifyType);
}
break;
+ case PAYLOAD_TYPE_SA: // Will be handled separately; fall through
+ case PAYLOAD_TYPE_CP: // Will be handled separately; fall through
+ case PAYLOAD_TYPE_TS_INITIATOR: // Will be handled separately; fall through
+ case PAYLOAD_TYPE_TS_RESPONDER: // Will be handled separately; fall through
+ break;
default:
logw(
"Received unexpected payload in IKE AUTH response. Payload"
@@ -3878,7 +3878,7 @@ public class IkeSessionStateMachine extends AbstractSessionStateMachine {
+ firstErrorNotifyPayload.notifyType
+ " for rekey IKE. Schedule a retry");
if (!isSimulRekey) {
- scheduleRetry(mCurrentIkeSaRecord.getFutureRekeyEvent());
+ mCurrentIkeSaRecord.rescheduleRekey(RETRY_INTERVAL_MS);
}
}
@@ -3940,7 +3940,6 @@ public class IkeSessionStateMachine extends AbstractSessionStateMachine {
newIntegrity == null ? 0 : newIntegrity.getKeyLength(),
newCipher.getKeyLength(),
isLocalInit,
- new IkeLocalRequest(CMD_LOCAL_REQUEST_REKEY_IKE),
buildSaLifetimeAlarmScheduler(remoteSpi));
addIkeSaRecord(newSaRecord);
@@ -3969,7 +3968,7 @@ public class IkeSessionStateMachine extends AbstractSessionStateMachine {
mRetransmitter = new EncryptedRetransmitter(buildIkeRekeyReq());
} catch (IOException e) {
loge("Fail to assign IKE SPI for rekey. Schedule a retry.", e);
- scheduleRetry(mCurrentIkeSaRecord.getFutureRekeyEvent());
+ mCurrentIkeSaRecord.rescheduleRekey(RETRY_INTERVAL_MS);
transitionTo(mIdle);
}
}
@@ -3981,7 +3980,8 @@ public class IkeSessionStateMachine extends AbstractSessionStateMachine {
@Override
protected void handleTempFailure() {
- mTempFailHandler.handleTempFailure(mCurrentIkeSaRecord.getFutureRekeyEvent());
+ mTempFailHandler.handleTempFailure();
+ mCurrentIkeSaRecord.rescheduleRekey(RETRY_INTERVAL_MS);
}
/**
@@ -4666,21 +4666,23 @@ public class IkeSessionStateMachine extends AbstractSessionStateMachine {
selectedDhGroup,
IkeSaPayload.createInitialIkeSaPayload(saProposals),
randomFactory);
+ if (localAddr instanceof Inet4Address) {
+ // Though RFC says Notify-NAT payload is "just after the Ni and Nr payloads (before
+ // the optional CERTREQ payload)", it also says recipient MUST NOT reject " messages
+ // in which the payloads were not in the "right" order" due to the lack of clarity
+ // of the payload order.
+ payloadList.add(
+ new IkeNotifyPayload(
+ NOTIFY_TYPE_NAT_DETECTION_SOURCE_IP,
+ IkeNotifyPayload.generateNatDetectionData(
+ initIkeSpi, respIkeSpi, localAddr, localPort)));
+ payloadList.add(
+ new IkeNotifyPayload(
+ NOTIFY_TYPE_NAT_DETECTION_DESTINATION_IP,
+ IkeNotifyPayload.generateNatDetectionData(
+ initIkeSpi, respIkeSpi, remoteAddr, remotePort)));
+ }
- // Though RFC says Notify-NAT payload is "just after the Ni and Nr payloads (before the
- // optional CERTREQ payload)", it also says recipient MUST NOT reject " messages in
- // which the payloads were not in the "right" order" due to the lack of clarity of the
- // payload order.
- payloadList.add(
- new IkeNotifyPayload(
- NOTIFY_TYPE_NAT_DETECTION_SOURCE_IP,
- IkeNotifyPayload.generateNatDetectionData(
- initIkeSpi, respIkeSpi, localAddr, localPort)));
- payloadList.add(
- new IkeNotifyPayload(
- NOTIFY_TYPE_NAT_DETECTION_DESTINATION_IP,
- IkeNotifyPayload.generateNatDetectionData(
- initIkeSpi, respIkeSpi, remoteAddr, remotePort)));
return payloadList;
}
diff --git a/src/java/com/android/internal/net/ipsec/ike/IkeSocket.java b/src/java/com/android/internal/net/ipsec/ike/IkeSocket.java
index 8553f30d..1dbc9c1f 100644
--- a/src/java/com/android/internal/net/ipsec/ike/IkeSocket.java
+++ b/src/java/com/android/internal/net/ipsec/ike/IkeSocket.java
@@ -27,11 +27,13 @@ import android.util.LongSparseArray;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.net.ipsec.ike.message.IkeHeader;
-import com.android.internal.net.ipsec.ike.utils.PacketReader;
import java.io.FileDescriptor;
+import java.io.FileInputStream;
+import java.io.IOException;
import java.net.InetAddress;
import java.net.InetSocketAddress;
+import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
@@ -53,7 +55,7 @@ import java.util.Set;
* IkeSessionStateMachine}. Since all {@link IkeSessionStateMachine}s run on the same working
* thread, there will not be concurrent modification problems.
*/
-public abstract class IkeSocket extends PacketReader implements AutoCloseable {
+public abstract class IkeSocket implements AutoCloseable {
private static final String TAG = "IkeSocket";
/** Non-udp-encapsulated IKE packets MUST be sent to 500. */
@@ -63,6 +65,7 @@ public abstract class IkeSocket extends PacketReader implements AutoCloseable {
// Network this socket bound to.
private final Network mNetwork;
+ private final Handler mHandler;
// Map from locally generated IKE SPI to IkeSessionStateMachine instances.
@VisibleForTesting
@@ -74,7 +77,7 @@ public abstract class IkeSocket extends PacketReader implements AutoCloseable {
protected final Set<IkeSessionStateMachine> mAliveIkeSessions = new HashSet<>();
protected IkeSocket(Network network, Handler handler) {
- super(handler);
+ mHandler = handler;
mNetwork = network;
}
@@ -107,6 +110,53 @@ public abstract class IkeSocket extends PacketReader implements AutoCloseable {
}
}
+ /** Starts the packet reading poll-loop. */
+ public void start() {
+ // Start background reader thread
+ new Thread(
+ () -> {
+ try {
+ // Loop will exit and thread will quit when the retrieved fd is closed.
+ // Receiving either EOF or an exception will exit this reader loop.
+ // FileInputStream in uninterruptable, so there's no good way to ensure that
+ // that this thread shuts down except upon FD closure.
+ while (true) {
+ byte[] intercepted = receiveFromFd();
+ if (intercepted == null) {
+ // Exit once we've hit EOF
+ return;
+ } else if (intercepted.length > 0) {
+ // Only save packet if we've received any bytes.
+ getIkeLog()
+ .d(
+ this.getClass().getSimpleName(),
+ "Received packet");
+ mHandler.post(
+ () -> {
+ handlePacket(intercepted, intercepted.length);
+ });
+ }
+ }
+ } catch (IOException ignored) {
+ // Simply exit this reader thread
+ return;
+ }
+ }).start();
+ }
+
+ private byte[] receiveFromFd() throws IOException {
+ FileInputStream in = new FileInputStream(getFd());
+ byte[] inBytes = new byte[4096];
+ int bytesRead = in.read(inBytes);
+
+ if (bytesRead < 0) {
+ return null; // return null for EOF
+ } else if (bytesRead >= 4096) {
+ throw new IllegalStateException("Too big packet. Fragmentation unsupported");
+ }
+ return Arrays.copyOf(inBytes, bytesRead);
+ }
+
/**
* Returns the port that this IKE socket is listening on (bound to).
*/
@@ -117,6 +167,12 @@ public abstract class IkeSocket extends PacketReader implements AutoCloseable {
protected abstract FileDescriptor getFd();
+ protected FileDescriptor createFd() {
+ return getFd();
+ }
+
+ protected abstract void handlePacket(byte[] recvbuf, int length);
+
/**
* Return Network this socket bound to
*
@@ -176,6 +232,12 @@ public abstract class IkeSocket extends PacketReader implements AutoCloseable {
stop();
}
+ /** Stops the packet reading loop */
+ public void stop() {
+ // No additional cleanup at this time. Subclasses are in charge of closing their sockets,
+ // which will result in the packet reading poll loop exiting.
+ }
+
/**
* IPacketReceiver provides a package private interface for handling received packet.
*
diff --git a/src/java/com/android/internal/net/ipsec/ike/IkeUdpEncapSocket.java b/src/java/com/android/internal/net/ipsec/ike/IkeUdpEncapSocket.java
index 2eb10104..11bb9340 100644
--- a/src/java/com/android/internal/net/ipsec/ike/IkeUdpEncapSocket.java
+++ b/src/java/com/android/internal/net/ipsec/ike/IkeUdpEncapSocket.java
@@ -26,6 +26,7 @@ import android.net.IpSecManager.ResourceUnavailableException;
import android.net.IpSecManager.UdpEncapsulationSocket;
import android.net.Network;
import android.os.Handler;
+import android.os.Looper;
import android.system.ErrnoException;
import android.system.Os;
import android.util.LongSparseArray;
@@ -81,7 +82,10 @@ public final class IkeUdpEncapSocket extends IkeSocket {
* @return an IkeUdpEncapSocket instance
*/
public static IkeUdpEncapSocket getIkeUdpEncapSocket(
- Network network, IpSecManager ipsecManager, IkeSessionStateMachine ikeSession)
+ Network network,
+ IpSecManager ipsecManager,
+ IkeSessionStateMachine ikeSession,
+ Looper looper)
throws ErrnoException, IOException, ResourceUnavailableException {
IkeUdpEncapSocket ikeSocket = sNetworkToIkeSocketMap.get(network);
if (ikeSocket == null) {
@@ -92,7 +96,7 @@ public final class IkeUdpEncapSocket extends IkeSocket {
Os.fcntlInt(fd, F_SETFL, SOCK_DGRAM | SOCK_NONBLOCK);
network.bindSocket(fd);
- ikeSocket = new IkeUdpEncapSocket(udpEncapSocket, network, new Handler());
+ ikeSocket = new IkeUdpEncapSocket(udpEncapSocket, network, new Handler(looper));
// Create and register FileDescriptor for receiving IKE packet on current thread.
ikeSocket.start();
@@ -112,21 +116,10 @@ public final class IkeUdpEncapSocket extends IkeSocket {
return mUdpEncapSocket;
}
+ /** Get FileDescriptor for use with the packet reader polling loop */
@Override
protected FileDescriptor getFd() {
- return createFd(); // Returns cached FD
- }
-
- /**
- * Get FileDescriptor of mUdpEncapSocket.
- *
- * <p>PacketReader registers a listener for this file descriptor on the thread where
- * IkeUdpEncapSocket is constructed. When there is a read event, this listener is invoked and
- * then calls {@link handlePacket} to handle the received packet.
- */
- @Override
- protected FileDescriptor createFd() {
- return mUdpEncapSocket.getFileDescriptor();
+ return mUdpEncapSocket.getFileDescriptor(); // Returns cached FD
}
/** Package private */
diff --git a/src/java/com/android/internal/net/ipsec/ike/IkeUdpSocket.java b/src/java/com/android/internal/net/ipsec/ike/IkeUdpSocket.java
index a0bb29ad..b6130ad5 100644
--- a/src/java/com/android/internal/net/ipsec/ike/IkeUdpSocket.java
+++ b/src/java/com/android/internal/net/ipsec/ike/IkeUdpSocket.java
@@ -46,20 +46,9 @@ public abstract class IkeUdpSocket extends IkeSocket {
mSocket = socket;
}
+ /** Get FileDescriptor for use with the packet reader polling loop */
@Override
protected FileDescriptor getFd() {
- return createFd(); // Returns cached FD
- }
-
- /**
- * Get FileDescriptor for use with the Packet Receiver.
- *
- * <p>PacketReader registers a listener for this file descriptor on the thread where
- * IkeUdpSocket is constructed. When there is a read event, this listener is invoked and then
- * calls {@link handlePacket} to handle the received packet.
- */
- @Override
- protected FileDescriptor createFd() {
return mSocket;
}
diff --git a/src/java/com/android/internal/net/ipsec/ike/SaRecord.java b/src/java/com/android/internal/net/ipsec/ike/SaRecord.java
index a0b585fc..c2d86dc9 100644
--- a/src/java/com/android/internal/net/ipsec/ike/SaRecord.java
+++ b/src/java/com/android/internal/net/ipsec/ike/SaRecord.java
@@ -31,8 +31,6 @@ import android.os.SystemClock;
import android.util.CloseGuard;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.net.ipsec.ike.IkeLocalRequestScheduler.ChildLocalRequest;
-import com.android.internal.net.ipsec.ike.IkeLocalRequestScheduler.LocalRequest;
import com.android.internal.net.ipsec.ike.crypto.IkeCipher;
import com.android.internal.net.ipsec.ike.crypto.IkeMacIntegrity;
import com.android.internal.net.ipsec.ike.crypto.IkeMacPrf;
@@ -82,10 +80,7 @@ public abstract class SaRecord implements AutoCloseable {
private final byte[] mSkEi;
private final byte[] mSkEr;
- private final SaLifetimeAlarmScheduler mSaLifetimeAlarmScheduler;
-
- // TODO(b/149058810): Use AlarmManager and PendingIntent to schedule rekey
- private final LocalRequest mFutureRekeyEvent;
+ @VisibleForTesting final SaLifetimeAlarmScheduler mSaLifetimeAlarmScheduler;
private final CloseGuard mCloseGuard = new CloseGuard();
@@ -98,7 +93,6 @@ public abstract class SaRecord implements AutoCloseable {
byte[] skAr,
byte[] skEi,
byte[] skEr,
- LocalRequest futureRekeyEvent,
SaLifetimeAlarmScheduler saLifetimeAlarmScheduler) {
isLocalInit = localInit;
nonceInitiator = nonceInit;
@@ -117,9 +111,6 @@ public abstract class SaRecord implements AutoCloseable {
mSaLifetimeAlarmScheduler = saLifetimeAlarmScheduler;
mSaLifetimeAlarmScheduler.scheduleLifetimeExpiryAlarm(getTag());
- // TODO(b/149058810): Use alarmManager to schedule rekey event and remove mFutureRekeyEvent
- mFutureRekeyEvent = futureRekeyEvent;
-
mCloseGuard.open("close");
}
@@ -167,6 +158,11 @@ public abstract class SaRecord implements AutoCloseable {
return isLocalInit ? mSkEr : mSkEi;
}
+ /** Reschedule rekey */
+ public void rescheduleRekey(long retryDelayMs) {
+ mSaLifetimeAlarmScheduler.rescheduleRekey(retryDelayMs);
+ }
+
/** Check that the SaRecord was closed properly. */
@Override
protected void finalize() throws Throwable {
@@ -178,17 +174,10 @@ public abstract class SaRecord implements AutoCloseable {
@Override
public void close() {
- mFutureRekeyEvent.cancel();
-
mSaLifetimeAlarmScheduler.cancelLifetimeExpiryAlarm(getTag());
}
/** Package private */
- LocalRequest getFutureRekeyEvent() {
- return mFutureRekeyEvent;
- }
-
- /** Package private */
@VisibleForTesting
static void setSaRecordHelper(ISaRecordHelper helper) {
sSaRecordHelper = helper;
@@ -336,7 +325,6 @@ public abstract class SaRecord implements AutoCloseable {
skEr,
skPi,
skPr,
- ikeSaRecordConfig.futureRekeyEvent,
ikeSaRecordConfig.saLifetimeAlarmScheduler);
}
@@ -471,8 +459,7 @@ public abstract class SaRecord implements AutoCloseable {
skEr,
inTransform,
outTransform,
- childSaRecordConfig.futureRekeyEvent,
- childSaRecordConfig.saLifetimeAlarmSched);
+ childSaRecordConfig.saLifetimeAlarmScheduler);
} catch (Exception e) {
if (initTransform != null) initTransform.close();
@@ -560,9 +547,26 @@ public abstract class SaRecord implements AutoCloseable {
AlarmManager.ELAPSED_REALTIME_WAKEUP,
SystemClock.elapsedRealtime() + mDeleteDelayMs,
mDeleteSaIntent);
- getIkeLog().d(tag, "Hard lifetime expiry alarm set for " + mDeleteDelayMs + "ms");
+ mAlarmManager.setExactAndAllowWhileIdle(
+ AlarmManager.ELAPSED_REALTIME_WAKEUP,
+ SystemClock.elapsedRealtime() + mRekeyDelayMs,
+ mRekeySaIntent);
+
+ getIkeLog()
+ .d(
+ tag,
+ "Lifetime alarm set: Hard lifetime ("
+ + mDeleteDelayMs
+ + "ms) Soft lifetime ("
+ + mRekeyDelayMs
+ + "ms)");
+ }
- // TODO: Schedule alarm for rekey
+ public void rescheduleRekey(long retryDelayMs) {
+ mAlarmManager.setExactAndAllowWhileIdle(
+ AlarmManager.ELAPSED_REALTIME_WAKEUP,
+ SystemClock.elapsedRealtime() + retryDelayMs,
+ mRekeySaIntent);
}
public void cancelLifetimeExpiryAlarm(String tag) {
@@ -591,8 +595,7 @@ public abstract class SaRecord implements AutoCloseable {
public final boolean isTransport;
public final boolean isLocalInit;
public final boolean hasIntegrityAlgo;
- public final ChildLocalRequest futureRekeyEvent;
- public final SaLifetimeAlarmScheduler saLifetimeAlarmSched;
+ public final SaLifetimeAlarmScheduler saLifetimeAlarmScheduler;
ChildSaRecordConfig(
Context context,
@@ -607,8 +610,7 @@ public abstract class SaRecord implements AutoCloseable {
byte[] skD,
boolean isTransport,
boolean isLocalInit,
- ChildLocalRequest futureRekeyEvent,
- SaLifetimeAlarmScheduler saLifetimeAlarmSched) {
+ SaLifetimeAlarmScheduler saLifetimeAlarmScheduler) {
this.context = context;
this.initSpi = initSpi;
this.respSpi = respSpi;
@@ -622,8 +624,7 @@ public abstract class SaRecord implements AutoCloseable {
this.isTransport = isTransport;
this.isLocalInit = isLocalInit;
hasIntegrityAlgo = (integrityAlgo != null);
- this.futureRekeyEvent = futureRekeyEvent;
- this.saLifetimeAlarmSched = saLifetimeAlarmSched;
+ this.saLifetimeAlarmScheduler = saLifetimeAlarmScheduler;
}
}
@@ -663,7 +664,6 @@ public abstract class SaRecord implements AutoCloseable {
byte[] skEr,
byte[] skPi,
byte[] skPr,
- LocalRequest futureRekeyEvent,
SaLifetimeAlarmScheduler saLifetimeAlarmScheduler) {
super(
localInit,
@@ -673,7 +673,6 @@ public abstract class SaRecord implements AutoCloseable {
skAr,
skEi,
skEr,
- futureRekeyEvent,
saLifetimeAlarmScheduler);
mInitiatorSpiResource = initSpi;
@@ -706,7 +705,6 @@ public abstract class SaRecord implements AutoCloseable {
IkeMacPrf prf,
int integrityKeyLength,
int encryptionKeyLength,
- LocalRequest futureRekeyEvent,
SaLifetimeAlarmScheduler saLifetimeAlarmScheduler)
throws GeneralSecurityException {
return sSaRecordHelper.makeFirstIkeSaRecord(
@@ -719,7 +717,6 @@ public abstract class SaRecord implements AutoCloseable {
integrityKeyLength,
encryptionKeyLength,
true /*isLocalInit*/,
- futureRekeyEvent,
saLifetimeAlarmScheduler));
}
@@ -735,7 +732,6 @@ public abstract class SaRecord implements AutoCloseable {
int integrityKeyLength,
int encryptionKeyLength,
boolean isLocalInit,
- LocalRequest futureRekeyEvent,
SaLifetimeAlarmScheduler saLifetimeAlarmScheduler)
throws GeneralSecurityException {
return sSaRecordHelper.makeRekeyedIkeSaRecord(
@@ -750,7 +746,6 @@ public abstract class SaRecord implements AutoCloseable {
integrityKeyLength,
encryptionKeyLength,
isLocalInit,
- futureRekeyEvent,
saLifetimeAlarmScheduler));
}
@@ -929,7 +924,6 @@ public abstract class SaRecord implements AutoCloseable {
public final int integrityKeyLength;
public final int encryptionKeyLength;
public final boolean isLocalInit;
- public final LocalRequest futureRekeyEvent;
public final SaLifetimeAlarmScheduler saLifetimeAlarmScheduler;
IkeSaRecordConfig(
@@ -939,7 +933,6 @@ public abstract class SaRecord implements AutoCloseable {
int integrityKeyLength,
int encryptionKeyLength,
boolean isLocalInit,
- LocalRequest futureRekeyEvent,
SaLifetimeAlarmScheduler saLifetimeAlarmScheduler) {
this.initSpi = initSpi;
this.respSpi = respSpi;
@@ -947,7 +940,6 @@ public abstract class SaRecord implements AutoCloseable {
this.integrityKeyLength = integrityKeyLength;
this.encryptionKeyLength = encryptionKeyLength;
this.isLocalInit = isLocalInit;
- this.futureRekeyEvent = futureRekeyEvent;
this.saLifetimeAlarmScheduler = saLifetimeAlarmScheduler;
}
}
@@ -979,7 +971,6 @@ public abstract class SaRecord implements AutoCloseable {
byte[] skEr,
IpSecTransform inTransform,
IpSecTransform outTransform,
- ChildLocalRequest futureRekeyEvent,
SaLifetimeAlarmScheduler saLifetimeAlarmScheduler) {
super(
localInit,
@@ -989,7 +980,6 @@ public abstract class SaRecord implements AutoCloseable {
skAr,
skEi,
skEr,
- futureRekeyEvent,
saLifetimeAlarmScheduler);
mInboundSpi = inSpi;
@@ -1017,7 +1007,6 @@ public abstract class SaRecord implements AutoCloseable {
byte[] skD,
boolean isTransport,
boolean isLocalInit,
- ChildLocalRequest futureRekeyEvent,
SaLifetimeAlarmScheduler saLifetimeAlarmScheduler)
throws GeneralSecurityException, ResourceUnavailableException,
SpiUnavailableException, IOException {
@@ -1037,7 +1026,6 @@ public abstract class SaRecord implements AutoCloseable {
skD,
isTransport,
isLocalInit,
- futureRekeyEvent,
saLifetimeAlarmScheduler));
}
diff --git a/src/java/com/android/internal/net/ipsec/ike/message/IkeCertReqPayload.java b/src/java/com/android/internal/net/ipsec/ike/message/IkeCertReqPayload.java
new file mode 100644
index 00000000..92f32074
--- /dev/null
+++ b/src/java/com/android/internal/net/ipsec/ike/message/IkeCertReqPayload.java
@@ -0,0 +1,97 @@
+/*
+ * 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 com.android.internal.net.ipsec.ike.message;
+
+import static com.android.internal.net.ipsec.ike.message.IkeCertPayload.CERT_ENCODING_LEN;
+import static com.android.internal.net.ipsec.ike.message.IkeCertPayload.CertificateEncoding;
+
+import android.net.ipsec.ike.exceptions.IkeProtocolException;
+
+import java.nio.ByteBuffer;
+
+/**
+ * This class represents a Certificate Request Payload
+ *
+ * <p>A Certificate Request Payload provides suggestion for an end certificate to select. Receiver
+ * of this payload is allowed to send an alternate. It is possible that there is a preferred CA sent
+ * in the IkeCertReqPayload, but an alternate is still acceptable.
+ *
+ * <p>IKE library will always ignore this payload since only one end certificate can be configured
+ * by users.
+ *
+ * @see <a href="https://tools.ietf.org/html/rfc7296#section-3.8">RFC 7296, Internet Key Exchange
+ * Protocol Version 2 (IKEv2)</a>
+ */
+public class IkeCertReqPayload extends IkePayload {
+ /** Certificate encoding type */
+ @CertificateEncoding public final int certEncodingType;
+ /** Concatenated list of SHA-1 hashes of CAs' Subject Public Key Info */
+ public final byte[] caSubjectPublicKeyInforHashes;
+
+ /**
+ * Construct an instance of IkeCertReqPayload from decoding an inbound IKE packet.
+ *
+ * <p>NegativeArraySizeException and BufferUnderflowException will be caught in {@link
+ * IkeMessage}
+ *
+ * @param critical indicates if this payload is critical. Ignored in supported payload as
+ * instructed by the RFC 7296.
+ * @param payloadBody payload body in byte array
+ * @throws IkeProtocolException if there is any error
+ */
+ public IkeCertReqPayload(boolean critical, byte[] payloadBody) throws IkeProtocolException {
+ super(PAYLOAD_TYPE_CERT_REQUEST, critical);
+
+ ByteBuffer inputBuffer = ByteBuffer.wrap(payloadBody);
+ certEncodingType = Byte.toUnsignedInt(inputBuffer.get());
+ caSubjectPublicKeyInforHashes = new byte[inputBuffer.remaining()];
+ inputBuffer.get(caSubjectPublicKeyInforHashes);
+ }
+
+ /**
+ * Encode Certificate Request Payload to ByteBuffer.
+ *
+ * @param nextPayload type of payload that follows this payload.
+ * @param byteBuffer destination ByteBuffer that stores encoded payload.
+ */
+ @Override
+ protected void encodeToByteBuffer(@PayloadType int nextPayload, ByteBuffer byteBuffer) {
+ encodePayloadHeaderToByteBuffer(nextPayload, getPayloadLength(), byteBuffer);
+
+ byteBuffer.put((byte) certEncodingType).put(caSubjectPublicKeyInforHashes);
+ }
+
+ /**
+ * Get entire payload length.
+ *
+ * @return entire payload length.
+ */
+ @Override
+ protected int getPayloadLength() {
+ return GENERIC_HEADER_LENGTH + CERT_ENCODING_LEN + caSubjectPublicKeyInforHashes.length;
+ }
+
+ /**
+ * Return the payload type as a String.
+ *
+ * @return the payload type as a String.
+ */
+ @Override
+ public String getTypeString() {
+ return "CertReq";
+ }
+}
diff --git a/src/java/com/android/internal/net/ipsec/ike/message/IkeConfigPayload.java b/src/java/com/android/internal/net/ipsec/ike/message/IkeConfigPayload.java
index bb5f9a39..778faa41 100644
--- a/src/java/com/android/internal/net/ipsec/ike/message/IkeConfigPayload.java
+++ b/src/java/com/android/internal/net/ipsec/ike/message/IkeConfigPayload.java
@@ -538,7 +538,6 @@ public final class IkeConfigPayload extends IkePayload {
super(CONFIG_ATTR_INTERNAL_IP4_DHCP, value);
}
- @Override
public Inet4Address getAddress() {
return address;
}
@@ -572,7 +571,6 @@ public final class IkeConfigPayload extends IkePayload {
super(CONFIG_ATTR_INTERNAL_IP4_DNS, value);
}
- @Override
public Inet4Address getAddress() {
return address;
}
@@ -979,7 +977,6 @@ public final class IkeConfigPayload extends IkePayload {
super(CONFIG_ATTR_INTERNAL_IP6_DNS, value);
}
- @Override
public Inet6Address getAddress() {
return address;
}
diff --git a/src/java/com/android/internal/net/ipsec/ike/message/IkeMessage.java b/src/java/com/android/internal/net/ipsec/ike/message/IkeMessage.java
index 1c5f1f6b..3da649ac 100644
--- a/src/java/com/android/internal/net/ipsec/ike/message/IkeMessage.java
+++ b/src/java/com/android/internal/net/ipsec/ike/message/IkeMessage.java
@@ -503,10 +503,12 @@ public final class IkeMessage {
int fragNum = i + 1; // 1-based
+ int fragFirstInnerPayload =
+ i == 0 ? firstInnerPayload : IkePayload.PAYLOAD_TYPE_NO_NEXT;
IkeSkfPayload skfPayload =
new IkeSkfPayload(
- ikeHeader,
- firstInnerPayload,
+ skfHeader,
+ fragFirstInnerPayload,
unencryptedData,
integrityMac,
encryptCipher,
@@ -515,11 +517,7 @@ public final class IkeMessage {
fragNum,
totalFragments);
- packetList[i] =
- encodeHeaderAndBody(
- skfHeader,
- skfPayload,
- i == 0 ? firstInnerPayload : IkePayload.PAYLOAD_TYPE_NO_NEXT);
+ packetList[i] = encodeHeaderAndBody(skfHeader, skfPayload, fragFirstInnerPayload);
getIkeLog()
.d(
"IkeMessage",
@@ -528,7 +526,7 @@ public final class IkeMessage {
+ "/"
+ totalFragments
+ "): "
- + getIkeLog().pii(packetList[0]));
+ + getIkeLog().pii(packetList[i]));
}
return packetList;
diff --git a/src/java/com/android/internal/net/ipsec/ike/message/IkeNotifyPayload.java b/src/java/com/android/internal/net/ipsec/ike/message/IkeNotifyPayload.java
index b9087777..30145338 100644
--- a/src/java/com/android/internal/net/ipsec/ike/message/IkeNotifyPayload.java
+++ b/src/java/com/android/internal/net/ipsec/ike/message/IkeNotifyPayload.java
@@ -16,6 +16,7 @@
package com.android.internal.net.ipsec.ike.message;
+import static android.net.ipsec.ike.IkeManager.getIkeLog;
import static android.net.ipsec.ike.exceptions.IkeProtocolException.ERROR_TYPE_AUTHENTICATION_FAILED;
import static android.net.ipsec.ike.exceptions.IkeProtocolException.ERROR_TYPE_CHILD_SA_NOT_FOUND;
import static android.net.ipsec.ike.exceptions.IkeProtocolException.ERROR_TYPE_FAILED_CP_REQUIRED;
@@ -74,6 +75,8 @@ import java.util.Set;
* Version 2 (IKEv2)</a>
*/
public final class IkeNotifyPayload extends IkeInformationalPayload {
+ private static final String TAG = IkeNotifyPayload.class.getSimpleName();
+
@Retention(RetentionPolicy.SOURCE)
@IntDef({
NOTIFY_TYPE_ADDITIONAL_TS_POSSIBLE,
@@ -267,8 +270,7 @@ public final class IkeNotifyPayload extends IkeInformationalPayload {
private void validateNotifyPayloadForIkeAndNewChild() throws InvalidSyntaxException {
if (protocolId != PROTOCOL_ID_UNSET) {
- throw new InvalidSyntaxException(
- "Expected Procotol ID unset: Protocol ID is " + protocolId);
+ getIkeLog().w(TAG, "Expected Procotol ID unset: Protocol ID is " + protocolId);
}
if (notifyType == ERROR_TYPE_INVALID_SELECTORS
diff --git a/src/java/com/android/internal/net/ipsec/ike/message/IkePayloadFactory.java b/src/java/com/android/internal/net/ipsec/ike/message/IkePayloadFactory.java
index 2f191d4e..b50f0934 100644
--- a/src/java/com/android/internal/net/ipsec/ike/message/IkePayloadFactory.java
+++ b/src/java/com/android/internal/net/ipsec/ike/message/IkePayloadFactory.java
@@ -60,7 +60,6 @@ final class IkePayloadFactory {
int payloadType, boolean isCritical, boolean isResp, byte[] payloadBody)
throws IkeProtocolException {
switch (payloadType) {
- // TODO: Add cases for creating supported payloads.
case IkePayload.PAYLOAD_TYPE_SA:
return new IkeSaPayload(isCritical, isResp, payloadBody);
case IkePayload.PAYLOAD_TYPE_KE:
@@ -71,6 +70,8 @@ final class IkePayloadFactory {
return new IkeIdPayload(isCritical, payloadBody, false);
case IkePayload.PAYLOAD_TYPE_CERT:
return IkeCertPayload.getIkeCertPayload(isCritical, payloadBody);
+ case IkeCertReqPayload.PAYLOAD_TYPE_CERT_REQUEST:
+ return new IkeCertReqPayload(isCritical, payloadBody);
case IkePayload.PAYLOAD_TYPE_AUTH:
return IkeAuthPayload.getIkeAuthPayload(isCritical, payloadBody);
case IkePayload.PAYLOAD_TYPE_NONCE:
diff --git a/src/java/com/android/internal/net/ipsec/ike/message/IkeSaPayload.java b/src/java/com/android/internal/net/ipsec/ike/message/IkeSaPayload.java
index d2dd3943..40eecf02 100644
--- a/src/java/com/android/internal/net/ipsec/ike/message/IkeSaPayload.java
+++ b/src/java/com/android/internal/net/ipsec/ike/message/IkeSaPayload.java
@@ -514,7 +514,20 @@ public final class IkeSaPayload extends IkePayload {
Transform[] decodeTransforms(int count, ByteBuffer inputBuffer) throws IkeProtocolException;
}
- // TODO: Add another constructor for building outbound message.
+ /**
+ * Release IPsec SPI resources in the outbound Create Child request
+ *
+ * <p>This method is usually called when an IKE library fails to receive a Create Child response
+ * before it is terminated. It is also safe to call after the Create Child exchange has
+ * succeeded because the newly created IpSecTransform pair will hold the IPsec SPI resource.
+ */
+ public void releaseChildSpiResourcesIfExists() {
+ for (Proposal proposal : proposalList) {
+ if (proposal instanceof ChildProposal) {
+ proposal.releaseSpiResourceIfExists();
+ }
+ }
+ }
/**
* This class represents the common information of an IKE Proposal and a Child Proposal.
diff --git a/src/java/com/android/internal/net/ipsec/ike/utils/RandomnessFactory.java b/src/java/com/android/internal/net/ipsec/ike/utils/RandomnessFactory.java
index 9ebb057a..2e5d1703 100644
--- a/src/java/com/android/internal/net/ipsec/ike/utils/RandomnessFactory.java
+++ b/src/java/com/android/internal/net/ipsec/ike/utils/RandomnessFactory.java
@@ -31,7 +31,7 @@ import java.security.SecureRandom;
public class RandomnessFactory implements EapRandomFactory {
// This constant is mirrored of android.net.NetworkCapabilities.TRANSPORT_TEST due to lack of
// @TestApi guarantees in mainline modules
- public static final int NETWORK_CAPABILITY_TRANSPORT_TEST = 7;
+ public static final int TRANSPORT_TEST = 7;
private final boolean mIsTestModeEnabled;
@@ -41,8 +41,7 @@ public class RandomnessFactory implements EapRandomFactory {
NetworkCapabilities networkCapabilities = connectManager.getNetworkCapabilities(network);
mIsTestModeEnabled =
- networkCapabilities != null
- && networkCapabilities.hasCapability(NETWORK_CAPABILITY_TRANSPORT_TEST);
+ networkCapabilities != null && networkCapabilities.hasTransport(TRANSPORT_TEST);
}
/**
diff --git a/tests/iketests/AndroidManifest.xml b/tests/iketests/AndroidManifest.xml
index 0abddbaa..bdcfdb71 100644
--- a/tests/iketests/AndroidManifest.xml
+++ b/tests/iketests/AndroidManifest.xml
@@ -22,6 +22,8 @@
<uses-permission android:name="android.permission.INTERNET"/>
<!--Allow tests to call ConnectivityManager#getActiveNetwork()-->
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
+ <!--Allow tests to use wake locks-->
+ <uses-permission android:name="android.permission.WAKE_LOCK"/>
<!--
'debuggable=true' is required to properly load mockito jvmti dependencies,
diff --git a/tests/iketests/src/java/android/net/ipsec/ike/SaProposalTest.java b/tests/iketests/src/java/android/net/ipsec/ike/SaProposalTest.java
index d4efb0c3..0c98bfa0 100644
--- a/tests/iketests/src/java/android/net/ipsec/ike/SaProposalTest.java
+++ b/tests/iketests/src/java/android/net/ipsec/ike/SaProposalTest.java
@@ -16,8 +16,15 @@
package android.net.ipsec.ike;
+import static android.net.ipsec.ike.SaProposal.DH_GROUP_1024_BIT_MODP;
+import static android.net.ipsec.ike.SaProposal.DH_GROUP_2048_BIT_MODP;
+import static android.net.ipsec.ike.SaProposal.ENCRYPTION_ALGORITHM_AES_GCM_12;
+import static android.net.ipsec.ike.SaProposal.ENCRYPTION_ALGORITHM_AES_GCM_8;
+import static android.net.ipsec.ike.SaProposal.INTEGRITY_ALGORITHM_NONE;
import static android.net.ipsec.ike.SaProposal.KEY_LEN_AES_128;
import static android.net.ipsec.ike.SaProposal.KEY_LEN_UNUSED;
+import static android.net.ipsec.ike.SaProposal.PSEUDORANDOM_FUNCTION_AES128_XCBC;
+import static android.net.ipsec.ike.SaProposal.PSEUDORANDOM_FUNCTION_SHA2_256;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
@@ -318,4 +325,30 @@ public final class SaProposalTest {
new Transform[] {mIntegrityNoneTransform},
new Transform[] {mIntegrityHmacSha1Transform}));
}
+
+ @Test
+ public void testIsNegotiatedFromProposalWithIntegrityNone() throws Exception {
+ SaProposal respProposal =
+ new IkeSaProposal.Builder()
+ .addEncryptionAlgorithm(
+ ENCRYPTION_ALGORITHM_AES_GCM_12, SaProposal.KEY_LEN_AES_128)
+ .addDhGroup(DH_GROUP_2048_BIT_MODP)
+ .addPseudorandomFunction(PSEUDORANDOM_FUNCTION_AES128_XCBC)
+ .build();
+
+ SaProposal reqProposal =
+ new IkeSaProposal.Builder()
+ .addEncryptionAlgorithm(
+ ENCRYPTION_ALGORITHM_AES_GCM_12, SaProposal.KEY_LEN_AES_128)
+ .addEncryptionAlgorithm(
+ ENCRYPTION_ALGORITHM_AES_GCM_8, SaProposal.KEY_LEN_AES_128)
+ .addIntegrityAlgorithm(INTEGRITY_ALGORITHM_NONE)
+ .addDhGroup(DH_GROUP_1024_BIT_MODP)
+ .addDhGroup(DH_GROUP_2048_BIT_MODP)
+ .addPseudorandomFunction(PSEUDORANDOM_FUNCTION_AES128_XCBC)
+ .addPseudorandomFunction(PSEUDORANDOM_FUNCTION_SHA2_256)
+ .build();
+
+ assertTrue(respProposal.isNegotiatedFrom(reqProposal));
+ }
}
diff --git a/tests/iketests/src/java/com/android/internal/net/ipsec/ike/ChildSessionStateMachineTest.java b/tests/iketests/src/java/com/android/internal/net/ipsec/ike/ChildSessionStateMachineTest.java
index 20e4b76b..e20d0b1d 100644
--- a/tests/iketests/src/java/com/android/internal/net/ipsec/ike/ChildSessionStateMachineTest.java
+++ b/tests/iketests/src/java/com/android/internal/net/ipsec/ike/ChildSessionStateMachineTest.java
@@ -22,6 +22,7 @@ import static android.net.ipsec.ike.exceptions.IkeProtocolException.ERROR_TYPE_T
import static android.system.OsConstants.AF_INET;
import static com.android.internal.net.TestUtils.createMockRandomFactory;
+import static com.android.internal.net.ipsec.ike.AbstractSessionStateMachine.RETRY_INTERVAL_MS;
import static com.android.internal.net.ipsec.ike.ChildSessionStateMachine.CMD_FORCE_TRANSITION;
import static com.android.internal.net.ipsec.ike.IkeSessionStateMachine.CMD_LOCAL_REQUEST_REKEY_CHILD;
import static com.android.internal.net.ipsec.ike.IkeSessionStateMachine.IKE_EXCHANGE_SUBTYPE_DELETE_CHILD;
@@ -47,11 +48,9 @@ import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
-import static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyBoolean;
import static org.mockito.Matchers.anyInt;
-import static org.mockito.Matchers.anyLong;
import static org.mockito.Matchers.anyObject;
import static org.mockito.Matchers.anyString;
import static org.mockito.Matchers.eq;
@@ -77,6 +76,7 @@ import android.net.ipsec.ike.ChildSessionCallback;
import android.net.ipsec.ike.ChildSessionConfiguration;
import android.net.ipsec.ike.ChildSessionParams;
import android.net.ipsec.ike.IkeManager;
+import android.net.ipsec.ike.IkeSaProposal;
import android.net.ipsec.ike.IkeTrafficSelector;
import android.net.ipsec.ike.SaProposal;
import android.net.ipsec.ike.TunnelModeChildSessionParams;
@@ -224,8 +224,9 @@ public final class ChildSessionStateMachineTest {
private ArgumentMatcher<ChildLocalRequest> mRekeyChildLocalReqMatcher =
(argument) -> {
- return CMD_LOCAL_REQUEST_REKEY_CHILD == argument.procedureType
- && mMockChildSessionCallback == argument.childSessionCallback;
+ return (CMD_LOCAL_REQUEST_REKEY_CHILD == argument.procedureType
+ && mMockChildSessionCallback == argument.childSessionCallback
+ || CURRENT_CHILD_SA_SPI_OUT == argument.remoteSpi);
};
public ChildSessionStateMachineTest() {
@@ -270,19 +271,7 @@ public final class ChildSessionStateMachineTest {
// Setup thread and looper
mLooper = new TestLooper();
- mChildSessionStateMachine =
- new ChildSessionStateMachine(
- mLooper.getLooper(),
- mContext,
- IKE_SESSION_UNIQUE_ID,
- mMockAlarmManager,
- createMockRandomFactory(),
- mMockIpSecManager,
- mIpSecSpiGenerator,
- mChildSessionParams,
- mSpyUserCbExecutor,
- mMockChildSessionCallback,
- mMockChildSessionSmCallback);
+ mChildSessionStateMachine = buildChildSession(mChildSessionParams);
mChildSessionStateMachine.setDbg(true);
SaRecord.setSaRecordHelper(mMockSaRecordHelper);
@@ -396,7 +385,6 @@ public final class ChildSessionStateMachineTest {
null,
mock(IpSecTransform.class),
mock(IpSecTransform.class),
- mock(ChildLocalRequest.class),
mock(SaLifetimeAlarmScheduler.class)));
doNothing().when(child).close();
return child;
@@ -438,11 +426,7 @@ public final class ChildSessionStateMachineTest {
assertFalse(childSaRecordConfig.isTransport);
assertEquals(isLocalInit, childSaRecordConfig.isLocalInit);
assertTrue(childSaRecordConfig.hasIntegrityAlgo);
- assertEquals(
- CMD_LOCAL_REQUEST_REKEY_CHILD, childSaRecordConfig.futureRekeyEvent.procedureType);
- assertEquals(
- mMockChildSessionCallback,
- childSaRecordConfig.futureRekeyEvent.childSessionCallback);
+ assertNotNull(childSaRecordConfig.saLifetimeAlarmScheduler);
}
private void verifyNotifyUsersCreateIpSecSa(
@@ -492,8 +476,6 @@ public final class ChildSessionStateMachineTest {
verify(mMockChildSessionSmCallback)
.onChildSaCreated(anyInt(), eq(mChildSessionStateMachine));
- verify(mMockChildSessionSmCallback)
- .scheduleLocalRequest(argThat(mRekeyChildLocalReqMatcher), anyLong());
verify(mMockChildSessionSmCallback).onProcedureFinished(mChildSessionStateMachine);
assertTrue(
mChildSessionStateMachine.getCurrentState()
@@ -951,6 +933,14 @@ public final class ChildSessionStateMachineTest {
(IkeSaPayload)
(IkeTestUtils.hexStringToIkePayload(
IkePayload.PAYLOAD_TYPE_SA, true, inboundSaHexString));
+
+ return makeInboundRekeyChildPayloads(remoteSpi, saPayload, isLocalInitRekey);
+ }
+
+ private List<IkePayload> makeInboundRekeyChildPayloads(
+ int remoteSpi, IkeSaPayload saPayload, boolean isLocalInitRekey) throws Exception {
+ List<IkePayload> inboundPayloads = new LinkedList<>();
+
inboundPayloads.add(saPayload);
// Build TS Payloads
@@ -1023,8 +1013,6 @@ public final class ChildSessionStateMachineTest {
.onChildSaCreated(
eq(mSpyLocalInitNewChildSaRecord.getRemoteSpi()),
eq(mChildSessionStateMachine));
- verify(mMockChildSessionSmCallback)
- .scheduleLocalRequest(argThat(mRekeyChildLocalReqMatcher), anyLong());
verify(mMockSaRecordHelper)
.makeChildSaRecord(
@@ -1061,9 +1049,7 @@ public final class ChildSessionStateMachineTest {
mLooper.dispatchAll();
// Verify rekey has been rescheduled and Child Session is alive
- verify(mMockChildSessionSmCallback)
- .scheduleRetryLocalRequest(
- (ChildLocalRequest) mSpyCurrentChildSaRecord.getFutureRekeyEvent());
+ verify(mSpyCurrentChildSaRecord).rescheduleRekey(eq(RETRY_INTERVAL_MS));
assertTrue(
mChildSessionStateMachine.getCurrentState()
instanceof ChildSessionStateMachine.Idle);
@@ -1261,6 +1247,8 @@ public final class ChildSessionStateMachineTest {
IKE_EXCHANGE_SUBTYPE_REKEY_CHILD, EXCHANGE_TYPE_CREATE_CHILD_SA, rekeyReqPayloads);
mLooper.dispatchAll();
+ assertEquals(0, mChildSessionStateMachine.mSaProposal.getDhGroups().size());
+
assertTrue(
mChildSessionStateMachine.getCurrentState()
instanceof ChildSessionStateMachine.RekeyChildRemoteDelete);
@@ -1287,8 +1275,6 @@ public final class ChildSessionStateMachineTest {
.onChildSaCreated(
eq(mSpyRemoteInitNewChildSaRecord.getRemoteSpi()),
eq(mChildSessionStateMachine));
- verify(mMockChildSessionSmCallback)
- .scheduleLocalRequest(argThat(mRekeyChildLocalReqMatcher), anyLong());
verify(mMockSaRecordHelper)
.makeChildSaRecord(
@@ -1674,4 +1660,123 @@ public final class ChildSessionStateMachineTest {
verifyHandleFatalErrorAndQuit(IkeInternalException.class);
verify(spyIkeLog).wtf(anyString(), anyString(), any(RuntimeException.class));
}
+
+ @Test
+ public void testFirstChildLocalRekey() throws Exception {
+ ChildSaProposal saProposal = buildSaProposalWithDhGroup(SaProposal.DH_GROUP_2048_BIT_MODP);
+ ChildSessionParams childSessionParams =
+ new TunnelModeChildSessionParams.Builder()
+ .addSaProposal(saProposal)
+ .addInternalAddressRequest(AF_INET)
+ .addInternalAddressRequest(INTERNAL_ADDRESS)
+ .build();
+ mChildSessionStateMachine = buildChildSession(childSessionParams);
+ mChildSessionStateMachine.mIsFirstChild = true;
+ mChildSessionStateMachine.setDbg(true);
+ mChildSessionStateMachine.start();
+
+ setupIdleStateMachine();
+
+ assertEquals(0, mChildSessionStateMachine.mSaProposal.getDhGroups().size());
+
+ // Send Rekey-Create request
+ mChildSessionStateMachine.rekeyChildSession();
+ mLooper.dispatchAll();
+
+ assertTrue(
+ mChildSessionStateMachine.getCurrentState()
+ instanceof ChildSessionStateMachine.RekeyChildLocalCreate);
+
+ verifyOutboundRekeyKePayload(false /*isResp*/);
+ }
+
+ private void verifyOutboundRekeyKePayload(boolean isResp) {
+ verify(mMockChildSessionSmCallback)
+ .onOutboundPayloadsReady(
+ eq(EXCHANGE_TYPE_CREATE_CHILD_SA),
+ eq(isResp),
+ mPayloadListCaptor.capture(),
+ eq(mChildSessionStateMachine));
+
+ // Verify outbound payload list
+ List<IkePayload> reqPayloadList = mPayloadListCaptor.getValue();
+
+ assertNotNull(
+ IkePayload.getPayloadForTypeInProvidedList(
+ PAYLOAD_TYPE_KE, IkeKePayload.class, reqPayloadList));
+ }
+
+ private ChildSessionStateMachine buildChildSession(ChildSessionParams childSessionParams) {
+ return new ChildSessionStateMachine(
+ mLooper.getLooper(),
+ mContext,
+ IKE_SESSION_UNIQUE_ID,
+ mMockAlarmManager,
+ createMockRandomFactory(),
+ mMockIpSecManager,
+ mIpSecSpiGenerator,
+ childSessionParams,
+ mSpyUserCbExecutor,
+ mMockChildSessionCallback,
+ mMockChildSessionSmCallback);
+ }
+
+ private ChildSaProposal buildSaProposalWithDhGroup(int dhGroup) {
+ return new ChildSaProposal.Builder()
+ .addEncryptionAlgorithm(
+ SaProposal.ENCRYPTION_ALGORITHM_AES_CBC, SaProposal.KEY_LEN_AES_128)
+ .addIntegrityAlgorithm(SaProposal.INTEGRITY_ALGORITHM_HMAC_SHA1_96)
+ .addDhGroup(dhGroup)
+ .build();
+ }
+
+ @Test
+ public void testRemoteRekeyWithKePayload() throws Exception {
+ // Use child session params with dh group to initiate the state machine
+ ChildSaProposal saProposal = buildSaProposalWithDhGroup(SaProposal.DH_GROUP_2048_BIT_MODP);
+ ChildSessionParams childSessionParams =
+ new TunnelModeChildSessionParams.Builder()
+ .addSaProposal(saProposal)
+ .addInternalAddressRequest(AF_INET)
+ .addInternalAddressRequest(INTERNAL_ADDRESS)
+ .build();
+ mChildSessionStateMachine = buildChildSession(childSessionParams);
+ mChildSessionStateMachine.setDbg(true);
+ mChildSessionStateMachine.start();
+
+ setupIdleStateMachine();
+
+ // Setup for new Child SA negotiation.
+ setUpSpiResource(LOCAL_ADDRESS, REMOTE_INIT_NEW_CHILD_SA_SPI_IN);
+ setUpSpiResource(REMOTE_ADDRESS, REMOTE_INIT_NEW_CHILD_SA_SPI_OUT);
+
+ IkeSaPayload saPayload =
+ IkeSaPayload.createChildSaRequestPayload(
+ new ChildSaProposal[] {saProposal}, mIpSecSpiGenerator, LOCAL_ADDRESS);
+ List<IkePayload> rekeyReqPayloads =
+ makeInboundRekeyChildPayloads(
+ REMOTE_INIT_NEW_CHILD_SA_SPI_OUT, saPayload, false /*isLocalInitRekey*/);
+
+ rekeyReqPayloads.add(
+ new IkeKePayload(IkeSaProposal.DH_GROUP_2048_BIT_MODP, createMockRandomFactory()));
+
+ when(mMockSaRecordHelper.makeChildSaRecord(
+ eq(rekeyReqPayloads), any(List.class), any(ChildSaRecordConfig.class)))
+ .thenReturn(mSpyRemoteInitNewChildSaRecord);
+
+ assertEquals(0, mChildSessionStateMachine.mSaProposal.getDhGroups().size());
+
+ // Receive rekey Child request
+ mChildSessionStateMachine.receiveRequest(
+ IKE_EXCHANGE_SUBTYPE_REKEY_CHILD, EXCHANGE_TYPE_CREATE_CHILD_SA, rekeyReqPayloads);
+ mLooper.dispatchAll();
+
+ assertTrue(
+ mChildSessionStateMachine.getCurrentState()
+ instanceof ChildSessionStateMachine.RekeyChildRemoteDelete);
+
+ verifyOutboundRekeyKePayload(true /*isResp*/);
+
+ assertEquals(1, mChildSessionStateMachine.mSaProposal.getDhGroups().size());
+ }
}
diff --git a/tests/iketests/src/java/com/android/internal/net/ipsec/ike/IkeSessionStateMachineTest.java b/tests/iketests/src/java/com/android/internal/net/ipsec/ike/IkeSessionStateMachineTest.java
index 8fb4ebba..6fe4aada 100644
--- a/tests/iketests/src/java/com/android/internal/net/ipsec/ike/IkeSessionStateMachineTest.java
+++ b/tests/iketests/src/java/com/android/internal/net/ipsec/ike/IkeSessionStateMachineTest.java
@@ -18,7 +18,9 @@ package com.android.internal.net.ipsec.ike;
import static android.net.ipsec.ike.IkeSessionConfiguration.EXTENSION_TYPE_FRAGMENTATION;
import static android.net.ipsec.ike.IkeSessionParams.IKE_OPTION_EAP_ONLY_AUTH;
+import static android.net.ipsec.ike.exceptions.IkeProtocolException.ERROR_TYPE_AUTHENTICATION_FAILED;
import static android.net.ipsec.ike.exceptions.IkeProtocolException.ERROR_TYPE_CHILD_SA_NOT_FOUND;
+import static android.net.ipsec.ike.exceptions.IkeProtocolException.ERROR_TYPE_INTERNAL_ADDRESS_FAILURE;
import static android.net.ipsec.ike.exceptions.IkeProtocolException.ERROR_TYPE_INVALID_SYNTAX;
import static android.net.ipsec.ike.exceptions.IkeProtocolException.ERROR_TYPE_NO_ADDITIONAL_SAS;
import static android.net.ipsec.ike.exceptions.IkeProtocolException.ERROR_TYPE_NO_PROPOSAL_CHOSEN;
@@ -28,7 +30,7 @@ import static android.system.OsConstants.AF_INET;
import static android.system.OsConstants.AF_INET6;
import static com.android.internal.net.TestUtils.createMockRandomFactory;
-import static com.android.internal.net.ipsec.ike.IkeSessionStateMachine.CMD_LOCAL_REQUEST_REKEY_IKE;
+import static com.android.internal.net.ipsec.ike.AbstractSessionStateMachine.RETRY_INTERVAL_MS;
import static com.android.internal.net.ipsec.ike.IkeSessionStateMachine.CMD_RECEIVE_IKE_PACKET;
import static com.android.internal.net.ipsec.ike.IkeSessionStateMachine.IKE_EXCHANGE_SUBTYPE_DELETE_CHILD;
import static com.android.internal.net.ipsec.ike.IkeSessionStateMachine.IKE_EXCHANGE_SUBTYPE_REKEY_CHILD;
@@ -67,6 +69,7 @@ import static org.mockito.Matchers.anyObject;
import static org.mockito.Matchers.anyString;
import static org.mockito.Matchers.argThat;
import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.doThrow;
@@ -641,10 +644,25 @@ public final class IkeSessionStateMachineTest extends IkeSessionTestBase {
new byte[KEY_LEN_IKE_ENCR],
TestUtils.hexStringToByteArray(PRF_KEY_INIT_HEX_STRING),
TestUtils.hexStringToByteArray(PRF_KEY_RESP_HEX_STRING),
- new IkeLocalRequest(CMD_LOCAL_REQUEST_REKEY_IKE),
mock(SaLifetimeAlarmScheduler.class));
}
+ private void mockScheduleRekey(SaLifetimeAlarmScheduler mockSaLifetimeAlarmScheduler) {
+ IkeLocalRequest rekeyReq =
+ new IkeLocalRequest(IkeSessionStateMachine.CMD_LOCAL_REQUEST_REKEY_IKE);
+ doAnswer(
+ (invocation) -> {
+ mIkeSessionStateMachine.sendMessageDelayed(
+ IkeSessionStateMachine.CMD_LOCAL_REQUEST_REKEY_IKE,
+ rekeyReq,
+ mIkeSessionStateMachine.mIkeSessionParams
+ .getSoftLifetimeMsInternal());
+ return null;
+ })
+ .when(mockSaLifetimeAlarmScheduler)
+ .scheduleLifetimeExpiryAlarm(anyString());
+ }
+
@Before
public void setUp() throws Exception {
super.setUp();
@@ -755,13 +773,11 @@ public final class IkeSessionStateMachineTest extends IkeSessionTestBase {
ikeSession.mLocalAddress = LOCAL_ADDRESS;
assertEquals(REMOTE_ADDRESS, ikeSession.mRemoteAddress);
- // Get socket instances used by the IkeSessionStateMachine by virtue of the caching.
- mSpyIkeUdp4Socket = spy(IkeUdp4Socket.getInstance(mMockDefaultNetwork, ikeSession));
- mSpyIkeUdp6Socket = spy(IkeUdp6Socket.getInstance(mMockDefaultNetwork, ikeSession));
- mSpyIkeUdpEncapSocket =
- spy(
- IkeUdpEncapSocket.getIkeUdpEncapSocket(
- mMockDefaultNetwork, mIpSecManager, ikeSession));
+ // Setup socket instances used by the IkeSessionStateMachine
+ // TODO: rename these from spy to mock.
+ mSpyIkeUdp4Socket = mock(IkeUdp4Socket.class);
+ mSpyIkeUdp6Socket = mock(IkeUdp6Socket.class);
+ mSpyIkeUdpEncapSocket = mock(IkeUdpEncapSocket.class);
doNothing().when(mSpyIkeUdp4Socket).sendIkePacket(any(), any());
doNothing().when(mSpyIkeUdp6Socket).sendIkePacket(any(), any());
@@ -1182,7 +1198,7 @@ public final class IkeSessionStateMachineTest extends IkeSessionTestBase {
mLooper.dispatchAll();
verify(mSpyCurrentIkeSocket).releaseReference(eq(mIkeSessionStateMachine));
- verify(mSpyCurrentIkeSocket).close();
+ verify(mMockBusyWakelock).release();
}
@Test
@@ -1216,6 +1232,9 @@ public final class IkeSessionStateMachineTest extends IkeSessionTestBase {
.thenAnswer(
(invocation) -> {
captureAndReleaseIkeSpiResource(invocation, 2);
+ mockScheduleRekey(mSpyCurrentIkeSaRecord.mSaLifetimeAlarmScheduler);
+ mSpyCurrentIkeSaRecord.mSaLifetimeAlarmScheduler
+ .scheduleLifetimeExpiryAlarm(anyString());
return mSpyCurrentIkeSaRecord;
});
}
@@ -1228,6 +1247,9 @@ public final class IkeSessionStateMachineTest extends IkeSessionTestBase {
.thenAnswer(
(invocation) -> {
captureAndReleaseIkeSpiResource(invocation, 4);
+ mockScheduleRekey(rekeySaRecord.mSaLifetimeAlarmScheduler);
+ rekeySaRecord.mSaLifetimeAlarmScheduler.scheduleLifetimeExpiryAlarm(
+ anyString());
return rekeySaRecord;
});
}
@@ -1268,7 +1290,7 @@ public final class IkeSessionStateMachineTest extends IkeSessionTestBase {
public void testEnableTestMode() throws Exception {
doReturn(true)
.when(mMockNetworkCapabilities)
- .hasCapability(RandomnessFactory.NETWORK_CAPABILITY_TRANSPORT_TEST);
+ .hasTransport(RandomnessFactory.TRANSPORT_TEST);
IkeSessionStateMachine ikeSession = makeAndStartIkeSession(buildIkeSessionParams());
@@ -1281,7 +1303,7 @@ public final class IkeSessionStateMachineTest extends IkeSessionTestBase {
public void testDisableTestMode() throws Exception {
doReturn(false)
.when(mMockNetworkCapabilities)
- .hasCapability(RandomnessFactory.NETWORK_CAPABILITY_TRANSPORT_TEST);
+ .hasTransport(RandomnessFactory.TRANSPORT_TEST);
IkeSessionStateMachine ikeSession = makeAndStartIkeSession(buildIkeSessionParams());
@@ -1331,9 +1353,6 @@ public final class IkeSessionStateMachineTest extends IkeSessionTestBase {
// Validate socket switched
assertTrue(mIkeSessionStateMachine.mIkeSocket instanceof IkeUdpEncapSocket);
verify(mSpyIkeUdp4Socket).unregisterIke(anyLong());
-
- // Validate keepalive has started
- verify(mMockSocketKeepalive).start(anyInt());
}
@Ignore
@@ -1403,7 +1422,7 @@ public final class IkeSessionStateMachineTest extends IkeSessionTestBase {
assertEquals(KEY_LEN_IKE_PRF, ikeSaRecordConfig.prf.getKeyLength());
assertEquals(KEY_LEN_IKE_INTE, ikeSaRecordConfig.integrityKeyLength);
assertEquals(KEY_LEN_IKE_ENCR, ikeSaRecordConfig.encryptionKeyLength);
- assertEquals(CMD_LOCAL_REQUEST_REKEY_IKE, ikeSaRecordConfig.futureRekeyEvent.procedureType);
+ assertNotNull(ikeSaRecordConfig.saLifetimeAlarmScheduler);
// Validate NAT detection
assertTrue(mIkeSessionStateMachine.mIsLocalBehindNat);
@@ -1441,6 +1460,8 @@ public final class IkeSessionStateMachineTest extends IkeSessionTestBase {
/** Initializes the mIkeSessionStateMachine in the IDLE state. */
private void setupIdleStateMachine() throws Exception {
+ verify(mMockBusyWakelock).acquire();
+
setIkeInitResults();
mIkeSessionStateMachine.sendMessage(
@@ -1453,6 +1474,11 @@ public final class IkeSessionStateMachineTest extends IkeSessionTestBase {
assertTrue(
mIkeSessionStateMachine.getCurrentState() instanceof IkeSessionStateMachine.Idle);
+
+ verify(mMockBusyWakelock).release();
+
+ // For convenience to verify wakelocks in all other places.
+ reset(mMockBusyWakelock);
}
private void mockIkeInitAndTransitionToIkeAuth(State authState) throws Exception {
@@ -1614,6 +1640,7 @@ public final class IkeSessionStateMachineTest extends IkeSessionTestBase {
mIkeSessionStateMachine.getCurrentState()
instanceof IkeSessionStateMachine.ChildProcedureOngoing);
verify(mMockChildSessionStateMachine).deleteChildSession();
+ verify(mMockBusyWakelock).acquire();
}
@Test
@@ -1648,6 +1675,7 @@ public final class IkeSessionStateMachineTest extends IkeSessionTestBase {
mIkeSessionStateMachine.getCurrentState()
instanceof IkeSessionStateMachine.ChildProcedureOngoing);
verify(mMockChildSessionStateMachine).rekeyChildSession();
+ verify(mMockBusyWakelock).acquire();
}
@Test
@@ -1787,6 +1815,7 @@ public final class IkeSessionStateMachineTest extends IkeSessionTestBase {
List<IkePayload> payloadList = verifyOutInfoMsgHeaderAndGetPayloads(true /*isResp*/);
assertEquals(1, payloadList.size());
assertEquals(outDelPayload, ((IkeDeletePayload) payloadList.get(0)));
+ verify(mMockBusyWakelock).acquire();
}
@Test
@@ -2501,6 +2530,48 @@ public final class IkeSessionStateMachineTest extends IkeSessionTestBase {
}
@Test
+ public void testAuthHandlesIkeErrorNotify() throws Exception {
+ mockIkeInitAndTransitionToIkeAuth(mIkeSessionStateMachine.mCreateIkeLocalIkeAuth);
+ verifyRetransmissionStarted();
+ resetMockIkeMessageHelper();
+
+ // Mock rejecting IKE AUTH with Authenticatio Failure notification
+ ReceivedIkePacket mockAuthFailPacket =
+ makeIkeAuthRespWithoutChildPayloads(
+ Arrays.asList(new IkeNotifyPayload(ERROR_TYPE_AUTHENTICATION_FAILED)));
+ mIkeSessionStateMachine.sendMessage(CMD_RECEIVE_IKE_PACKET, mockAuthFailPacket);
+ mLooper.dispatchAll();
+
+ // Verify IKE Session is closed properly
+ assertNull(mIkeSessionStateMachine.getCurrentState());
+ verify(mMockIkeSessionCallback)
+ .onClosedExceptionally(any(AuthenticationFailedException.class));
+ }
+
+ @Test
+ public void testAuthHandlesCreateChildErrorNotify() throws Exception {
+ mockIkeInitAndTransitionToIkeAuth(mIkeSessionStateMachine.mCreateIkeLocalIkeAuth);
+ verifyRetransmissionStarted();
+ resetMockIkeMessageHelper();
+
+ // Mock rejecting IKE AUTH with a Create Child error notification
+ ReceivedIkePacket mockAuthFailPacket =
+ makeIkeAuthRespWithoutChildPayloads(
+ Arrays.asList(new IkeNotifyPayload(ERROR_TYPE_INTERNAL_ADDRESS_FAILURE)));
+ mIkeSessionStateMachine.sendMessage(CMD_RECEIVE_IKE_PACKET, mockAuthFailPacket);
+ mLooper.dispatchAll();
+
+ // Verify IKE Session is closed properly
+ assertNull(mIkeSessionStateMachine.getCurrentState());
+
+ ArgumentCaptor<IkeProtocolException> captor =
+ ArgumentCaptor.forClass(IkeProtocolException.class);
+ verify(mMockIkeSessionCallback).onClosedExceptionally(captor.capture());
+ IkeProtocolException exception = captor.getValue();
+ assertEquals(ERROR_TYPE_INTERNAL_ADDRESS_FAILURE, exception.getErrorType());
+ }
+
+ @Test
public void testAuthPskHandleRespWithParsingError() throws Exception {
mockIkeInitAndTransitionToIkeAuth(mIkeSessionStateMachine.mCreateIkeLocalIkeAuth);
verifyRetransmissionStarted();
@@ -3077,17 +3148,10 @@ public final class IkeSessionStateMachineTest extends IkeSessionTestBase {
mIkeSessionStateMachine.sendMessage(CMD_RECEIVE_IKE_PACKET, resp);
mLooper.dispatchAll();
- // Verify IKE Session goes back to Idle
+ // Verify IKE Session goes back to Idle and retry is scheduled
+ verify(mSpyCurrentIkeSaRecord).rescheduleRekey(eq(RETRY_INTERVAL_MS));
assertTrue(
mIkeSessionStateMachine.getCurrentState() instanceof IkeSessionStateMachine.Idle);
-
- // Move time forward to trigger retry
- mLooper.moveTimeForward(IkeSessionStateMachine.RETRY_INTERVAL_MS);
- mLooper.dispatchAll();
-
- assertTrue(
- mIkeSessionStateMachine.getCurrentState()
- instanceof IkeSessionStateMachine.RekeyIkeLocalCreate);
}
@Test
@@ -3189,6 +3253,40 @@ public final class IkeSessionStateMachineTest extends IkeSessionTestBase {
instanceof IkeSessionStateMachine.RekeyIkeLocalCreate);
}
+ private void mockRescheduleRekey(IkeSaRecord spySaRecord) {
+ IkeLocalRequest rekeyReq =
+ new IkeLocalRequest(IkeSessionStateMachine.CMD_LOCAL_REQUEST_REKEY_IKE);
+ doAnswer(
+ (invocation) -> {
+ mIkeSessionStateMachine.sendMessageDelayed(
+ IkeSessionStateMachine.CMD_LOCAL_REQUEST_REKEY_IKE,
+ rekeyReq,
+ RETRY_INTERVAL_MS);
+ return null;
+ })
+ .when(spySaRecord)
+ .rescheduleRekey(eq(RETRY_INTERVAL_MS));
+ }
+
+ @Test
+ public void testRekeyIkeLocalCreateHandleRespWithTempFailure() throws Exception {
+ setupIdleStateMachine();
+
+ // Send Rekey-Create request
+ mIkeSessionStateMachine.sendMessage(
+ IkeSessionStateMachine.CMD_EXECUTE_LOCAL_REQ,
+ new IkeLocalRequest(IkeSessionStateMachine.CMD_LOCAL_REQUEST_REKEY_IKE));
+ mLooper.dispatchAll();
+
+ // Mock sending TEMPORARY_FAILURE response
+ mockRcvTempFail();
+ mLooper.dispatchAll();
+
+ verify(mSpyCurrentIkeSaRecord).rescheduleRekey(eq(RETRY_INTERVAL_MS));
+ assertTrue(
+ mIkeSessionStateMachine.getCurrentState() instanceof IkeSessionStateMachine.Idle);
+ }
+
private void mockCreateAndTransitionToRekeyDeleteLocal() {
// Seed fake rekey data and force transition to RekeyIkeLocalDelete
mIkeSessionStateMachine.mLocalInitNewIkeSaRecord = mSpyLocalInitIkeSaRecord;
@@ -4065,6 +4163,7 @@ public final class IkeSessionStateMachineTest extends IkeSessionTestBase {
// Verify state machine quit properly
assertNull(mIkeSessionStateMachine.getCurrentState());
+ verify(mMockBusyWakelock).release();
}
@Test
@@ -4392,24 +4491,6 @@ public final class IkeSessionStateMachineTest extends IkeSessionTestBase {
mLooper.dispatchAll();
}
- @Test
- public void testTempFailureHandlerScheduleRetry() throws Exception {
- mockSendRekeyChildReq();
-
- // Mock sending TEMPORARY_FAILURE response
- mockRcvTempFail();
-
- // Move time forward to trigger retry
- mLooper.moveTimeForward(IkeSessionStateMachine.RETRY_INTERVAL_MS);
- mLooper.dispatchAll();
-
- // Verify that rekey is triggered again
- assertTrue(
- mIkeSessionStateMachine.getCurrentState()
- instanceof IkeSessionStateMachine.ChildProcedureOngoing);
- verify(mMockChildSessionStateMachine, times(2)).rekeyChildSession();
- }
-
@Ignore
public void disableTestTempFailureHandlerTimeout() throws Exception {
long currentTime = 0;
@@ -4437,28 +4518,48 @@ public final class IkeSessionStateMachineTest extends IkeSessionTestBase {
@Test
public void testTempFailureHandlerCancelTimer() throws Exception {
- mockSendRekeyChildReq();
+ mockRescheduleRekey(mSpyCurrentIkeSaRecord);
+ setupIdleStateMachine();
+
+ // Send Rekey-Create request
+ mIkeSessionStateMachine.sendMessage(
+ IkeSessionStateMachine.CMD_EXECUTE_LOCAL_REQ,
+ new IkeLocalRequest(IkeSessionStateMachine.CMD_LOCAL_REQUEST_REKEY_IKE));
+ mLooper.dispatchAll();
+ verifyRetransmissionStarted();
// Mock sending TEMPORARY_FAILURE response
mockRcvTempFail();
+ mLooper.dispatchAll();
+ verify(mSpyCurrentIkeSaRecord).rescheduleRekey(eq(RETRY_INTERVAL_MS));
+ assertTrue(
+ mIkeSessionStateMachine.getCurrentState() instanceof IkeSessionStateMachine.Idle);
// Move time forward to trigger retry
mLooper.moveTimeForward(IkeSessionStateMachine.RETRY_INTERVAL_MS);
mLooper.dispatchAll();
- verify(mMockChildSessionStateMachine, times(2)).rekeyChildSession();
+ assertTrue(
+ mIkeSessionStateMachine.getCurrentState()
+ instanceof IkeSessionStateMachine.RekeyIkeLocalCreate);
- // Mock sending a valid response
- ReceivedIkePacket resp =
- makeDummyEncryptedReceivedIkePacketWithPayloadList(
- mSpyCurrentIkeSaRecord,
- EXCHANGE_TYPE_CREATE_CHILD_SA,
- true /*isResp*/,
- new LinkedList<>());
- mIkeSessionStateMachine.sendMessage(IkeSessionStateMachine.CMD_RECEIVE_IKE_PACKET, resp);
+ // Prepare "rekeyed" SA
+ setupRekeyedIkeSa(mSpyLocalInitIkeSaRecord);
+
+ // Receive valid Rekey-Create response
+ ReceivedIkePacket dummyRekeyIkeRespReceivedPacket = makeRekeyIkeResponse();
mIkeSessionStateMachine.sendMessage(
- IkeSessionStateMachine.CMD_FORCE_TRANSITION, mIkeSessionStateMachine.mIdle);
+ IkeSessionStateMachine.CMD_RECEIVE_IKE_PACKET, dummyRekeyIkeRespReceivedPacket);
mLooper.dispatchAll();
+ // Receive Delete response
+ ReceivedIkePacket dummyDeleteIkeRespReceivedPacket =
+ makeDeleteIkeResponse(mSpyCurrentIkeSaRecord);
+ mIkeSessionStateMachine.sendMessage(
+ IkeSessionStateMachine.CMD_RECEIVE_IKE_PACKET, dummyDeleteIkeRespReceivedPacket);
+ mLooper.dispatchAll();
+ assertTrue(
+ mIkeSessionStateMachine.getCurrentState() instanceof IkeSessionStateMachine.Idle);
+
// Move time forward
mLooper.moveTimeForward(IkeSessionStateMachine.TEMP_FAILURE_RETRY_TIMEOUT_MS);
mLooper.dispatchAll();
@@ -4466,8 +4567,6 @@ public final class IkeSessionStateMachineTest extends IkeSessionTestBase {
// Validate IKE Session is not closed
assertTrue(
mIkeSessionStateMachine.getCurrentState() instanceof IkeSessionStateMachine.Idle);
- // Validate no more retry
- verify(mMockChildSessionStateMachine, times(2)).rekeyChildSession();
}
@Ignore
diff --git a/tests/iketests/src/java/com/android/internal/net/ipsec/ike/IkeSessionTestBase.java b/tests/iketests/src/java/com/android/internal/net/ipsec/ike/IkeSessionTestBase.java
index 3728f368..e4e83794 100644
--- a/tests/iketests/src/java/com/android/internal/net/ipsec/ike/IkeSessionTestBase.java
+++ b/tests/iketests/src/java/com/android/internal/net/ipsec/ike/IkeSessionTestBase.java
@@ -18,8 +18,11 @@ package com.android.internal.net.ipsec.ike;
import static org.mockito.Matchers.any;
import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.anyInt;
+import static org.mockito.Mockito.anyString;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
@@ -33,6 +36,7 @@ import android.net.Network;
import android.net.NetworkCapabilities;
import android.net.SocketKeepalive;
import android.os.Handler;
+import android.os.PowerManager;
import com.android.internal.net.ipsec.ike.testutils.MockIpSecTestUtils;
import com.android.internal.net.ipsec.ike.utils.IkeAlarmReceiver;
@@ -50,9 +54,12 @@ public abstract class IkeSessionTestBase {
(Inet4Address) (InetAddresses.parseNumericAddress("127.0.0.1"));
protected static final String REMOTE_HOSTNAME = "ike.test.android.com";
+ protected PowerManager.WakeLock mMockBusyWakelock;
+
protected MockIpSecTestUtils mMockIpSecTestUtils;
protected Context mSpyContext;
protected IpSecManager mIpSecManager;
+ protected PowerManager mPowerManager;
protected ConnectivityManager mMockConnectManager;
protected Network mMockDefaultNetwork;
@@ -74,6 +81,11 @@ public abstract class IkeSessionTestBase {
any(Handler.class));
doNothing().when(mSpyContext).unregisterReceiver(any(IkeAlarmReceiver.class));
+ mPowerManager = mock(PowerManager.class);
+ mMockBusyWakelock = mock(PowerManager.WakeLock.class);
+ doReturn(mPowerManager).when(mSpyContext).getSystemService(eq(PowerManager.class));
+ doReturn(mMockBusyWakelock).when(mPowerManager).newWakeLock(anyInt(), anyString());
+
mMockConnectManager = mock(ConnectivityManager.class);
mMockDefaultNetwork = mock(Network.class);
doReturn(mMockDefaultNetwork).when(mMockConnectManager).getActiveNetwork();
@@ -102,6 +114,6 @@ public abstract class IkeSessionTestBase {
.getNetworkCapabilities(any(Network.class));
doReturn(false)
.when(mMockNetworkCapabilities)
- .hasCapability(RandomnessFactory.NETWORK_CAPABILITY_TRANSPORT_TEST);
+ .hasTransport(RandomnessFactory.TRANSPORT_TEST);
}
}
diff --git a/tests/iketests/src/java/com/android/internal/net/ipsec/ike/IkeUdpEncapSocketTest.java b/tests/iketests/src/java/com/android/internal/net/ipsec/ike/IkeUdpEncapSocketTest.java
index 8c0bf4d5..439fa27e 100644
--- a/tests/iketests/src/java/com/android/internal/net/ipsec/ike/IkeUdpEncapSocketTest.java
+++ b/tests/iketests/src/java/com/android/internal/net/ipsec/ike/IkeUdpEncapSocketTest.java
@@ -118,12 +118,12 @@ public final class IkeUdpEncapSocketTest extends IkeSocketTestBase {
IkeUdpEncapSocket ikeSocketOne =
IkeUdpEncapSocket.getIkeUdpEncapSocket(
- mMockNetwork, mSpyIpSecManager, mockIkeSessionOne);
+ mMockNetwork, mSpyIpSecManager, mockIkeSessionOne, Looper.myLooper());
assertEquals(1, ikeSocketOne.mAliveIkeSessions.size());
IkeUdpEncapSocket ikeSocketTwo =
IkeUdpEncapSocket.getIkeUdpEncapSocket(
- mMockNetwork, mSpyIpSecManager, mockIkeSessionTwo);
+ mMockNetwork, mSpyIpSecManager, mockIkeSessionTwo, Looper.myLooper());
assertEquals(2, ikeSocketTwo.mAliveIkeSessions.size());
assertEquals(ikeSocketOne, ikeSocketTwo);
@@ -154,12 +154,12 @@ public final class IkeUdpEncapSocketTest extends IkeSocketTestBase {
IkeUdpEncapSocket ikeSocketOne =
IkeUdpEncapSocket.getIkeUdpEncapSocket(
- mockNetworkOne, mSpyIpSecManager, mockIkeSessionOne);
+ mockNetworkOne, mSpyIpSecManager, mockIkeSessionOne, Looper.myLooper());
assertEquals(1, ikeSocketOne.mAliveIkeSessions.size());
IkeUdpEncapSocket ikeSocketTwo =
IkeUdpEncapSocket.getIkeUdpEncapSocket(
- mockNetworkTwo, mSpyIpSecManager, mockIkeSessionTwo);
+ mockNetworkTwo, mSpyIpSecManager, mockIkeSessionTwo, Looper.myLooper());
assertEquals(1, ikeSocketTwo.mAliveIkeSessions.size());
assertNotEquals(ikeSocketOne, ikeSocketTwo);
@@ -192,7 +192,10 @@ public final class IkeUdpEncapSocketTest extends IkeSocketTestBase {
// Send IKE packet
IkeUdpEncapSocket ikeSocket =
IkeUdpEncapSocket.getIkeUdpEncapSocket(
- mMockNetwork, mSpyIpSecManager, mMockIkeSessionStateMachine);
+ mMockNetwork,
+ mSpyIpSecManager,
+ mMockIkeSessionStateMachine,
+ Looper.myLooper());
ikeSocket.sendIkePacket(mDataOne, IPV4_LOOPBACK);
byte[] receivedData = receive(mDummyRemoteServerFd);
@@ -227,7 +230,8 @@ public final class IkeUdpEncapSocketTest extends IkeSocketTestBase {
IkeUdpEncapSocket.getIkeUdpEncapSocket(
mMockNetwork,
mSpyIpSecManager,
- mMockIkeSessionStateMachine));
+ mMockIkeSessionStateMachine,
+ mIkeThread.getLooper()));
createLatch.countDown();
Log.d("IkeUdpEncapSocketTest", "IkeUdpEncapSocket created.");
} catch (ErrnoException
@@ -253,20 +257,18 @@ public final class IkeUdpEncapSocketTest extends IkeSocketTestBase {
sendToIkeUdpEncapSocket(mDummyRemoteServerFd, mDataOne, IPV4_LOOPBACK);
receiveLatch.await();
- assertEquals(1, ikeSocket.numPacketsReceived());
assertArrayEquals(mDataOne, packetReceiver.mReceivedData);
// Send second packet.
sendToIkeUdpEncapSocket(mDummyRemoteServerFd, mDataTwo, IPV4_LOOPBACK);
receiveLatch.await();
- assertEquals(2, ikeSocket.numPacketsReceived());
assertArrayEquals(mDataTwo, packetReceiver.mReceivedData);
// Close IkeUdpEncapSocket.
TestCountDownLatch closeLatch = new TestCountDownLatch();
- ikeSocket
- .getHandler()
+ mIkeThread
+ .getThreadHandler()
.post(
() -> {
ikeSocket.releaseReference(mMockIkeSessionStateMachine);
diff --git a/tests/iketests/src/java/com/android/internal/net/ipsec/ike/SaRecordTest.java b/tests/iketests/src/java/com/android/internal/net/ipsec/ike/SaRecordTest.java
index 644ba066..b193099d 100644
--- a/tests/iketests/src/java/com/android/internal/net/ipsec/ike/SaRecordTest.java
+++ b/tests/iketests/src/java/com/android/internal/net/ipsec/ike/SaRecordTest.java
@@ -24,6 +24,7 @@ import static org.junit.Assert.assertTrue;
import static org.mockito.AdditionalMatchers.aryEq;
import static org.mockito.Matchers.anyInt;
import static org.mockito.Matchers.anyObject;
+import static org.mockito.Matchers.anyString;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
@@ -38,8 +39,6 @@ import android.net.IpSecTransform;
import android.net.ipsec.ike.SaProposal;
import com.android.internal.net.TestUtils;
-import com.android.internal.net.ipsec.ike.IkeLocalRequestScheduler.ChildLocalRequest;
-import com.android.internal.net.ipsec.ike.IkeLocalRequestScheduler.LocalRequest;
import com.android.internal.net.ipsec.ike.SaRecord.ChildSaRecord;
import com.android.internal.net.ipsec.ike.SaRecord.ChildSaRecordConfig;
import com.android.internal.net.ipsec.ike.SaRecord.IIpSecTransformHelper;
@@ -148,9 +147,6 @@ public final class SaRecordTest {
private IkeMacIntegrity mHmacSha1IntegrityMac;
private IkeCipher mAesCbcCipher;
- private LocalRequest mMockFutureRekeyIkeEvent;
- private ChildLocalRequest mMockFutureRekeyChildEvent;
-
private SaLifetimeAlarmScheduler mMockLifetimeAlarmScheduler;
private SaRecordHelper mSaRecordHelper = new SaRecordHelper();
@@ -168,8 +164,6 @@ public final class SaRecordTest {
SaProposal.ENCRYPTION_ALGORITHM_AES_CBC,
SaProposal.KEY_LEN_AES_128));
- mMockFutureRekeyIkeEvent = mock(LocalRequest.class);
- mMockFutureRekeyChildEvent = mock(ChildLocalRequest.class);
mMockLifetimeAlarmScheduler = mock(SaLifetimeAlarmScheduler.class);
}
@@ -192,8 +186,7 @@ public final class SaRecordTest {
IKE_AUTH_ALGO_KEY_LEN,
IKE_ENCR_ALGO_KEY_LEN,
true /*isLocalInit*/,
- mMockFutureRekeyIkeEvent,
- mock(SaLifetimeAlarmScheduler.class));
+ mMockLifetimeAlarmScheduler);
int keyMaterialLen =
IKE_SK_D_KEY_LEN
@@ -225,10 +218,10 @@ public final class SaRecordTest {
TestUtils.hexStringToByteArray(IKE_SK_PRF_INIT_HEX_STRING), ikeSaRecord.getSkPi());
assertArrayEquals(
TestUtils.hexStringToByteArray(IKE_SK_PRF_RESP_HEX_STRING), ikeSaRecord.getSkPr());
+ verify(mMockLifetimeAlarmScheduler).scheduleLifetimeExpiryAlarm(anyString());
ikeSaRecord.close();
-
- verify(mMockFutureRekeyIkeEvent).cancel();
+ verify(mMockLifetimeAlarmScheduler).cancelLifetimeExpiryAlarm(anyString());
}
// Test generating keying material and building IpSecTransform for making Child SA.
@@ -306,7 +299,6 @@ public final class SaRecordTest {
TestUtils.hexStringToByteArray(IKE_SK_D_HEX_STRING),
false /*isTransport*/,
true /*isLocalInit*/,
- mMockFutureRekeyChildEvent,
mMockLifetimeAlarmScheduler);
ChildSaRecord childSaRecord =
@@ -332,8 +324,10 @@ public final class SaRecordTest {
TestUtils.hexStringToByteArray(FIRST_CHILD_ENCR_RESP_HEX_STRING),
childSaRecord.getInboundDecryptionKey());
+ verify(mMockLifetimeAlarmScheduler).scheduleLifetimeExpiryAlarm(anyString());
+
childSaRecord.close();
- verify(mMockFutureRekeyChildEvent).cancel();
+ verify(mMockLifetimeAlarmScheduler).cancelLifetimeExpiryAlarm(anyString());
SaRecord.setIpSecTransformHelper(new IpSecTransformHelper());
}
diff --git a/tests/iketests/src/java/com/android/internal/net/ipsec/ike/message/IkeCertReqPayloadTest.java b/tests/iketests/src/java/com/android/internal/net/ipsec/ike/message/IkeCertReqPayloadTest.java
new file mode 100644
index 00000000..f859a822
--- /dev/null
+++ b/tests/iketests/src/java/com/android/internal/net/ipsec/ike/message/IkeCertReqPayloadTest.java
@@ -0,0 +1,71 @@
+/*
+ * 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 com.android.internal.net.ipsec.ike.message;
+
+import static com.android.internal.net.ipsec.ike.message.IkeCertPayload.CERTIFICATE_ENCODING_X509_CERT_SIGNATURE;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+
+import android.util.Pair;
+
+import com.android.internal.net.TestUtils;
+
+import org.junit.Test;
+
+import java.nio.ByteBuffer;
+
+public class IkeCertReqPayloadTest {
+ private static final int CERT_ENCODING_TYPE = CERTIFICATE_ENCODING_X509_CERT_SIGNATURE;
+ private static final int NEXT_PAYLOAD_TYPE = IkePayload.PAYLOAD_TYPE_AUTH;
+ private static final byte[] CERT_REQ_PAYLOAD =
+ TestUtils.hexStringToByteArray("27000019040d0a12bb1f98996563f15b10db95c67eea7990fa");
+ private static final byte[] CA_SUBJECT_PUBLIC_KEY_INFO_HASH =
+ TestUtils.hexStringToByteArray("0d0a12bb1f98996563f15b10db95c67eea7990fa");
+
+ @Test
+ public void testDecode() throws Exception {
+ Pair<IkePayload, Integer> pair =
+ IkePayloadFactory.getIkePayload(
+ IkePayload.PAYLOAD_TYPE_CERT_REQUEST,
+ false /*isResp*/,
+ ByteBuffer.wrap(CERT_REQ_PAYLOAD));
+
+ IkeCertReqPayload certPayload = (IkeCertReqPayload) pair.first;
+ assertEquals(CERT_ENCODING_TYPE, certPayload.certEncodingType);
+ assertArrayEquals(
+ CA_SUBJECT_PUBLIC_KEY_INFO_HASH, certPayload.caSubjectPublicKeyInforHashes);
+
+ assertEquals(NEXT_PAYLOAD_TYPE, (int) pair.second);
+ }
+
+ @Test
+ public void testEncode() throws Exception {
+ Pair<IkePayload, Integer> pair =
+ IkePayloadFactory.getIkePayload(
+ IkePayload.PAYLOAD_TYPE_CERT_REQUEST,
+ false /*isResp*/,
+ ByteBuffer.wrap(CERT_REQ_PAYLOAD));
+ IkeCertReqPayload certPayload = (IkeCertReqPayload) pair.first;
+
+ ByteBuffer byteBuffer = ByteBuffer.allocate(CERT_REQ_PAYLOAD.length);
+ certPayload.encodeToByteBuffer(NEXT_PAYLOAD_TYPE, byteBuffer);
+ assertArrayEquals(CERT_REQ_PAYLOAD, byteBuffer.array());
+
+ assertEquals(CERT_REQ_PAYLOAD.length, certPayload.getPayloadLength());
+ }
+}
diff --git a/tests/iketests/src/java/com/android/internal/net/ipsec/ike/message/IkeNotifyPayloadTest.java b/tests/iketests/src/java/com/android/internal/net/ipsec/ike/message/IkeNotifyPayloadTest.java
index 884c76eb..0b3c3583 100644
--- a/tests/iketests/src/java/com/android/internal/net/ipsec/ike/message/IkeNotifyPayloadTest.java
+++ b/tests/iketests/src/java/com/android/internal/net/ipsec/ike/message/IkeNotifyPayloadTest.java
@@ -85,19 +85,6 @@ public final class IkeNotifyPayloadTest {
}
@Test
- public void testDecodeNotifyPayloadThrowException() throws Exception {
- byte[] inputPacket =
- TestUtils.hexStringToByteArray(NOTIFY_NAT_DETECTION_PAYLOAD_BODY_HEX_STRING);
- // Change Protocol ID to ESP
- inputPacket[PROTOCOL_ID_OFFSET] = (byte) (IkePayload.PROTOCOL_ID_ESP & 0xFF);
- try {
- IkeNotifyPayload payload = new IkeNotifyPayload(false, inputPacket);
- fail("Expected InvalidSyntaxException: Protocol ID should not be ESP");
- } catch (InvalidSyntaxException expected) {
- }
- }
-
- @Test
public void testGenerateNatDetectionData() throws Exception {
long initiatorIkeSpi = Long.parseLong(IKE_INITIATOR_SPI_HEX_STRING, 16);
long responderIkespi = Long.parseLong(IKE_RESPODNER_SPI_HEX_STRING, 16);