Add IPsec Tunnel mode data tests

This change adds single-direction tests for the IPsec Tunnel Mode API.
In the outbound direction, TUNs are used to capture outgoing packets,
and values are inspected. In the inbound direction, packets are built
manually, using the PacketUtils framework. Additional testing for
end-to-end integration tests will follow in aosp/941021 using packet
reflection via the TUN.

Bug: 72950854
Test: This; passing
Change-Id: Ic4181fc857fa880db5553314efa914f870dbe87c
This commit is contained in:
Benedict Wong
2019-04-08 10:15:26 -07:00
parent 75fb3d002e
commit 64e64ff454
3 changed files with 661 additions and 83 deletions

View File

@@ -28,11 +28,12 @@ import android.system.OsConstants;
import android.test.AndroidTestCase;
import android.util.Log;
import androidx.test.InstrumentationRegistry;
import java.io.FileDescriptor;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.Inet4Address;
import java.net.Inet6Address;
import java.net.InetAddress;
import java.net.InetSocketAddress;
@@ -72,8 +73,14 @@ public class IpSecBaseTest extends AndroidTestCase {
protected void setUp() throws Exception {
super.setUp();
mISM = (IpSecManager) getContext().getSystemService(Context.IPSEC_SERVICE);
mCM = (ConnectivityManager) getContext().getSystemService(Context.CONNECTIVITY_SERVICE);
mISM =
(IpSecManager)
InstrumentationRegistry.getContext()
.getSystemService(Context.IPSEC_SERVICE);
mCM =
(ConnectivityManager)
InstrumentationRegistry.getContext()
.getSystemService(Context.CONNECTIVITY_SERVICE);
}
protected static byte[] getKey(int bitLength) {
@@ -195,6 +202,17 @@ public class IpSecBaseTest extends AndroidTestCase {
public static class JavaUdpSocket implements GenericUdpSocket {
public final DatagramSocket mSocket;
public JavaUdpSocket(InetAddress localAddr, int port) {
try {
mSocket = new DatagramSocket(port, localAddr);
mSocket.setSoTimeout(SOCK_TIMEOUT);
} catch (SocketException e) {
// Fail loudly if we can't set up sockets properly. And without the timeout, we
// could easily end up in an endless wait.
throw new RuntimeException(e);
}
}
public JavaUdpSocket(InetAddress localAddr) {
try {
mSocket = new DatagramSocket(0, localAddr);
@@ -425,26 +443,25 @@ public class IpSecBaseTest extends AndroidTestCase {
}
protected static IpSecTransform buildIpSecTransform(
Context mContext,
Context context,
IpSecManager.SecurityParameterIndex spi,
IpSecManager.UdpEncapsulationSocket encapSocket,
InetAddress remoteAddr)
throws Exception {
String localAddr = (remoteAddr instanceof Inet4Address) ? IPV4_LOOPBACK : IPV6_LOOPBACK;
IpSecTransform.Builder builder =
new IpSecTransform.Builder(mContext)
.setEncryption(new IpSecAlgorithm(IpSecAlgorithm.CRYPT_AES_CBC, CRYPT_KEY))
.setAuthentication(
new IpSecAlgorithm(
IpSecAlgorithm.AUTH_HMAC_SHA256,
AUTH_KEY,
AUTH_KEY.length * 4));
new IpSecTransform.Builder(context)
.setEncryption(new IpSecAlgorithm(IpSecAlgorithm.CRYPT_AES_CBC, CRYPT_KEY))
.setAuthentication(
new IpSecAlgorithm(
IpSecAlgorithm.AUTH_HMAC_SHA256,
AUTH_KEY,
AUTH_KEY.length * 4));
if (encapSocket != null) {
builder.setIpv4Encapsulation(encapSocket, encapSocket.getPort());
}
return builder.buildTransportModeTransform(InetAddress.getByName(localAddr), spi);
return builder.buildTransportModeTransform(remoteAddr, spi);
}
private IpSecTransform buildDefaultTransform(InetAddress localAddr) throws Exception {

View File

@@ -16,174 +16,728 @@
package android.net.cts;
import static android.app.AppOpsManager.OP_MANAGE_IPSEC_TUNNELS;
import static android.net.IpSecManager.UdpEncapsulationSocket;
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VPN;
import static android.net.NetworkCapabilities.NET_CAPABILITY_TRUSTED;
import static android.net.NetworkCapabilities.TRANSPORT_TEST;
import static android.net.cts.PacketUtils.AES_CBC_BLK_SIZE;
import static android.net.cts.PacketUtils.AES_CBC_IV_LEN;
import static android.net.cts.PacketUtils.BytePayload;
import static android.net.cts.PacketUtils.EspHeader;
import static android.net.cts.PacketUtils.IP4_HDRLEN;
import static android.net.cts.PacketUtils.IP6_HDRLEN;
import static android.net.cts.PacketUtils.Ip4Header;
import static android.net.cts.PacketUtils.Ip6Header;
import static android.net.cts.PacketUtils.IpHeader;
import static android.net.cts.PacketUtils.UDP_HDRLEN;
import static android.net.cts.PacketUtils.UdpHeader;
import static android.system.OsConstants.AF_INET;
import static android.system.OsConstants.AF_INET6;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import android.app.AppOpsManager;
import android.content.Context;
import android.content.pm.PackageManager;
import android.net.ConnectivityManager;
import android.net.IpSecAlgorithm;
import android.net.IpSecManager;
import android.net.IpSecTransform;
import android.net.LinkAddress;
import android.net.Network;
import android.net.NetworkRequest;
import android.net.TestNetworkInterface;
import android.net.TestNetworkManager;
import android.net.cts.PacketUtils.Payload;
import android.os.Binder;
import android.os.IBinder;
import android.os.ParcelFileDescriptor;
import androidx.test.InstrumentationRegistry;
import androidx.test.runner.AndroidJUnit4;
import com.android.compatibility.common.util.SystemUtil;
import java.net.Inet4Address;
import java.net.Inet6Address;
import java.net.InetAddress;
import java.net.InterfaceAddress;
import java.net.NetworkInterface;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.runner.RunWith;
@RunWith(AndroidJUnit4.class)
public class IpSecManagerTunnelTest extends IpSecBaseTest {
private static final String TAG = IpSecManagerTunnelTest.class.getSimpleName();
private static final int IP4_PREFIX_LEN = 24;
private static final int IP6_PREFIX_LEN = 48;
private static final InetAddress OUTER_ADDR4 = InetAddress.parseNumericAddress("192.0.2.0");
private static final InetAddress OUTER_ADDR6 =
InetAddress.parseNumericAddress("2001:db8:f00d::1");
private static final InetAddress INNER_ADDR4 = InetAddress.parseNumericAddress("10.0.0.1");
private static final InetAddress INNER_ADDR6 =
InetAddress.parseNumericAddress("2001:db8:d00d::1");
private Network mUnderlyingNetwork;
private Network mIpSecNetwork;
private static final InetAddress LOCAL_OUTER_4 = InetAddress.parseNumericAddress("192.0.2.1");
private static final InetAddress REMOTE_OUTER_4 = InetAddress.parseNumericAddress("192.0.2.2");
private static final InetAddress LOCAL_OUTER_6 =
InetAddress.parseNumericAddress("2001:db8:1::1");
private static final InetAddress REMOTE_OUTER_6 =
InetAddress.parseNumericAddress("2001:db8:1::2");
protected void setUp() throws Exception {
private static final InetAddress LOCAL_INNER_4 =
InetAddress.parseNumericAddress("198.51.100.1");
private static final InetAddress REMOTE_INNER_4 =
InetAddress.parseNumericAddress("198.51.100.2");
private static final InetAddress LOCAL_INNER_6 =
InetAddress.parseNumericAddress("2001:db8:2::1");
private static final InetAddress REMOTE_INNER_6 =
InetAddress.parseNumericAddress("2001:db8:2::2");
private static final int IP4_PREFIX_LEN = 32;
private static final int IP6_PREFIX_LEN = 128;
private static final int TIMEOUT_MS = 500;
// Static state to reduce setup/teardown
private static ConnectivityManager sCM;
private static TestNetworkManager sTNM;
private static ParcelFileDescriptor sTunFd;
private static TestNetworkCallback sTunNetworkCallback;
private static Network sTunNetwork;
private static TunUtils sTunUtils;
private static Context sContext = InstrumentationRegistry.getContext();
private static IBinder sBinder = new Binder();
@BeforeClass
public static void setUpBeforeClass() throws Exception {
InstrumentationRegistry.getInstrumentation()
.getUiAutomation()
.adoptShellPermissionIdentity();
sCM = (ConnectivityManager) sContext.getSystemService(Context.CONNECTIVITY_SERVICE);
sTNM = (TestNetworkManager) sContext.getSystemService(Context.TEST_NETWORK_SERVICE);
// Under normal circumstances, the MANAGE_IPSEC_TUNNELS appop would be auto-granted, and
// a standard permission is insufficient. So we shell out the appop, to give us the
// right appop permissions.
setAppop(OP_MANAGE_IPSEC_TUNNELS, true);
TestNetworkInterface testIntf =
sTNM.createTunInterface(
new LinkAddress[] {
new LinkAddress(LOCAL_OUTER_4, IP4_PREFIX_LEN),
new LinkAddress(LOCAL_OUTER_6, IP6_PREFIX_LEN)
});
sTunFd = testIntf.getFileDescriptor();
sTunNetworkCallback = setupAndGetTestNetwork(testIntf.getInterfaceName());
sTunNetwork = sTunNetworkCallback.getNetworkBlocking();
sTunUtils = new TunUtils(sTunFd);
}
@Before
public void setUp() throws Exception {
super.setUp();
// Set to true before every run; some tests flip this.
setAppop(OP_MANAGE_IPSEC_TUNNELS, true);
// Clear sTunUtils state
sTunUtils.reset();
}
protected void tearDown() {
setAppop(false);
@AfterClass
public static void tearDownAfterClass() throws Exception {
setAppop(OP_MANAGE_IPSEC_TUNNELS, false);
sCM.unregisterNetworkCallback(sTunNetworkCallback);
sTNM.teardownTestNetwork(sTunNetwork);
sTunFd.close();
InstrumentationRegistry.getInstrumentation()
.getUiAutomation()
.dropShellPermissionIdentity();
}
private boolean hasTunnelsFeature() {
return getContext()
.getPackageManager()
.hasSystemFeature(PackageManager.FEATURE_IPSEC_TUNNELS);
private static boolean hasTunnelsFeature() {
return sContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_IPSEC_TUNNELS);
}
private void setAppop(boolean allow) {
// Under normal circumstances, the MANAGE_IPSEC_TUNNELS appop would be auto-granted by the
// telephony framework, and the only permission that is sufficient is NETWORK_STACK. So we
// shell out the appop manager, to give us the right appop permissions.
String cmd =
"appops set "
+ mContext.getPackageName()
+ " MANAGE_IPSEC_TUNNELS "
+ (allow ? "allow" : "deny");
SystemUtil.runShellCommand(cmd);
private static void setAppop(int appop, boolean allow) {
String opName = AppOpsManager.opToName(appop);
for (String pkg : new String[] {"com.android.shell", sContext.getPackageName()}) {
String cmd =
String.format(
"appops set %s %s %s",
pkg, // Package name
opName, // Appop
(allow ? "allow" : "deny")); // Action
SystemUtil.runShellCommand(cmd);
}
}
public void testSecurityExceptionsCreateTunnelInterface() throws Exception {
private static TestNetworkCallback setupAndGetTestNetwork(String ifname) throws Exception {
// Build a network request
NetworkRequest nr =
new NetworkRequest.Builder()
.addTransportType(TRANSPORT_TEST)
.removeCapability(NET_CAPABILITY_TRUSTED)
.removeCapability(NET_CAPABILITY_NOT_VPN)
.setNetworkSpecifier(ifname)
.build();
TestNetworkCallback cb = new TestNetworkCallback();
sCM.requestNetwork(nr, cb);
// Setup the test network after network request is filed to prevent Network from being
// reaped due to no requests matching it.
sTNM.setupTestNetwork(ifname, sBinder);
return cb;
}
@Test
public void testSecurityExceptionCreateTunnelInterfaceWithoutAppop() throws Exception {
if (!hasTunnelsFeature()) return;
// Ensure we don't have the appop. Permission is not requested in the Manifest
setAppop(false);
setAppop(OP_MANAGE_IPSEC_TUNNELS, false);
// Security exceptions are thrown regardless of IPv4/IPv6. Just test one
try {
mISM.createIpSecTunnelInterface(OUTER_ADDR6, OUTER_ADDR6, mUnderlyingNetwork);
mISM.createIpSecTunnelInterface(LOCAL_INNER_6, REMOTE_INNER_6, sTunNetwork);
fail("Did not throw SecurityException for Tunnel creation without appop");
} catch (SecurityException expected) {
}
}
public void testSecurityExceptionsBuildTunnelTransform() throws Exception {
@Test
public void testSecurityExceptionBuildTunnelTransformWithoutAppop() throws Exception {
if (!hasTunnelsFeature()) return;
// Ensure we don't have the appop. Permission is not requested in the Manifest
setAppop(false);
setAppop(OP_MANAGE_IPSEC_TUNNELS, false);
// Security exceptions are thrown regardless of IPv4/IPv6. Just test one
try (IpSecManager.SecurityParameterIndex spi =
mISM.allocateSecurityParameterIndex(OUTER_ADDR4);
mISM.allocateSecurityParameterIndex(LOCAL_INNER_4);
IpSecTransform transform =
new IpSecTransform.Builder(mContext)
.buildTunnelModeTransform(OUTER_ADDR4, spi)) {
new IpSecTransform.Builder(sContext)
.buildTunnelModeTransform(REMOTE_INNER_4, spi)) {
fail("Did not throw SecurityException for Transform creation without appop");
} catch (SecurityException expected) {
}
}
private void checkTunnel(InetAddress inner, InetAddress outer, boolean useEncap)
/* Test runnables for callbacks after IPsec tunnels are set up. */
private interface TestRunnable {
void run(Network ipsecNetwork) throws Exception;
}
private static class TestNetworkCallback extends ConnectivityManager.NetworkCallback {
private final CompletableFuture<Network> futureNetwork = new CompletableFuture<>();
@Override
public void onAvailable(Network network) {
futureNetwork.complete(network);
}
public Network getNetworkBlocking() throws Exception {
return futureNetwork.get(TIMEOUT_MS, TimeUnit.MILLISECONDS);
}
}
private int getPacketSize(
int innerFamily, int outerFamily, boolean useEncap, boolean transportInTunnelMode) {
int expectedPacketSize = TEST_DATA.length + UDP_HDRLEN;
// Inner Transport mode packet size
if (transportInTunnelMode) {
expectedPacketSize =
PacketUtils.calculateEspPacketSize(
expectedPacketSize,
AES_CBC_IV_LEN,
AES_CBC_BLK_SIZE,
AUTH_KEY.length * 4);
}
// Inner IP Header
expectedPacketSize += innerFamily == AF_INET ? IP4_HDRLEN : IP6_HDRLEN;
// Tunnel mode transform size
expectedPacketSize =
PacketUtils.calculateEspPacketSize(
expectedPacketSize, AES_CBC_IV_LEN, AES_CBC_BLK_SIZE, AUTH_KEY.length * 4);
// UDP encap size
expectedPacketSize += useEncap ? UDP_HDRLEN : 0;
// Outer IP Header
expectedPacketSize += outerFamily == AF_INET ? IP4_HDRLEN : IP6_HDRLEN;
return expectedPacketSize;
}
private interface TestRunnableFactory {
TestRunnable getTestRunnable(
boolean transportInTunnelMode,
int spi,
InetAddress localInner,
InetAddress remoteInner,
InetAddress localOuter,
InetAddress remoteOuter,
IpSecTransform inTransportTransform,
IpSecTransform outTransportTransform,
int encapPort,
int expectedPacketSize)
throws Exception;
}
private class OutputTestRunnableFactory implements TestRunnableFactory {
public TestRunnable getTestRunnable(
boolean transportInTunnelMode,
int spi,
InetAddress localInner,
InetAddress remoteInner,
InetAddress localOuter,
InetAddress remoteOuter,
IpSecTransform inTransportTransform,
IpSecTransform outTransportTransform,
int encapPort,
int expectedPacketSize) {
return new TestRunnable() {
@Override
public void run(Network ipsecNetwork) throws Exception {
// Build a socket and send traffic
JavaUdpSocket socket = new JavaUdpSocket(localInner);
ipsecNetwork.bindSocket(socket.mSocket);
// For Transport-In-Tunnel mode, apply transform to socket
if (transportInTunnelMode) {
mISM.applyTransportModeTransform(
socket.mSocket, IpSecManager.DIRECTION_IN, inTransportTransform);
mISM.applyTransportModeTransform(
socket.mSocket, IpSecManager.DIRECTION_OUT, outTransportTransform);
}
socket.sendTo(TEST_DATA, remoteInner, socket.getPort());
// Verify that an encrypted packet is sent. As of right now, checking encrypted
// body is not possible, due to our not knowing some of the fields of the
// inner IP header (flow label, flags, etc)
sTunUtils.awaitEspPacketNoPlaintext(
spi, TEST_DATA, encapPort != 0, expectedPacketSize);
socket.close();
}
};
}
}
private class InputPacketGeneratorTestRunnableFactory implements TestRunnableFactory {
public TestRunnable getTestRunnable(
boolean transportInTunnelMode,
int spi,
InetAddress localInner,
InetAddress remoteInner,
InetAddress localOuter,
InetAddress remoteOuter,
IpSecTransform inTransportTransform,
IpSecTransform outTransportTransform,
int encapPort,
int expectedPacketSize)
throws Exception {
return new TestRunnable() {
@Override
public void run(Network ipsecNetwork) throws Exception {
// Build a socket and receive traffic
JavaUdpSocket socket = new JavaUdpSocket(localInner);
// JavaUdpSocket socket = new JavaUdpSocket(localInner, socketPort.get());
ipsecNetwork.bindSocket(socket.mSocket);
// For Transport-In-Tunnel mode, apply transform to socket
if (transportInTunnelMode) {
mISM.applyTransportModeTransform(
socket.mSocket, IpSecManager.DIRECTION_IN, outTransportTransform);
mISM.applyTransportModeTransform(
socket.mSocket, IpSecManager.DIRECTION_OUT, inTransportTransform);
}
byte[] pkt;
if (transportInTunnelMode) {
pkt =
getTransportInTunnelModePacket(
spi,
spi,
remoteInner,
localInner,
remoteOuter,
localOuter,
socket.getPort(),
encapPort);
} else {
pkt =
getTunnelModePacket(
spi,
remoteInner,
localInner,
remoteOuter,
localOuter,
socket.getPort(),
encapPort);
}
sTunUtils.injectPacket(pkt);
// Receive packet from socket, and validate
receiveAndValidatePacket(socket);
socket.close();
}
};
}
}
private void checkTunnelOutput(
int innerFamily, int outerFamily, boolean useEncap, boolean transportInTunnelMode)
throws Exception {
checkTunnel(
innerFamily,
outerFamily,
useEncap,
transportInTunnelMode,
new OutputTestRunnableFactory());
}
private void checkTunnelInput(
int innerFamily, int outerFamily, boolean useEncap, boolean transportInTunnelMode)
throws Exception {
checkTunnel(
innerFamily,
outerFamily,
useEncap,
transportInTunnelMode,
new InputPacketGeneratorTestRunnableFactory());
}
public void checkTunnel(
int innerFamily,
int outerFamily,
boolean useEncap,
boolean transportInTunnelMode,
TestRunnableFactory factory)
throws Exception {
if (!hasTunnelsFeature()) return;
setAppop(true);
int innerPrefixLen = inner instanceof Inet6Address ? IP6_PREFIX_LEN : IP4_PREFIX_LEN;
InetAddress localInner = innerFamily == AF_INET ? LOCAL_INNER_4 : LOCAL_INNER_6;
InetAddress remoteInner = innerFamily == AF_INET ? REMOTE_INNER_4 : REMOTE_INNER_6;
try (IpSecManager.SecurityParameterIndex spi = mISM.allocateSecurityParameterIndex(outer);
InetAddress localOuter = outerFamily == AF_INET ? LOCAL_OUTER_4 : LOCAL_OUTER_6;
InetAddress remoteOuter = outerFamily == AF_INET ? REMOTE_OUTER_4 : REMOTE_OUTER_6;
// Preselect both SPI and encap port, to be used for both inbound and outbound tunnels.
// Re-uses the same SPI to ensure that even in cases of symmetric SPIs shared across tunnel
// and transport mode, packets are encrypted/decrypted properly based on the src/dst.
int spi = getRandomSpi(localOuter, remoteOuter);
int expectedPacketSize =
getPacketSize(innerFamily, outerFamily, useEncap, transportInTunnelMode);
try (IpSecManager.SecurityParameterIndex inTransportSpi =
mISM.allocateSecurityParameterIndex(localInner, spi);
IpSecManager.SecurityParameterIndex outTransportSpi =
mISM.allocateSecurityParameterIndex(remoteInner, spi);
IpSecTransform inTransportTransform =
buildIpSecTransform(sContext, inTransportSpi, null, remoteInner);
IpSecTransform outTransportTransform =
buildIpSecTransform(sContext, outTransportSpi, null, localInner);
UdpEncapsulationSocket encapSocket = mISM.openUdpEncapsulationSocket()) {
buildTunnelAndNetwork(
localInner,
remoteInner,
localOuter,
remoteOuter,
spi,
useEncap ? encapSocket : null,
factory.getTestRunnable(
transportInTunnelMode,
spi,
localInner,
remoteInner,
localOuter,
remoteOuter,
inTransportTransform,
outTransportTransform,
useEncap ? encapSocket.getPort() : 0,
expectedPacketSize));
}
}
private void buildTunnelAndNetwork(
InetAddress localInner,
InetAddress remoteInner,
InetAddress localOuter,
InetAddress remoteOuter,
int spi,
UdpEncapsulationSocket encapSocket,
TestRunnable test)
throws Exception {
int innerPrefixLen = localInner instanceof Inet6Address ? IP6_PREFIX_LEN : IP4_PREFIX_LEN;
TestNetworkCallback testNetworkCb = null;
try (IpSecManager.SecurityParameterIndex inSpi =
mISM.allocateSecurityParameterIndex(localOuter, spi);
IpSecManager.SecurityParameterIndex outSpi =
mISM.allocateSecurityParameterIndex(remoteOuter, spi);
IpSecManager.IpSecTunnelInterface tunnelIntf =
mISM.createIpSecTunnelInterface(outer, outer, mCM.getActiveNetwork());
IpSecManager.UdpEncapsulationSocket encapSocket =
mISM.openUdpEncapsulationSocket()) {
mISM.createIpSecTunnelInterface(localOuter, remoteOuter, sTunNetwork)) {
// Build the test network
tunnelIntf.addAddress(localInner, innerPrefixLen);
testNetworkCb = setupAndGetTestNetwork(tunnelIntf.getInterfaceName());
Network testNetwork = testNetworkCb.getNetworkBlocking();
IpSecTransform.Builder transformBuilder = new IpSecTransform.Builder(mContext);
// Check interface was created
NetworkInterface netIntf = NetworkInterface.getByName(tunnelIntf.getInterfaceName());
assertNotNull(netIntf);
// Check addresses
List<InterfaceAddress> intfAddrs = netIntf.getInterfaceAddresses();
assertEquals(1, intfAddrs.size());
assertEquals(localInner, intfAddrs.get(0).getAddress());
assertEquals(innerPrefixLen, intfAddrs.get(0).getNetworkPrefixLength());
// Configure Transform parameters
IpSecTransform.Builder transformBuilder = new IpSecTransform.Builder(sContext);
transformBuilder.setEncryption(
new IpSecAlgorithm(IpSecAlgorithm.CRYPT_AES_CBC, CRYPT_KEY));
transformBuilder.setAuthentication(
new IpSecAlgorithm(
IpSecAlgorithm.AUTH_HMAC_SHA256, AUTH_KEY, AUTH_KEY.length * 4));
if (useEncap) {
if (encapSocket != null) {
transformBuilder.setIpv4Encapsulation(encapSocket, encapSocket.getPort());
}
// Check transform application
try (IpSecTransform transform = transformBuilder.buildTunnelModeTransform(outer, spi)) {
mISM.applyTunnelModeTransform(tunnelIntf, IpSecManager.DIRECTION_IN, transform);
mISM.applyTunnelModeTransform(tunnelIntf, IpSecManager.DIRECTION_OUT, transform);
// Apply transform and check that traffic is properly encrypted
try (IpSecTransform inTransform =
transformBuilder.buildTunnelModeTransform(remoteOuter, inSpi);
IpSecTransform outTransform =
transformBuilder.buildTunnelModeTransform(localOuter, outSpi)) {
mISM.applyTunnelModeTransform(tunnelIntf, IpSecManager.DIRECTION_IN, inTransform);
mISM.applyTunnelModeTransform(tunnelIntf, IpSecManager.DIRECTION_OUT, outTransform);
// TODO: Test to ensure that send/receive works with these transforms.
test.run(testNetwork);
}
// Check interface was created
NetworkInterface netIntf = NetworkInterface.getByName(tunnelIntf.getInterfaceName());
assertNotNull(netIntf);
// Add addresses and check
tunnelIntf.addAddress(inner, innerPrefixLen);
for (InterfaceAddress intfAddr : netIntf.getInterfaceAddresses()) {
assertEquals(intfAddr.getAddress(), inner);
assertEquals(intfAddr.getNetworkPrefixLength(), innerPrefixLen);
}
// Teardown the test network
sTNM.teardownTestNetwork(testNetwork);
// Remove addresses and check
tunnelIntf.removeAddress(inner, innerPrefixLen);
tunnelIntf.removeAddress(localInner, innerPrefixLen);
netIntf = NetworkInterface.getByName(tunnelIntf.getInterfaceName());
assertTrue(netIntf.getInterfaceAddresses().isEmpty());
// Check interface was cleaned up
tunnelIntf.close();
netIntf = NetworkInterface.getByName(tunnelIntf.getInterfaceName());
assertNull(netIntf);
} finally {
if (testNetworkCb != null) {
sCM.unregisterNetworkCallback(testNetworkCb);
}
}
}
/*
* Create, add and remove addresses, then teardown tunnel
*/
private static void receiveAndValidatePacket(JavaUdpSocket socket) throws Exception {
byte[] socketResponseBytes = socket.receive();
assertArrayEquals(TEST_DATA, socketResponseBytes);
}
private int getRandomSpi(InetAddress localOuter, InetAddress remoteOuter) throws Exception {
// Try to allocate both in and out SPIs using the same requested SPI value.
try (IpSecManager.SecurityParameterIndex inSpi =
mISM.allocateSecurityParameterIndex(localOuter);
IpSecManager.SecurityParameterIndex outSpi =
mISM.allocateSecurityParameterIndex(remoteOuter, inSpi.getSpi()); ) {
return inSpi.getSpi();
}
}
private IpHeader getIpHeader(int protocol, InetAddress src, InetAddress dst, Payload payload) {
if ((src instanceof Inet6Address) != (dst instanceof Inet6Address)) {
throw new IllegalArgumentException("Invalid src/dst address combination");
}
if (src instanceof Inet6Address) {
return new Ip6Header(protocol, (Inet6Address) src, (Inet6Address) dst, payload);
} else {
return new Ip4Header(protocol, (Inet4Address) src, (Inet4Address) dst, payload);
}
}
private EspHeader buildTransportModeEspPacket(
int spi, InetAddress src, InetAddress dst, int port, Payload payload) throws Exception {
IpHeader preEspIpHeader = getIpHeader(payload.getProtocolId(), src, dst, payload);
return new EspHeader(
payload.getProtocolId(),
spi,
1, // sequence number
CRYPT_KEY, // Same key for auth and crypt
payload.getPacketBytes(preEspIpHeader));
}
private EspHeader buildTunnelModeEspPacket(
int spi,
InetAddress srcInner,
InetAddress dstInner,
InetAddress srcOuter,
InetAddress dstOuter,
int port,
int encapPort,
Payload payload)
throws Exception {
IpHeader innerIp = getIpHeader(payload.getProtocolId(), srcInner, dstInner, payload);
return new EspHeader(
innerIp.getProtocolId(),
spi,
1, // sequence number
CRYPT_KEY, // Same key for auth and crypt
innerIp.getPacketBytes());
}
private IpHeader maybeEncapPacket(
InetAddress src, InetAddress dst, int encapPort, EspHeader espPayload)
throws Exception {
Payload payload = espPayload;
if (encapPort != 0) {
payload = new UdpHeader(encapPort, encapPort, espPayload);
}
return getIpHeader(payload.getProtocolId(), src, dst, payload);
}
private byte[] getTunnelModePacket(
int spi,
InetAddress srcInner,
InetAddress dstInner,
InetAddress srcOuter,
InetAddress dstOuter,
int port,
int encapPort)
throws Exception {
UdpHeader udp = new UdpHeader(port, port, new BytePayload(TEST_DATA));
EspHeader espPayload =
buildTunnelModeEspPacket(
spi, srcInner, dstInner, srcOuter, dstOuter, port, encapPort, udp);
return maybeEncapPacket(srcOuter, dstOuter, encapPort, espPayload).getPacketBytes();
}
private byte[] getTransportInTunnelModePacket(
int spiInner,
int spiOuter,
InetAddress srcInner,
InetAddress dstInner,
InetAddress srcOuter,
InetAddress dstOuter,
int port,
int encapPort)
throws Exception {
UdpHeader udp = new UdpHeader(port, port, new BytePayload(TEST_DATA));
EspHeader espPayload = buildTransportModeEspPacket(spiInner, srcInner, dstInner, port, udp);
espPayload =
buildTunnelModeEspPacket(
spiOuter,
srcInner,
dstInner,
srcOuter,
dstOuter,
port,
encapPort,
espPayload);
return maybeEncapPacket(srcOuter, dstOuter, encapPort, espPayload).getPacketBytes();
}
// Transport-in-Tunnel mode tests
@Test
public void testTransportInTunnelModeV4InV4() throws Exception {
checkTunnelOutput(AF_INET, AF_INET, false, true);
checkTunnelInput(AF_INET, AF_INET, false, true);
}
@Test
public void testTransportInTunnelModeV4InV4UdpEncap() throws Exception {
checkTunnelOutput(AF_INET, AF_INET, true, true);
checkTunnelInput(AF_INET, AF_INET, true, true);
}
@Test
public void testTransportInTunnelModeV4InV6() throws Exception {
checkTunnelOutput(AF_INET, AF_INET6, false, true);
checkTunnelInput(AF_INET, AF_INET6, false, true);
}
@Test
public void testTransportInTunnelModeV6InV4() throws Exception {
checkTunnelOutput(AF_INET6, AF_INET, false, true);
checkTunnelInput(AF_INET6, AF_INET, false, true);
}
@Test
public void testTransportInTunnelModeV6InV4UdpEncap() throws Exception {
checkTunnelOutput(AF_INET6, AF_INET, true, true);
checkTunnelInput(AF_INET6, AF_INET, true, true);
}
@Test
public void testTransportInTunnelModeV6InV6() throws Exception {
checkTunnelOutput(AF_INET, AF_INET6, false, true);
checkTunnelInput(AF_INET, AF_INET6, false, true);
}
// Tunnel mode tests
@Test
public void testTunnelV4InV4() throws Exception {
checkTunnel(INNER_ADDR4, OUTER_ADDR4, false);
checkTunnelOutput(AF_INET, AF_INET, false, false);
checkTunnelInput(AF_INET, AF_INET, false, false);
}
@Test
public void testTunnelV4InV4UdpEncap() throws Exception {
checkTunnel(INNER_ADDR4, OUTER_ADDR4, true);
checkTunnelOutput(AF_INET, AF_INET, true, false);
checkTunnelInput(AF_INET, AF_INET, true, false);
}
@Test
public void testTunnelV4InV6() throws Exception {
checkTunnel(INNER_ADDR4, OUTER_ADDR6, false);
checkTunnelOutput(AF_INET, AF_INET6, false, false);
checkTunnelInput(AF_INET, AF_INET6, false, false);
}
@Test
public void testTunnelV6InV4() throws Exception {
checkTunnel(INNER_ADDR6, OUTER_ADDR4, false);
checkTunnelOutput(AF_INET6, AF_INET, false, false);
checkTunnelInput(AF_INET6, AF_INET, false, false);
}
@Test
public void testTunnelV6InV4UdpEncap() throws Exception {
checkTunnel(INNER_ADDR6, OUTER_ADDR4, true);
checkTunnelOutput(AF_INET6, AF_INET, true, false);
checkTunnelInput(AF_INET6, AF_INET, true, false);
}
@Test
public void testTunnelV6InV6() throws Exception {
checkTunnel(INNER_ADDR6, OUTER_ADDR6, false);
checkTunnelOutput(AF_INET6, AF_INET6, false, false);
checkTunnelInput(AF_INET6, AF_INET6, false, false);
}
}

View File

@@ -247,4 +247,11 @@ public class TunUtils {
out.write(pkt);
out.flush();
}
/** Resets the intercepted packets. */
public void reset() throws IOException {
synchronized (mPackets) {
mPackets.clear();
}
}
}