Defer incoming requests in IKE_AUTH states

This change defers incoming IKE requests during authentication. This
could be a result of network packets racing, and is perfectly valid. As
such, they should be deferred to the next (Idle) state.

Bug: 139482382
Test: FrameworksIkeTests passing
Change-Id: Ia9ed9c60fde32b0a0af1ba0225772b4d632cfea7
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 2e5e8f5..60d18b9 100644
--- a/src/java/com/android/internal/net/ipsec/ike/IkeSessionStateMachine.java
+++ b/src/java/com/android/internal/net/ipsec/ike/IkeSessionStateMachine.java
@@ -3048,6 +3048,19 @@
         // TODO: b/139482382 If receiving a remote request while waiting for the last IKE AUTH
         // response, defer it to next state.
 
+        @Override
+        protected void handleRequestIkeMessage(
+                IkeMessage ikeMessage, int ikeExchangeSubType, Message message) {
+            IkeSaRecord ikeSaRecord = getIkeSaRecordForPacket(ikeMessage.ikeHeader);
+
+            // Null out last received packet, so the next state (that handles the actual request)
+            // does not treat the message as a retransmission.
+            ikeSaRecord.updateLastReceivedReqFirstPacket(null);
+
+            // Send to next state; we can't handle this yet.
+            deferMessage(message);
+        }
+
         protected IkeMessage buildIkeAuthReqMessage(List<IkePayload> payloadList) {
             // Build IKE header
             IkeHeader ikeHeader =
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 73b6c7e..e382328 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
@@ -169,6 +169,7 @@
 import java.security.PrivateKey;
 import java.security.cert.X509Certificate;
 import java.util.Arrays;
+import java.util.Collections;
 import java.util.LinkedList;
 import java.util.List;
 import java.util.concurrent.Executor;
@@ -2317,6 +2318,61 @@
                         IkePayload.PAYLOAD_TYPE_ID_RESPONDER, true /*isResp*/, idRespPayloadHex);
     }
 
+    private void verifyEmptyInformationalSent(int count) {
+        verify(mMockIkeMessageHelper, times(count))
+                .encryptAndEncode(
+                        anyObject(),
+                        anyObject(),
+                        eq(mSpyCurrentIkeSaRecord),
+                        argThat(
+                                msg -> {
+                                    return msg.ikePayloadList.isEmpty()
+                                            && msg.ikeHeader.isResponseMsg
+                                            && msg.ikeHeader.fromIkeInitiator
+                                            && msg.ikeHeader.exchangeType
+                                                    == IkeHeader.EXCHANGE_TYPE_INFORMATIONAL;
+                                }),
+                        anyBoolean(),
+                        anyInt());
+    }
+
+    @Test
+    public void testCreateIkeLocalIkeAuthDefersOtherMessages() throws Exception {
+        mockIkeInitAndTransitionToIkeAuth(mIkeSessionStateMachine.mCreateIkeLocalIkeAuth);
+        verifyRetransmissionStarted();
+
+        // Build IKE AUTH response with Auth-PSK, ID-Responder and config payloads.
+        List<IkePayload> authRelatedPayloads = new LinkedList<>();
+        IkeAuthPskPayload spyAuthPayload = makeSpyRespPskPayload();
+        authRelatedPayloads.add(spyAuthPayload);
+
+        ReceivedIkePacket req =
+                makeDummyEncryptedReceivedIkePacket(
+                        mSpyCurrentIkeSaRecord,
+                        IkeHeader.EXCHANGE_TYPE_INFORMATIONAL,
+                        false,
+                        Collections.emptyList(),
+                        Collections.emptyList());
+        mIkeSessionStateMachine.sendMessage(IkeSessionStateMachine.CMD_RECEIVE_IKE_PACKET, req);
+
+        verifyEmptyInformationalSent(0);
+
+        // Send IKE AUTH response to IKE state machine to trigger moving to next state
+        IkeIdPayload respIdPayload = makeRespIdPayload();
+        authRelatedPayloads.add(respIdPayload);
+        authRelatedPayloads.add(makeConfigPayload());
+
+        ReceivedIkePacket authResp = makeIkeAuthRespWithChildPayloads(authRelatedPayloads);
+        mIkeSessionStateMachine.sendMessage(
+                IkeSessionStateMachine.CMD_RECEIVE_IKE_PACKET, authResp);
+        mLooper.dispatchAll();
+
+        verifyEmptyInformationalSent(1);
+        assertTrue(
+                mIkeSessionStateMachine.getCurrentState()
+                        instanceof IkeSessionStateMachine.ChildProcedureOngoing);
+    }
+
     @Test
     public void testCreateIkeLocalIkeAuthDigitalSignature() throws Exception {
         // Quit and restart IKE Session with Digital Signature Auth params