am 78271a94: Merge "Test TCP and ICMPv6 on VPNs in addition to UDP." into lmp-mr1-dev
* commit '78271a94b07e235f2d5213516b75c5bf1d4c87ab': Test TCP and ICMPv6 on VPNs in addition to UDP.
This commit is contained in:
@@ -34,7 +34,7 @@ public class MyVpnService extends VpnService {
|
||||
private static int MTU = 1799;
|
||||
|
||||
private ParcelFileDescriptor mFd = null;
|
||||
private UdpReflector mUdpReflector = null;
|
||||
private PacketReflector mPacketReflector = null;
|
||||
|
||||
@Override
|
||||
public int onStartCommand(Intent intent, int flags, int startId) {
|
||||
@@ -127,14 +127,14 @@ public class MyVpnService extends VpnService {
|
||||
mFd = builder.establish();
|
||||
Log.i(TAG, "Established, fd=" + (mFd == null ? "null" : mFd.getFd()));
|
||||
|
||||
mUdpReflector = new UdpReflector(mFd.getFileDescriptor(), MTU);
|
||||
mUdpReflector.start();
|
||||
mPacketReflector = new PacketReflector(mFd.getFileDescriptor(), MTU);
|
||||
mPacketReflector.start();
|
||||
}
|
||||
|
||||
private void stop() {
|
||||
if (mUdpReflector != null) {
|
||||
mUdpReflector.interrupt();
|
||||
mUdpReflector = null;
|
||||
if (mPacketReflector != null) {
|
||||
mPacketReflector.interrupt();
|
||||
mPacketReflector = null;
|
||||
}
|
||||
try {
|
||||
if (mFd != null) {
|
||||
|
||||
@@ -0,0 +1,234 @@
|
||||
/*
|
||||
* Copyright (C) 2014 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.cts.net.hostside;
|
||||
|
||||
import android.system.ErrnoException;
|
||||
import android.system.Os;
|
||||
import android.util.Log;
|
||||
|
||||
import java.io.FileDescriptor;
|
||||
import java.io.IOException;
|
||||
|
||||
public class PacketReflector extends Thread {
|
||||
|
||||
private static int IPV4_HEADER_LENGTH = 20;
|
||||
private static int IPV6_HEADER_LENGTH = 40;
|
||||
|
||||
private static int IPV4_ADDR_OFFSET = 12;
|
||||
private static int IPV6_ADDR_OFFSET = 8;
|
||||
private static int IPV4_ADDR_LENGTH = 4;
|
||||
private static int IPV6_ADDR_LENGTH = 16;
|
||||
|
||||
private static int IPV4_PROTO_OFFSET = 9;
|
||||
private static int IPV6_PROTO_OFFSET = 6;
|
||||
|
||||
private static final byte IPPROTO_ICMP = 1;
|
||||
private static final byte IPPROTO_TCP = 6;
|
||||
private static final byte IPPROTO_UDP = 17;
|
||||
private static final byte IPPROTO_ICMPV6 = 58;
|
||||
|
||||
private static int ICMP_HEADER_LENGTH = 8;
|
||||
private static int TCP_HEADER_LENGTH = 20;
|
||||
private static int UDP_HEADER_LENGTH = 8;
|
||||
|
||||
private static final byte ICMP_ECHO = 8;
|
||||
private static final byte ICMP_ECHOREPLY = 0;
|
||||
private static final byte ICMPV6_ECHO_REQUEST = (byte) 128;
|
||||
private static final byte ICMPV6_ECHO_REPLY = (byte) 129;
|
||||
|
||||
private static String TAG = "PacketReflector";
|
||||
|
||||
private FileDescriptor mFd;
|
||||
private byte[] mBuf;
|
||||
|
||||
public PacketReflector(FileDescriptor fd, int mtu) {
|
||||
super("PacketReflector");
|
||||
mFd = fd;
|
||||
mBuf = new byte[mtu];
|
||||
}
|
||||
|
||||
private static void swapBytes(byte[] buf, int pos1, int pos2, int len) {
|
||||
for (int i = 0; i < len; i++) {
|
||||
byte b = buf[pos1 + i];
|
||||
buf[pos1 + i] = buf[pos2 + i];
|
||||
buf[pos2 + i] = b;
|
||||
}
|
||||
}
|
||||
|
||||
private static void swapAddresses(byte[] buf, int version) {
|
||||
int addrPos, addrLen;
|
||||
switch(version) {
|
||||
case 4:
|
||||
addrPos = IPV4_ADDR_OFFSET;
|
||||
addrLen = IPV4_ADDR_LENGTH;
|
||||
break;
|
||||
case 6:
|
||||
addrPos = IPV6_ADDR_OFFSET;
|
||||
addrLen = IPV6_ADDR_LENGTH;
|
||||
break;
|
||||
default:
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
swapBytes(buf, addrPos, addrPos + addrLen, addrLen);
|
||||
}
|
||||
|
||||
// Reflect TCP packets: swap the source and destination addresses, but don't change the ports.
|
||||
// This is used by the test to "connect to itself" through the VPN.
|
||||
private void processTcpPacket(byte[] buf, int version, int len, int hdrLen) {
|
||||
if (len < hdrLen + TCP_HEADER_LENGTH) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Swap src and dst IP addresses.
|
||||
swapAddresses(buf, version);
|
||||
|
||||
// Send the packet back.
|
||||
writePacket(buf, len);
|
||||
}
|
||||
|
||||
// Echo UDP packets: swap source and destination addresses, and source and destination ports.
|
||||
// This is used by the test to check that the bytes it sends are echoed back.
|
||||
private void processUdpPacket(byte[] buf, int version, int len, int hdrLen) {
|
||||
if (len < hdrLen + UDP_HEADER_LENGTH) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Swap src and dst IP addresses.
|
||||
swapAddresses(buf, version);
|
||||
|
||||
// Swap dst and src ports.
|
||||
int portOffset = hdrLen;
|
||||
swapBytes(buf, portOffset, portOffset + 2, 2);
|
||||
|
||||
// Send the packet back.
|
||||
writePacket(buf, len);
|
||||
}
|
||||
|
||||
private void processIcmpPacket(byte[] buf, int version, int len, int hdrLen) {
|
||||
if (len < hdrLen + ICMP_HEADER_LENGTH) {
|
||||
return;
|
||||
}
|
||||
|
||||
byte type = buf[hdrLen];
|
||||
if (!(version == 4 && type == ICMP_ECHO) &&
|
||||
!(version == 6 && type == ICMPV6_ECHO_REQUEST)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Save the ping packet we received.
|
||||
byte[] request = buf.clone();
|
||||
|
||||
// Swap src and dst IP addresses, and send the packet back.
|
||||
// This effectively pings the device to see if it replies.
|
||||
swapAddresses(buf, version);
|
||||
writePacket(buf, len);
|
||||
|
||||
// The device should have replied, and buf should now contain a ping response.
|
||||
int received = readPacket(buf);
|
||||
if (received != len) {
|
||||
Log.i(TAG, "Reflecting ping did not result in ping response: " +
|
||||
"read=" + received + " expected=" + len);
|
||||
return;
|
||||
}
|
||||
|
||||
// Compare the response we got with the original packet.
|
||||
// The only thing that should have changed are addresses, type and checksum.
|
||||
// Overwrite them with the received bytes and see if the packet is otherwise identical.
|
||||
request[hdrLen] = buf[hdrLen]; // Type.
|
||||
request[hdrLen + 2] = buf[hdrLen + 2]; // Checksum byte 1.
|
||||
request[hdrLen + 3] = buf[hdrLen + 3]; // Checksum byte 2.
|
||||
for (int i = 0; i < len; i++) {
|
||||
if (buf[i] != request[i]) {
|
||||
Log.i(TAG, "Received non-matching packet when expecting ping response.");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Now swap the addresses again and reflect the packet. This sends a ping reply.
|
||||
swapAddresses(buf, version);
|
||||
writePacket(buf, len);
|
||||
}
|
||||
|
||||
private void writePacket(byte[] buf, int len) {
|
||||
try {
|
||||
Os.write(mFd, buf, 0, len);
|
||||
} catch (ErrnoException|IOException e) {
|
||||
Log.e(TAG, "Error writing packet: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private int readPacket(byte[] buf) {
|
||||
int len;
|
||||
try {
|
||||
len = Os.read(mFd, buf, 0, buf.length);
|
||||
} catch (ErrnoException|IOException e) {
|
||||
Log.e(TAG, "Error reading packet: " + e.getMessage());
|
||||
len = -1;
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
||||
// Reads one packet from our mFd, and possibly writes the packet back.
|
||||
private void processPacket() {
|
||||
int len = readPacket(mBuf);
|
||||
if (len < 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
int version = mBuf[0] >> 4;
|
||||
int addrPos, protoPos, hdrLen, addrLen;
|
||||
if (version == 4) {
|
||||
hdrLen = IPV4_HEADER_LENGTH;
|
||||
protoPos = IPV4_PROTO_OFFSET;
|
||||
addrPos = IPV4_ADDR_OFFSET;
|
||||
addrLen = IPV4_ADDR_LENGTH;
|
||||
} else if (version == 6) {
|
||||
hdrLen = IPV6_HEADER_LENGTH;
|
||||
protoPos = IPV6_PROTO_OFFSET;
|
||||
addrPos = IPV6_ADDR_OFFSET;
|
||||
addrLen = IPV6_ADDR_LENGTH;
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
|
||||
if (len < hdrLen) {
|
||||
return;
|
||||
}
|
||||
|
||||
byte proto = mBuf[protoPos];
|
||||
switch (proto) {
|
||||
case IPPROTO_ICMP:
|
||||
case IPPROTO_ICMPV6:
|
||||
processIcmpPacket(mBuf, version, len, hdrLen);
|
||||
break;
|
||||
case IPPROTO_TCP:
|
||||
processTcpPacket(mBuf, version, len, hdrLen);
|
||||
break;
|
||||
case IPPROTO_UDP:
|
||||
processUdpPacket(mBuf, version, len, hdrLen);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public void run() {
|
||||
Log.i(TAG, "PacketReflector starting fd=" + mFd + " valid=" + mFd.valid());
|
||||
while (!interrupted() && mFd.valid()) {
|
||||
processPacket();
|
||||
}
|
||||
Log.i(TAG, "PacketReflector exiting fd=" + mFd + " valid=" + mFd.valid());
|
||||
}
|
||||
}
|
||||
@@ -1,113 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2014 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.cts.net.hostside;
|
||||
|
||||
import android.system.Os;
|
||||
import android.system.ErrnoException;
|
||||
import android.util.Log;
|
||||
|
||||
import java.io.FileDescriptor;
|
||||
import java.io.IOException;
|
||||
|
||||
public class UdpReflector extends Thread {
|
||||
|
||||
private static int IPV4_HEADER_LENGTH = 20;
|
||||
private static int IPV6_HEADER_LENGTH = 40;
|
||||
private static int UDP_HEADER_LENGTH = 8;
|
||||
|
||||
private static int IPV4_PROTO_OFFSET = 9;
|
||||
private static int IPV6_PROTO_OFFSET = 6;
|
||||
private static int IPPROTO_UDP = 17;
|
||||
|
||||
private static int IPV4_ADDR_OFFSET = 12;
|
||||
private static int IPV6_ADDR_OFFSET = 8;
|
||||
private static int IPV4_ADDR_LENGTH = 4;
|
||||
private static int IPV6_ADDR_LENGTH = 16;
|
||||
|
||||
private static String TAG = "UdpReflector";
|
||||
|
||||
private FileDescriptor mFd;
|
||||
private byte[] mBuf;
|
||||
|
||||
public UdpReflector(FileDescriptor fd, int mtu) {
|
||||
super("UdpReflector");
|
||||
mFd = fd;
|
||||
mBuf = new byte[mtu];
|
||||
}
|
||||
|
||||
private static void swapBytes(byte[] buf, int pos1, int pos2, int len) {
|
||||
for (int i = 0; i < len; i++) {
|
||||
byte b = buf[pos1 + i];
|
||||
buf[pos1 + i] = buf[pos2 + i];
|
||||
buf[pos2 + i] = b;
|
||||
}
|
||||
}
|
||||
|
||||
/** Reads one packet from our mFd, and possibly writes the packet back. */
|
||||
private void processPacket() {
|
||||
int len;
|
||||
try {
|
||||
len = Os.read(mFd, mBuf, 0, mBuf.length);
|
||||
} catch (ErrnoException|IOException e) {
|
||||
Log.e(TAG, "Error reading packet: " + e.getMessage());
|
||||
return;
|
||||
}
|
||||
|
||||
int version = mBuf[0] >> 4;
|
||||
int addressOffset, protoOffset, headerLength, addressLength;
|
||||
if (version == 4) {
|
||||
headerLength = IPV4_HEADER_LENGTH;
|
||||
protoOffset = IPV4_PROTO_OFFSET;
|
||||
addressOffset = IPV4_ADDR_OFFSET;
|
||||
addressLength = IPV4_ADDR_LENGTH;
|
||||
} else if (version == 6) {
|
||||
headerLength = IPV6_HEADER_LENGTH;
|
||||
protoOffset = IPV6_PROTO_OFFSET;
|
||||
addressOffset = IPV6_ADDR_OFFSET;
|
||||
addressLength = IPV6_ADDR_LENGTH;
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
|
||||
if (len < headerLength + UDP_HEADER_LENGTH || mBuf[protoOffset] != IPPROTO_UDP) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Swap src and dst IP addresses.
|
||||
swapBytes(mBuf, addressOffset, addressOffset + addressLength, addressLength);
|
||||
|
||||
// Swap dst and src ports.
|
||||
int portOffset = headerLength;
|
||||
swapBytes(mBuf, portOffset, portOffset + 2, 2);
|
||||
|
||||
// Send the packet back. We don't need to recalculate the checksum because we didn't change
|
||||
// the packet bytes, we only moved them around.
|
||||
try {
|
||||
len = Os.write(mFd, mBuf, 0, len);
|
||||
} catch (ErrnoException|IOException e) {
|
||||
Log.e(TAG, "Error writing packet: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public void run() {
|
||||
Log.i(TAG, "UdpReflector starting fd=" + mFd + " valid=" + mFd.valid());
|
||||
while (!interrupted() && mFd.valid()) {
|
||||
processPacket();
|
||||
}
|
||||
Log.i(TAG, "UdpReflector exiting fd=" + mFd + " valid=" + mFd.valid());
|
||||
}
|
||||
}
|
||||
@@ -16,6 +16,8 @@
|
||||
|
||||
package com.android.cts.net.hostside;
|
||||
|
||||
import static android.system.OsConstants.*;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.net.ConnectivityManager;
|
||||
@@ -25,34 +27,58 @@ import android.net.Network;
|
||||
import android.net.NetworkCapabilities;
|
||||
import android.net.NetworkRequest;
|
||||
import android.net.VpnService;
|
||||
import android.os.ParcelFileDescriptor;
|
||||
import android.os.SystemClock;
|
||||
import android.support.test.uiautomator.UiDevice;
|
||||
import android.support.test.uiautomator.UiObject;
|
||||
import android.support.test.uiautomator.UiObjectNotFoundException;
|
||||
import android.support.test.uiautomator.UiScrollable;
|
||||
import android.support.test.uiautomator.UiSelector;
|
||||
import android.test.MoreAsserts;
|
||||
import android.system.ErrnoException;
|
||||
import android.system.Os;
|
||||
import android.system.StructPollfd;
|
||||
import android.test.InstrumentationTestCase;
|
||||
import android.test.MoreAsserts;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
|
||||
import java.io.Closeable;
|
||||
import java.io.FileDescriptor;
|
||||
import java.io.IOException;
|
||||
import java.net.SocketTimeoutException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.net.DatagramPacket;
|
||||
import java.net.DatagramSocket;
|
||||
import java.net.InetAddress;
|
||||
import java.net.Inet6Address;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import java.net.InetAddress;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.ServerSocket;
|
||||
import java.net.Socket;
|
||||
import java.util.Random;
|
||||
|
||||
/**
|
||||
* Tests for {@link DocumentsProvider} and interaction with platform intents
|
||||
* like {@link Intent#ACTION_OPEN_DOCUMENT}.
|
||||
* Tests for the VpnService API.
|
||||
*
|
||||
* These tests establish a VPN via the VpnService API, and have the service reflect the packets back
|
||||
* to the device without causing any network traffic. This allows testing the local VPN data path
|
||||
* without a network connection or a VPN server.
|
||||
*
|
||||
* Note: in Lollipop, VPN functionality relies on kernel support for UID-based routing. If these
|
||||
* tests fail, it may be due to the lack of kernel support. The necessary patches can be
|
||||
* cherry-picked from the Android common kernel trees:
|
||||
*
|
||||
* android-3.10:
|
||||
* https://android-review.googlesource.com/#/c/99220/
|
||||
* https://android-review.googlesource.com/#/c/100545/
|
||||
*
|
||||
* android-3.4:
|
||||
* https://android-review.googlesource.com/#/c/99225/
|
||||
* https://android-review.googlesource.com/#/c/100557/
|
||||
*
|
||||
*/
|
||||
public class VpnTest extends InstrumentationTestCase {
|
||||
|
||||
public static String TAG = "VpnTest";
|
||||
public static int TIMEOUT_MS = 3 * 1000;
|
||||
public static int SOCKET_TIMEOUT_MS = 100;
|
||||
|
||||
private UiDevice mDevice;
|
||||
private MyActivity mActivity;
|
||||
@@ -201,8 +227,156 @@ public class VpnTest extends InstrumentationTestCase {
|
||||
mActivity.startService(intent);
|
||||
}
|
||||
|
||||
private static void checkUdpEcho(
|
||||
String to, String expectedFrom, boolean expectReply) throws IOException {
|
||||
private static void closeQuietly(Closeable c) {
|
||||
if (c != null) {
|
||||
try {
|
||||
c.close();
|
||||
} catch (IOException e) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void checkPing(String to) throws IOException, ErrnoException {
|
||||
InetAddress address = InetAddress.getByName(to);
|
||||
FileDescriptor s;
|
||||
final int LENGTH = 64;
|
||||
byte[] packet = new byte[LENGTH];
|
||||
byte[] header;
|
||||
|
||||
// Construct a ping packet.
|
||||
Random random = new Random();
|
||||
random.nextBytes(packet);
|
||||
if (address instanceof Inet6Address) {
|
||||
s = Os.socket(AF_INET6, SOCK_DGRAM, IPPROTO_ICMPV6);
|
||||
header = new byte[] { (byte) 0x80, (byte) 0x00, (byte) 0x00, (byte) 0x00 };
|
||||
} else {
|
||||
// Note that this doesn't actually work due to http://b/18558481 .
|
||||
s = Os.socket(AF_INET, SOCK_DGRAM, IPPROTO_ICMP);
|
||||
header = new byte[] { (byte) 0x08, (byte) 0x00, (byte) 0x00, (byte) 0x00 };
|
||||
}
|
||||
System.arraycopy(header, 0, packet, 0, header.length);
|
||||
|
||||
// Send the packet.
|
||||
int port = random.nextInt(65534) + 1;
|
||||
Os.connect(s, address, port);
|
||||
Os.write(s, packet, 0, packet.length);
|
||||
|
||||
// Expect a reply.
|
||||
StructPollfd pollfd = new StructPollfd();
|
||||
pollfd.events = (short) POLLIN; // "error: possible loss of precision"
|
||||
pollfd.fd = s;
|
||||
int ret = Os.poll(new StructPollfd[] { pollfd }, SOCKET_TIMEOUT_MS);
|
||||
assertEquals("Expected reply after sending ping", 1, ret);
|
||||
|
||||
byte[] reply = new byte[LENGTH];
|
||||
int read = Os.read(s, reply, 0, LENGTH);
|
||||
assertEquals(LENGTH, read);
|
||||
|
||||
// Find out what the kernel set the ICMP ID to.
|
||||
InetSocketAddress local = (InetSocketAddress) Os.getsockname(s);
|
||||
port = local.getPort();
|
||||
packet[4] = (byte) ((port >> 8) & 0xff);
|
||||
packet[5] = (byte) (port & 0xff);
|
||||
|
||||
// Check the contents.
|
||||
if (packet[0] == (byte) 0x80) {
|
||||
packet[0] = (byte) 0x81;
|
||||
} else {
|
||||
packet[0] = 0;
|
||||
}
|
||||
// Zero out the checksum in the reply so it matches the uninitialized checksum in packet.
|
||||
reply[2] = reply[3] = 0;
|
||||
MoreAsserts.assertEquals(packet, reply);
|
||||
}
|
||||
|
||||
// Writes data to out and checks that it appears identically on in.
|
||||
private static void writeAndCheckData(
|
||||
OutputStream out, InputStream in, byte[] data) throws IOException {
|
||||
out.write(data, 0, data.length);
|
||||
out.flush();
|
||||
|
||||
byte[] read = new byte[data.length];
|
||||
int bytesRead = 0, totalRead = 0;
|
||||
do {
|
||||
bytesRead = in.read(read, totalRead, read.length - totalRead);
|
||||
totalRead += bytesRead;
|
||||
} while (bytesRead >= 0 && totalRead < data.length);
|
||||
assertEquals(totalRead, data.length);
|
||||
MoreAsserts.assertEquals(data, read);
|
||||
}
|
||||
|
||||
private static void checkTcpReflection(String to, String expectedFrom) throws IOException {
|
||||
// Exercise TCP over the VPN by "connecting to ourselves". We open a server socket and a
|
||||
// client socket, and connect the client socket to a remote host, with the port of the
|
||||
// server socket. The PacketReflector reflects the packets, changing the source addresses
|
||||
// but not the ports, so our client socket is connected to our server socket, though both
|
||||
// sockets think their peers are on the "remote" IP address.
|
||||
|
||||
// Open a listening socket.
|
||||
ServerSocket listen = new ServerSocket(0, 10, InetAddress.getByName("::"));
|
||||
|
||||
// Connect the client socket to it.
|
||||
InetAddress toAddr = InetAddress.getByName(to);
|
||||
Socket client = new Socket();
|
||||
try {
|
||||
client.connect(new InetSocketAddress(toAddr, listen.getLocalPort()), SOCKET_TIMEOUT_MS);
|
||||
if (expectedFrom == null) {
|
||||
closeQuietly(listen);
|
||||
closeQuietly(client);
|
||||
fail("Expected connection to fail, but it succeeded.");
|
||||
}
|
||||
} catch (IOException e) {
|
||||
if (expectedFrom != null) {
|
||||
closeQuietly(listen);
|
||||
fail("Expected connection to succeed, but it failed.");
|
||||
} else {
|
||||
// We expected the connection to fail, and it did, so there's nothing more to test.
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// The connection succeeded, and we expected it to succeed. Send some data; if things are
|
||||
// working, the data will be sent to the VPN, reflected by the PacketReflector, and arrive
|
||||
// at our server socket. For good measure, send some data in the other direction.
|
||||
Socket server = null;
|
||||
try {
|
||||
// Accept the connection on the server side.
|
||||
listen.setSoTimeout(SOCKET_TIMEOUT_MS);
|
||||
server = listen.accept();
|
||||
|
||||
// Check that the source and peer addresses are as expected.
|
||||
assertEquals(expectedFrom, client.getLocalAddress().getHostAddress());
|
||||
assertEquals(expectedFrom, server.getLocalAddress().getHostAddress());
|
||||
assertEquals(
|
||||
new InetSocketAddress(toAddr, client.getLocalPort()),
|
||||
server.getRemoteSocketAddress());
|
||||
assertEquals(
|
||||
new InetSocketAddress(toAddr, server.getLocalPort()),
|
||||
client.getRemoteSocketAddress());
|
||||
|
||||
// Now write some data.
|
||||
final int LENGTH = 32768;
|
||||
byte[] data = new byte[LENGTH];
|
||||
new Random().nextBytes(data);
|
||||
|
||||
// Make sure our writes don't block or time out, because we're single-threaded and can't
|
||||
// read and write at the same time.
|
||||
server.setReceiveBufferSize(LENGTH * 2);
|
||||
client.setSendBufferSize(LENGTH * 2);
|
||||
client.setSoTimeout(SOCKET_TIMEOUT_MS);
|
||||
server.setSoTimeout(SOCKET_TIMEOUT_MS);
|
||||
|
||||
// Send some data from client to server, then from server to client.
|
||||
writeAndCheckData(client.getOutputStream(), server.getInputStream(), data);
|
||||
writeAndCheckData(server.getOutputStream(), client.getInputStream(), data);
|
||||
} finally {
|
||||
closeQuietly(listen);
|
||||
closeQuietly(client);
|
||||
closeQuietly(server);
|
||||
}
|
||||
}
|
||||
|
||||
private static void checkUdpEcho(String to, String expectedFrom) throws IOException {
|
||||
DatagramSocket s;
|
||||
InetAddress address = InetAddress.getByName(to);
|
||||
if (address instanceof Inet6Address) { // http://b/18094870
|
||||
@@ -210,10 +384,12 @@ public class VpnTest extends InstrumentationTestCase {
|
||||
} else {
|
||||
s = new DatagramSocket();
|
||||
}
|
||||
s.setSoTimeout(100); // ms.
|
||||
s.setSoTimeout(SOCKET_TIMEOUT_MS);
|
||||
|
||||
String msg = "Hello, world!";
|
||||
DatagramPacket p = new DatagramPacket(msg.getBytes(), msg.length());
|
||||
Random random = new Random();
|
||||
byte[] data = new byte[random.nextInt(1650)];
|
||||
random.nextBytes(data);
|
||||
DatagramPacket p = new DatagramPacket(data, data.length);
|
||||
s.connect(address, 7);
|
||||
|
||||
if (expectedFrom != null) {
|
||||
@@ -222,10 +398,10 @@ public class VpnTest extends InstrumentationTestCase {
|
||||
}
|
||||
|
||||
try {
|
||||
if (expectReply) {
|
||||
if (expectedFrom != null) {
|
||||
s.send(p);
|
||||
s.receive(p);
|
||||
MoreAsserts.assertEquals(msg.getBytes(), p.getData());
|
||||
MoreAsserts.assertEquals(data, p.getData());
|
||||
} else {
|
||||
try {
|
||||
s.send(p);
|
||||
@@ -238,12 +414,19 @@ public class VpnTest extends InstrumentationTestCase {
|
||||
}
|
||||
}
|
||||
|
||||
private static void expectUdpEcho(String to, String expectedFrom) throws IOException {
|
||||
checkUdpEcho(to, expectedFrom, true);
|
||||
private void checkTrafficOnVpn() throws IOException, ErrnoException {
|
||||
checkUdpEcho("192.0.2.251", "192.0.2.2");
|
||||
checkUdpEcho("2001:db8:dead:beef::f00", "2001:db8:1:2::ffe");
|
||||
checkPing("2001:db8:dead:beef::f00");
|
||||
checkTcpReflection("192.0.2.252", "192.0.2.2");
|
||||
checkTcpReflection("2001:db8:dead:beef::f00", "2001:db8:1:2::ffe");
|
||||
}
|
||||
|
||||
private static void expectNoUdpEcho(String to) throws IOException {
|
||||
checkUdpEcho(to, null, false);
|
||||
private void checkNoTrafficOnVpn() throws IOException, ErrnoException {
|
||||
checkUdpEcho("192.0.2.251", null);
|
||||
checkUdpEcho("2001:db8:dead:beef::f00", null);
|
||||
checkTcpReflection("192.0.2.252", null);
|
||||
checkTcpReflection("2001:db8:dead:beef::f00", null);
|
||||
}
|
||||
|
||||
public void testDefault() throws Exception {
|
||||
@@ -253,8 +436,7 @@ public class VpnTest extends InstrumentationTestCase {
|
||||
new String[] {"192.0.2.0/24", "2001:db8::/32"},
|
||||
"", "");
|
||||
|
||||
expectUdpEcho("192.0.2.251", "192.0.2.2");
|
||||
expectUdpEcho("2001:db8:dead:beef::f00", "2001:db8:1:2::ffe");
|
||||
checkTrafficOnVpn();
|
||||
}
|
||||
|
||||
public void testAppAllowed() throws Exception {
|
||||
@@ -264,8 +446,7 @@ public class VpnTest extends InstrumentationTestCase {
|
||||
new String[] {"0.0.0.0/0", "::/0"},
|
||||
mPackageName, "");
|
||||
|
||||
expectUdpEcho("192.0.2.251", "192.0.2.2");
|
||||
expectUdpEcho("2001:db8:dead:beef::f00", "2001:db8:1:2::ffe");
|
||||
checkTrafficOnVpn();
|
||||
}
|
||||
|
||||
public void testAppDisallowed() throws Exception {
|
||||
@@ -275,7 +456,6 @@ public class VpnTest extends InstrumentationTestCase {
|
||||
new String[] {"192.0.2.0/24", "2001:db8::/32"},
|
||||
"", mPackageName);
|
||||
|
||||
expectNoUdpEcho("192.0.2.251");
|
||||
expectNoUdpEcho("2001:db8:dead:beef::f00");
|
||||
checkNoTrafficOnVpn();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user