blob: 2363cff505a3ce93dce700f2a9c156cc11579815 [file] [log] [blame]
/*
* 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;
import static org.junit.Assert.fail;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.reset;
import android.net.InetAddresses;
import android.net.Network;
import android.system.ErrnoException;
import android.system.Os;
import android.system.OsConstants;
import android.util.Log;
import android.util.LongSparseArray;
import org.junit.After;
import org.junit.Before;
import java.io.FileDescriptor;
import java.io.IOException;
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 abstract class IkeSocketTestBase {
protected static final int REMOTE_RECV_BUFF_SIZE = 2048;
protected static final int TIMEOUT = 1000;
protected static final String NON_ESP_MARKER_HEX_STRING = "00000000";
protected static final String IKE_REQ_MESSAGE_HEX_STRING =
"5f54bf6d8b48e6e100000000000000002120220800000000"
+ "00000150220000300000002c010100040300000c0100000c"
+ "800e00800300000803000002030000080400000200000008"
+ "020000022800008800020000b4a2faf4bb54878ae21d6385"
+ "12ece55d9236fc5046ab6cef82220f421f3ce6361faf3656"
+ "4ecb6d28798a94aad7b2b4b603ddeaaa5630adb9ece8ac37"
+ "534036040610ebdd92f46bef84f0be7db860351843858f8a"
+ "cf87056e272377f70c9f2d81e29c7b0ce4f291a3a72476bb"
+ "0b278fd4b7b0a4c26bbeb08214c707137607958729000024"
+ "c39b7f368f4681b89fa9b7be6465abd7c5f68b6ed5d3b4c7"
+ "2cb4240eb5c464122900001c00004004e54f73b7d83f6beb"
+ "881eab2051d8663f421d10b02b00001c00004005d915368c"
+ "a036004cb578ae3e3fb268509aeab1900000002069936922"
+ "8741c6d4ca094c93e242c9de19e7b7c60000000500000500";
protected static final long LOCAL_SPI = 0x0L;
protected static final long REMOTE_SPI = 0x5f54bf6d8b48e6e1L;
protected static final String DATA_ONE = "one 1";
protected static final String DATA_TWO = "two 2";
protected static final InetAddress IPV4_LOOPBACK =
InetAddresses.parseNumericAddress("127.0.0.1");
protected static final InetAddress IPV6_LOOPBACK = InetAddresses.parseNumericAddress("::1");
protected final LongSparseArray mSpiToIkeStateMachineMap =
new LongSparseArray<IkeSessionStateMachine>();
protected final Network mMockNetwork = mock(Network.class);
protected final IkeSessionStateMachine mMockIkeSessionStateMachine =
mock(IkeSessionStateMachine.class);
protected byte[] mDataOne;
protected byte[] mDataTwo;
protected FileDescriptor mDummyRemoteServerFd;
protected InetAddress mLocalAddress;
@Before
public void setUp() throws Exception {
mSpiToIkeStateMachineMap.put(LOCAL_SPI, mMockIkeSessionStateMachine);
mDummyRemoteServerFd = getBoundUdpSocket(IPV4_LOOPBACK);
mDataOne = DATA_ONE.getBytes("UTF-8");
mDataTwo = DATA_TWO.getBytes("UTF-8");
}
@After
public void tearDown() throws Exception {
Os.close(mDummyRemoteServerFd);
}
protected abstract IkeSocket.IPacketReceiver getPacketReceiver();
protected 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.SERVER_PORT_UDP_ENCAPSULATED);
return sock;
}
protected boolean isFdOpen(FileDescriptor fd) {
try {
Os.getsockname(fd);
return true;
} catch (ErrnoException ignored) {
return false;
}
}
protected void verifyCloseFd(FileDescriptor fd) {
try {
Os.sendto(
fd,
ByteBuffer.wrap(mDataOne),
0,
InetAddress.getLoopbackAddress(),
IkeSocket.SERVER_PORT_UDP_ENCAPSULATED);
fail("Expected to fail because fd is closed");
} catch (ErrnoException | IOException expected) {
}
}
protected 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);
}
}
});
receiveThread.start();
receiveThread.join(TIMEOUT);
return Arrays.copyOfRange(receiveBuffer, 0, bytesRead.get());
}
protected void sendToIkeSocket(
FileDescriptor fd, byte[] data, InetAddress destAddress, int port) throws Exception {
Os.sendto(fd, data, 0, data.length, 0, destAddress, port);
}
protected static class DummyPacketReceiver implements IkeSocket.IPacketReceiver {
byte[] mReceivedData = null;
final TestCountDownLatch mLatch;
DummyPacketReceiver(TestCountDownLatch latch) {
mLatch = latch;
}
@Override
public void handlePacket(
byte[] revbuf, LongSparseArray<IkeSessionStateMachine> spiToIkeSession) {
mReceivedData = Arrays.copyOfRange(revbuf, 0, revbuf.length);
mLatch.countDown();
}
}
protected 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();
}
}
}