diff options
author | evitayan <evitayan@google.com> | 2019-10-30 14:36:17 -0700 |
---|---|---|
committer | evitayan <evitayan@google.com> | 2019-11-07 14:52:32 -0800 |
commit | e8da20d0f53fa8241d9195ae14b4434fe9b8e1e8 (patch) | |
tree | b39c3e794945072d38b97c963c5a6f10753514e1 /tests/iketests/src/java/com/android/internal/net/ipsec/ike/IkeSocketTest.java | |
parent | e27a40124c6885b385cef49f7ae3132c323e00c1 (diff) | |
download | ike-e8da20d0f53fa8241d9195ae14b4434fe9b8e1e8.tar.gz |
Move API and impl to separate packages
This commit
- Moves API and implementation to separate packages so that later
we can expose the API package only.
- Also moves tests accordingly
Bug: 143983419
Test: atest FrameworksIkeTests(all tests passed)
Change-Id: I53cf15caefb09aabaeefd0167d8daeb23e5e571f
Diffstat (limited to 'tests/iketests/src/java/com/android/internal/net/ipsec/ike/IkeSocketTest.java')
-rw-r--r-- | tests/iketests/src/java/com/android/internal/net/ipsec/ike/IkeSocketTest.java | 385 |
1 files changed, 385 insertions, 0 deletions
diff --git a/tests/iketests/src/java/com/android/internal/net/ipsec/ike/IkeSocketTest.java b/tests/iketests/src/java/com/android/internal/net/ipsec/ike/IkeSocketTest.java new file mode 100644 index 00000000..1b8761f7 --- /dev/null +++ b/tests/iketests/src/java/com/android/internal/net/ipsec/ike/IkeSocketTest.java @@ -0,0 +1,385 @@ +/* + * Copyright (C) 2019 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; + +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; +import android.net.IpSecManager.UdpEncapsulationSocket; +import android.os.HandlerThread; +import android.os.Looper; +import android.system.ErrnoException; +import android.system.Os; +import android.system.OsConstants; +import android.util.Log; +import android.util.LongSparseArray; + +import androidx.test.InstrumentationRegistry; + +import com.android.internal.net.TestUtils; +import com.android.internal.net.ipsec.ike.IkeSocket.PacketReceiver; +import com.android.internal.net.ipsec.ike.message.IkeHeader; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.mockito.ArgumentCaptor; + +import java.io.FileDescriptor; +import java.net.InetAddress; +import java.nio.ByteBuffer; +import java.util.Arrays; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; + +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"; + + private static final String IPV4_LOOPBACK = "127.0.0.1"; + + 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(); + IpSecManager ipSecManager = (IpSecManager) context.getSystemService(Context.IPSEC_SERVICE); + mClientUdpEncapSocket = ipSecManager.openUdpEncapsulationSocket(); + + mLocalAddress = InetAddress.getByName(IPV4_LOOPBACK); + mDummyRemoteServerFd = getBoundUdpSocket(mLocalAddress); + + 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(mPacketReceiver); + Os.close(mDummyRemoteServerFd); + } + + private static FileDescriptor getBoundUdpSocket(InetAddress address) throws Exception { + FileDescriptor sock = + Os.socket(OsConstants.AF_INET, OsConstants.SOCK_DGRAM, OsConstants.IPPROTO_UDP); + Os.bind(sock, address, IkeSocket.IKE_SERVER_PORT); + return sock; + } + + @Test + public void testGetAndCloseIkeSocket() throws Exception { + // Must be prepared here; AndroidJUnitRunner runs tests on different threads from the + // setUp() call. Since the new Handler() call is run in getIkeSocket, the Looper must be + // prepared here. + if (Looper.myLooper() == null) Looper.prepare(); + + IkeSessionStateMachine mMockIkeSessionOne = mock(IkeSessionStateMachine.class); + IkeSessionStateMachine mMockIkeSessionTwo = mock(IkeSessionStateMachine.class); + + IkeSocket ikeSocketOne = IkeSocket.getIkeSocket(mClientUdpEncapSocket, mMockIkeSessionOne); + assertEquals(1, ikeSocketOne.mAliveIkeSessions.size()); + + IkeSocket ikeSocketTwo = IkeSocket.getIkeSocket(mClientUdpEncapSocket, mMockIkeSessionTwo); + assertEquals(ikeSocketOne, ikeSocketTwo); + assertEquals(2, ikeSocketTwo.mAliveIkeSessions.size()); + + ikeSocketOne.releaseReference(mMockIkeSessionOne); + assertEquals(1, ikeSocketOne.mAliveIkeSessions.size()); + + ikeSocketTwo.releaseReference(mMockIkeSessionTwo); + assertEquals(0, ikeSocketTwo.mAliveIkeSessions.size()); + } + + @Test + public void testSendIkePacket() throws Exception { + if (Looper.myLooper() == null) Looper.prepare(); + + // Send IKE packet + IkeSocket ikeSocket = + IkeSocket.getIkeSocket(mClientUdpEncapSocket, mMockIkeSessionStateMachine); + ikeSocket.sendIkePacket(mDataOne, mLocalAddress); + + byte[] receivedData = receive(mDummyRemoteServerFd); + + // Verify received data + ByteBuffer expectedBuffer = + ByteBuffer.allocate(IkeSocket.NON_ESP_MARKER_LEN + mDataOne.length); + expectedBuffer.put(IkeSocket.NON_ESP_MARKER).put(mDataOne); + + assertArrayEquals(expectedBuffer.array(), receivedData); + + ikeSocket.releaseReference(mMockIkeSessionStateMachine); + } + + @Test + public void testReceiveIkePacket() throws Exception { + // Create working thread. + HandlerThread mIkeThread = new HandlerThread("IkeSocketTest"); + mIkeThread.start(); + + // Create IkeSocket on working thread. + IkeSocketReceiver socketReceiver = new IkeSocketReceiver(); + TestCountDownLatch createLatch = new TestCountDownLatch(); + mIkeThread + .getThreadHandler() + .post( + () -> { + try { + socketReceiver.setIkeSocket( + IkeSocket.getIkeSocket( + mClientUdpEncapSocket, + mMockIkeSessionStateMachine)); + createLatch.countDown(); + Log.d("IkeSocketTest", "IkeSocket created."); + } catch (ErrnoException e) { + Log.e("IkeSocketTest", "error encountered creating IkeSocket ", e); + } + }); + createLatch.await(); + + IkeSocket ikeSocket = socketReceiver.getIkeSocket(); + assertNotNull(ikeSocket); + + // Configure IkeSocket + TestCountDownLatch receiveLatch = new TestCountDownLatch(); + DummyPacketReceiver packetReceiver = new DummyPacketReceiver(receiveLatch); + IkeSocket.setPacketReceiver(packetReceiver); + + // Send first packet. + sendToIkeSocket(mDummyRemoteServerFd, mDataOne, mLocalAddress); + receiveLatch.await(); + + assertEquals(1, ikeSocket.numPacketsReceived()); + assertArrayEquals(mDataOne, packetReceiver.mReceivedData); + + // Send second packet. + sendToIkeSocket(mDummyRemoteServerFd, mDataTwo, mLocalAddress); + receiveLatch.await(); + + assertEquals(2, ikeSocket.numPacketsReceived()); + assertArrayEquals(mDataTwo, packetReceiver.mReceivedData); + + // Close IkeSocket. + TestCountDownLatch closeLatch = new TestCountDownLatch(); + ikeSocket + .getHandler() + .post( + () -> { + ikeSocket.releaseReference(mMockIkeSessionStateMachine); + closeLatch.countDown(); + }); + closeLatch.await(); + + 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); + Thread receiveThread = + new Thread( + () -> { + while (bytesRead.get() < 0) { + try { + bytesRead.set( + Os.recvfrom( + mDummyRemoteServerFd, + receiveBuffer, + 0, + REMOTE_RECV_BUFF_SIZE, + 0, + null)); + } catch (Exception e) { + Log.e( + "IkeSocketTest", + "Error encountered reading from socket", + e); + } + } + Log.d( + "IkeSocketTest", + "Packet received with size of " + bytesRead.get()); + }); + + receiveThread.start(); + receiveThread.join(TIMEOUT); + + return Arrays.copyOfRange(receiveBuffer, 0, bytesRead.get()); + } + + private void sendToIkeSocket(FileDescriptor fd, byte[] data, InetAddress destAddress) + throws Exception { + Os.sendto(fd, data, 0, data.length, 0, destAddress, mClientUdpEncapSocket.getPort()); + } + + private static class IkeSocketReceiver { + private IkeSocket mIkeSocket; + + void setIkeSocket(IkeSocket ikeSocket) { + mIkeSocket = ikeSocket; + } + + IkeSocket getIkeSocket() { + return mIkeSocket; + } + } + + private static class DummyPacketReceiver implements IkeSocket.IPacketReceiver { + byte[] mReceivedData = null; + final TestCountDownLatch mLatch; + + DummyPacketReceiver(TestCountDownLatch latch) { + mLatch = latch; + } + + public void handlePacket( + byte[] revbuf, LongSparseArray<IkeSessionStateMachine> spiToIkeSession) { + mReceivedData = Arrays.copyOfRange(revbuf, 0, revbuf.length); + mLatch.countDown(); + Log.d("IkeSocketTest", "Packet received"); + } + } + + private static class TestCountDownLatch { + private CountDownLatch mLatch; + + TestCountDownLatch() { + reset(); + } + + private void reset() { + mLatch = new CountDownLatch(1); + } + + void countDown() { + mLatch.countDown(); + } + + void await() { + try { + if (!mLatch.await(TIMEOUT, TimeUnit.MILLISECONDS)) { + fail("Time out"); + } + } catch (InterruptedException e) { + fail(e.toString()); + } + reset(); + } + } +} |