aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorevitayan <evitayan@google.com>2019-04-02 11:11:55 -0700
committerandroid-build-merger <android-build-merger@google.com>2019-04-02 11:11:55 -0700
commit66d31fa04b4965b2f32fd1d088f7efb6881213e0 (patch)
treee19f89855c004da39d9e52cb9cc5425b362d7a39
parentb64adac3271cfbd37584d72aa41e323b6d08b3c9 (diff)
parentc85025ad953942935afb66f1c361ff3166cb105c (diff)
downloadike-66d31fa04b4965b2f32fd1d088f7efb6881213e0.tar.gz
Receive and demultiplex IKE packet am: d8375f932a
am: c85025ad95 Change-Id: I29e2c3063ca5caf3bf49a455bbc5a9da7f373d2a
-rw-r--r--src/java/com/android/ike/ikev2/IkeSessionStateMachine.java20
-rw-r--r--src/java/com/android/ike/ikev2/IkeSocket.java51
-rw-r--r--tests/iketests/src/java/com/android/ike/ikev2/IkeSessionStateMachineTest.java19
-rw-r--r--tests/iketests/src/java/com/android/ike/ikev2/IkeSocketTest.java97
4 files changed, 169 insertions, 18 deletions
diff --git a/src/java/com/android/ike/ikev2/IkeSessionStateMachine.java b/src/java/com/android/ike/ikev2/IkeSessionStateMachine.java
index c887ecb0..31d15e5e 100644
--- a/src/java/com/android/ike/ikev2/IkeSessionStateMachine.java
+++ b/src/java/com/android/ike/ikev2/IkeSessionStateMachine.java
@@ -18,7 +18,6 @@ package com.android.ike.ikev2;
import android.os.Looper;
import android.os.Message;
import android.util.LongSparseArray;
-import android.util.Pair;
import android.util.SparseArray;
import com.android.ike.ikev2.SaRecord.IkeSaRecord;
@@ -214,6 +213,19 @@ public class IkeSessionStateMachine extends StateMachine {
}
/**
+ * Receive IKE packet from remote server.
+ *
+ * <p>This method is called synchronously from IkeSocket. It proxies the synchronous call as an
+ * asynchronous job to the IkeSessionStateMachine handler.
+ *
+ * @param ikeHeader the decoded IKE header.
+ * @param ikePacketBytes the byte array of the entire received IKE packet.
+ */
+ public void receiveIkePacket(IkeHeader ikeHeader, byte[] ikePacketBytes) {
+ sendMessage(CMD_RECEIVE_IKE_PACKET, new ReceivedIkePacket(ikeHeader, ikePacketBytes));
+ }
+
+ /**
* ReceivedIkePacket is a package private data container consists of decoded IkeHeader and
* encoded IKE packet in a byte array.
*/
@@ -223,9 +235,9 @@ public class IkeSessionStateMachine extends StateMachine {
/** Entire encoded IKE message including IKE header */
public final byte[] ikePacketBytes;
- ReceivedIkePacket(Pair<IkeHeader, byte[]> ikePacketPair) {
- ikeHeader = ikePacketPair.first;
- ikePacketBytes = ikePacketPair.second;
+ ReceivedIkePacket(IkeHeader ikeHeader, byte[] ikePacketBytes) {
+ this.ikeHeader = ikeHeader;
+ this.ikePacketBytes = ikePacketBytes;
}
}
diff --git a/src/java/com/android/ike/ikev2/IkeSocket.java b/src/java/com/android/ike/ikev2/IkeSocket.java
index 71daddd2..0f1b9e65 100644
--- a/src/java/com/android/ike/ikev2/IkeSocket.java
+++ b/src/java/com/android/ike/ikev2/IkeSocket.java
@@ -25,8 +25,11 @@ import android.net.util.PacketReader;
import android.os.Handler;
import android.system.ErrnoException;
import android.system.Os;
+import android.util.Log;
import android.util.LongSparseArray;
+import com.android.ike.ikev2.exceptions.IkeException;
+import com.android.ike.ikev2.message.IkeHeader;
import com.android.internal.annotations.VisibleForTesting;
import java.io.FileDescriptor;
@@ -60,11 +63,15 @@ import java.util.Map;
* thread, there will not be concurrent modification problems.
*/
public final class IkeSocket extends PacketReader implements AutoCloseable {
+ private static final String TAG = "IkeSocket";
+
// TODO: b/129358324 Consider supporting IKE exchange without UDP Encapsulation.
// UDP-encapsulated IKE packets MUST be sent to 4500.
@VisibleForTesting static final int IKE_SERVER_PORT = 4500;
+
// A Non-ESP marker helps the recipient to distinguish IKE packets from ESP packets.
@VisibleForTesting static final int NON_ESP_MARKER_LEN = 4;
+ @VisibleForTesting static final byte[] NON_ESP_MARKER = new byte[NON_ESP_MARKER_LEN];
// Package private map from UdpEncapsulationSocket to IkeSocket instances.
static Map<UdpEncapsulationSocket, IkeSocket> sFdToIkeSocketMap = new HashMap<>();
@@ -138,10 +145,50 @@ public final class IkeSocket extends PacketReader implements AutoCloseable {
}
/** Package private */
+ @VisibleForTesting
static final class PacketReceiver implements IPacketReceiver {
public void handlePacket(
byte[] recvbuf, LongSparseArray<IkeSessionStateMachine> spiToIkeSession) {
- // TODO: Decode IKE header and demultiplex IKE packet
+ // TODO: b/129708574 Consider only logging the error some % of the time it happens, or
+ // only logging the error the first time it happens and then keep a count to prevent
+ // logspam.
+
+ ByteBuffer byteBuffer = ByteBuffer.wrap(recvbuf);
+
+ // Check the existence of the Non-ESP Marker. A received packet can be either an IKE
+ // packet starts with 4 zero-valued bytes Non-ESP Marker or an ESP packet starts with 4
+ // bytes ESP SPI. ESP SPI value can never be zero.
+ byte[] espMarker = new byte[NON_ESP_MARKER_LEN];
+ byteBuffer.get(espMarker);
+ if (!Arrays.equals(NON_ESP_MARKER, espMarker)) {
+ // Drop the received ESP packet.
+ Log.e(TAG, "Receive an ESP packet.");
+ return;
+ }
+
+ try {
+ // Re-direct IKE packet to IkeSessionStateMachine according to the locally generated
+ // IKE SPI.
+ byte[] ikePacketBytes = new byte[byteBuffer.remaining()];
+ byteBuffer.get(ikePacketBytes);
+ IkeHeader ikeHeader = new IkeHeader(ikePacketBytes);
+
+ long localGeneratedSpi =
+ ikeHeader.fromIkeInitiator
+ ? ikeHeader.ikeResponderSpi
+ : ikeHeader.ikeInitiatorSpi;
+
+ IkeSessionStateMachine ikeStateMachine = spiToIkeSession.get(localGeneratedSpi);
+ if (ikeStateMachine == null) {
+ Log.e(TAG, "Unrecognized IKE SPI.");
+ // TODO: Handle invalid IKE SPI error
+ } else {
+ ikeStateMachine.receiveIkePacket(ikeHeader, ikePacketBytes);
+ }
+ } catch (IkeException e) {
+ // Handle invalid IKE header
+ Log.e(TAG, "Can't parse malformed IKE packet header.");
+ }
}
}
@@ -171,7 +218,7 @@ public final class IkeSocket extends PacketReader implements AutoCloseable {
ByteBuffer buffer = ByteBuffer.allocate(NON_ESP_MARKER_LEN + ikePacket.length);
// Build outbound UDP Encapsulation packet body for sending IKE message.
- buffer.put(new byte[NON_ESP_MARKER_LEN]).put(ikePacket);
+ buffer.put(NON_ESP_MARKER).put(ikePacket);
buffer.rewind();
// Use unconnected UDP socket because one {@UdpEncapsulationSocket} may be shared by
diff --git a/tests/iketests/src/java/com/android/ike/ikev2/IkeSessionStateMachineTest.java b/tests/iketests/src/java/com/android/ike/ikev2/IkeSessionStateMachineTest.java
index 02aaa4c3..e3ad450a 100644
--- a/tests/iketests/src/java/com/android/ike/ikev2/IkeSessionStateMachineTest.java
+++ b/tests/iketests/src/java/com/android/ike/ikev2/IkeSessionStateMachineTest.java
@@ -27,7 +27,6 @@ import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.os.test.TestLooper;
-import android.util.Pair;
import com.android.ike.ikev2.ChildSessionStateMachineFactory.ChildSessionFactoryHelper;
import com.android.ike.ikev2.ChildSessionStateMachineFactory.IChildSessionFactoryHelper;
@@ -67,12 +66,12 @@ public final class IkeSessionStateMachineTest {
private ReceivedIkePacket makeDummyUnencryptedReceivedIkePacket(int packetType)
throws Exception {
IkeMessage dummyIkeMessage = makeDummyIkeMessageForTest(0, 0, false, false);
- Pair<IkeHeader, byte[]> dummyIkePacketPair =
- new Pair<>(dummyIkeMessage.ikeHeader, new byte[0]);
- when(mMockIkeMessageHelper.decode(dummyIkePacketPair.first, dummyIkePacketPair.second))
+ byte[] dummyIkePacketBytes = new byte[0];
+
+ when(mMockIkeMessageHelper.decode(dummyIkeMessage.ikeHeader, dummyIkePacketBytes))
.thenReturn(dummyIkeMessage);
when(mMockIkeMessageHelper.getMessageType(dummyIkeMessage)).thenReturn(packetType);
- return new ReceivedIkePacket(dummyIkePacketPair);
+ return new ReceivedIkePacket(dummyIkeMessage.ikeHeader, dummyIkePacketBytes);
}
private ReceivedIkePacket makeDummyEncryptedReceivedIkePacket(
@@ -81,16 +80,16 @@ public final class IkeSessionStateMachineTest {
IkeMessage dummyIkeMessage =
makeDummyIkeMessageForTest(
ikeSaRecord.initiatorSpi, ikeSaRecord.responderSpi, fromIkeInit, true);
- Pair<IkeHeader, byte[]> dummyIkePacketPair =
- new Pair<>(dummyIkeMessage.ikeHeader, new byte[0]);
+ byte[] dummyIkePacketBytes = new byte[0];
+
when(mMockIkeMessageHelper.decode(
mMockIkeSessionOptions,
ikeSaRecord,
- dummyIkePacketPair.first,
- dummyIkePacketPair.second))
+ dummyIkeMessage.ikeHeader,
+ dummyIkePacketBytes))
.thenReturn(dummyIkeMessage);
when(mMockIkeMessageHelper.getMessageType(dummyIkeMessage)).thenReturn(packetType);
- return new ReceivedIkePacket(dummyIkePacketPair);
+ return new ReceivedIkePacket(dummyIkeMessage.ikeHeader, dummyIkePacketBytes);
}
private IkeMessage makeDummyIkeMessageForTest(
diff --git a/tests/iketests/src/java/com/android/ike/ikev2/IkeSocketTest.java b/tests/iketests/src/java/com/android/ike/ikev2/IkeSocketTest.java
index 2dd44096..5f15f554 100644
--- a/tests/iketests/src/java/com/android/ike/ikev2/IkeSocketTest.java
+++ b/tests/iketests/src/java/com/android/ike/ikev2/IkeSocketTest.java
@@ -20,6 +20,11 @@ import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.fail;
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
import android.content.Context;
import android.net.IpSecManager;
@@ -34,9 +39,14 @@ import android.util.LongSparseArray;
import androidx.test.InstrumentationRegistry;
+import com.android.ike.ikev2.IkeSocket.PacketReceiver;
+import com.android.ike.ikev2.message.IkeHeader;
+import com.android.ike.ikev2.message.TestUtils;
+
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
+import org.mockito.ArgumentCaptor;
import java.io.FileDescriptor;
import java.net.InetAddress;
@@ -50,6 +60,26 @@ public final class IkeSocketTest {
private static final int REMOTE_RECV_BUFF_SIZE = 2048;
private static final int TIMEOUT = 1000;
+ private static final String NON_ESP_MARKER_HEX_STRING = "00000000";
+ private static final String IKE_REQ_MESSAGE_HEX_STRING =
+ "5f54bf6d8b48e6e100000000000000002120220800000000"
+ + "00000150220000300000002c010100040300000c0100000c"
+ + "800e00800300000803000002030000080400000200000008"
+ + "020000022800008800020000b4a2faf4bb54878ae21d6385"
+ + "12ece55d9236fc5046ab6cef82220f421f3ce6361faf3656"
+ + "4ecb6d28798a94aad7b2b4b603ddeaaa5630adb9ece8ac37"
+ + "534036040610ebdd92f46bef84f0be7db860351843858f8a"
+ + "cf87056e272377f70c9f2d81e29c7b0ce4f291a3a72476bb"
+ + "0b278fd4b7b0a4c26bbeb08214c707137607958729000024"
+ + "c39b7f368f4681b89fa9b7be6465abd7c5f68b6ed5d3b4c7"
+ + "2cb4240eb5c464122900001c00004004e54f73b7d83f6beb"
+ + "881eab2051d8663f421d10b02b00001c00004005d915368c"
+ + "a036004cb578ae3e3fb268509aeab1900000002069936922"
+ + "8741c6d4ca094c93e242c9de19e7b7c60000000500000500";
+
+ private static final String LOCAL_SPI = "0000000000000000";
+ private static final String REMOTE_SPI = "5f54bf6d8b48e6e1";
+
private static final String DATA_ONE = "one 1";
private static final String DATA_TWO = "two 2";
@@ -58,10 +88,18 @@ public final class IkeSocketTest {
private byte[] mDataOne;
private byte[] mDataTwo;
+ private long mLocalSpi;
+ private long mRemoteSpi;
+
+ private LongSparseArray mSpiToIkeStateMachineMap;
+ private PacketReceiver mPacketReceiver;
+
private UdpEncapsulationSocket mClientUdpEncapSocket;
private InetAddress mLocalAddress;
private FileDescriptor mDummyRemoteServerFd;
+ private IkeSessionStateMachine mMockIkeSessionStateMachine;
+
@Before
public void setUp() throws Exception {
Context context = InstrumentationRegistry.getContext();
@@ -73,12 +111,24 @@ public final class IkeSocketTest {
mDataOne = DATA_ONE.getBytes("UTF-8");
mDataTwo = DATA_TWO.getBytes("UTF-8");
+
+ ByteBuffer localSpiBuffer = ByteBuffer.wrap(TestUtils.hexStringToByteArray(LOCAL_SPI));
+ mLocalSpi = localSpiBuffer.getLong();
+ ByteBuffer remoteSpiBuffer = ByteBuffer.wrap(TestUtils.hexStringToByteArray(REMOTE_SPI));
+ mRemoteSpi = remoteSpiBuffer.getLong();
+
+ mMockIkeSessionStateMachine = mock(IkeSessionStateMachine.class);
+
+ mSpiToIkeStateMachineMap = new LongSparseArray<IkeSessionStateMachine>();
+ mSpiToIkeStateMachineMap.put(mLocalSpi, mMockIkeSessionStateMachine);
+
+ mPacketReceiver = new IkeSocket.PacketReceiver();
}
@After
public void tearDown() throws Exception {
mClientUdpEncapSocket.close();
- IkeSocket.setPacketReceiver(new IkeSocket.PacketReceiver());
+ IkeSocket.setPacketReceiver(mPacketReceiver);
Os.close(mDummyRemoteServerFd);
}
@@ -120,7 +170,7 @@ public final class IkeSocketTest {
// Verify received data
ByteBuffer expectedBuffer =
ByteBuffer.allocate(IkeSocket.NON_ESP_MARKER_LEN + mDataOne.length);
- expectedBuffer.put(new byte[IkeSocket.NON_ESP_MARKER_LEN]).put(mDataOne);
+ expectedBuffer.put(IkeSocket.NON_ESP_MARKER).put(mDataOne);
assertArrayEquals(expectedBuffer.array(), receivedData);
@@ -187,6 +237,49 @@ public final class IkeSocketTest {
mIkeThread.quitSafely();
}
+ @Test
+ public void testHandlePacket() throws Exception {
+ byte[] recvBuf =
+ TestUtils.hexStringToByteArray(
+ NON_ESP_MARKER_HEX_STRING + IKE_REQ_MESSAGE_HEX_STRING);
+
+ mPacketReceiver.handlePacket(recvBuf, mSpiToIkeStateMachineMap);
+
+ byte[] expectedIkePacketBytes = TestUtils.hexStringToByteArray(IKE_REQ_MESSAGE_HEX_STRING);
+ ArgumentCaptor<IkeHeader> ikeHeaderCaptor = ArgumentCaptor.forClass(IkeHeader.class);
+ verify(mMockIkeSessionStateMachine)
+ .receiveIkePacket(ikeHeaderCaptor.capture(), eq(expectedIkePacketBytes));
+
+ IkeHeader capturedIkeHeader = ikeHeaderCaptor.getValue();
+ assertEquals(mRemoteSpi, capturedIkeHeader.ikeInitiatorSpi);
+ assertEquals(mLocalSpi, capturedIkeHeader.ikeResponderSpi);
+ }
+
+ @Test
+ public void testHandleEspPacket() throws Exception {
+ byte[] recvBuf =
+ TestUtils.hexStringToByteArray(
+ NON_ESP_MARKER_HEX_STRING + IKE_REQ_MESSAGE_HEX_STRING);
+ // Modify Non-ESP Marker
+ recvBuf[0] = 1;
+
+ mPacketReceiver.handlePacket(recvBuf, mSpiToIkeStateMachineMap);
+
+ verify(mMockIkeSessionStateMachine, never()).receiveIkePacket(any(), any());
+ }
+
+ @Test
+ public void testHandlePacketWithMalformedHeader() throws Exception {
+ String malformedIkePacketHexString = "5f54bf6d8b48e6e100000000000000002120220800000000";
+ byte[] recvBuf =
+ TestUtils.hexStringToByteArray(
+ NON_ESP_MARKER_HEX_STRING + malformedIkePacketHexString);
+
+ mPacketReceiver.handlePacket(recvBuf, mSpiToIkeStateMachineMap);
+
+ verify(mMockIkeSessionStateMachine, never()).receiveIkePacket(any(), any());
+ }
+
private byte[] receive(FileDescriptor mfd) throws Exception {
byte[] receiveBuffer = new byte[REMOTE_RECV_BUFF_SIZE];
AtomicInteger bytesRead = new AtomicInteger(-1);