Add test for dropping packets to VPN address from non VPN-interface

Bug: 295800201
Test: HostsideVpnTests
Change-Id: I0785ee1562fa50c1b6a4aca9ac077ad753cc2700
This commit is contained in:
Motomu Utsumi 2023-10-27 14:42:45 +09:00
commit ffa2bce381
2 changed files with 102 additions and 50 deletions

View file

@ -24,6 +24,7 @@ import static android.content.pm.PackageManager.FEATURE_TELEPHONY;
import static android.content.pm.PackageManager.FEATURE_WIFI;
import static android.net.ConnectivityManager.TYPE_VPN;
import static android.net.NetworkCapabilities.TRANSPORT_VPN;
import static android.net.NetworkCapabilities.TRANSPORT_TEST;
import static android.os.Process.INVALID_UID;
import static android.system.OsConstants.AF_INET;
import static android.system.OsConstants.AF_INET6;
@ -72,6 +73,7 @@ import android.content.pm.PackageManager;
import android.database.Cursor;
import android.net.ConnectivityManager;
import android.net.ConnectivityManager.NetworkCallback;
import android.net.InetAddresses;
import android.net.IpSecManager;
import android.net.LinkAddress;
import android.net.LinkProperties;
@ -91,6 +93,7 @@ import android.net.VpnTransportInfo;
import android.net.cts.util.CtsNetUtils;
import android.net.util.KeepaliveUtils;
import android.net.wifi.WifiManager;
import android.os.Binder;
import android.os.Build;
import android.os.Handler;
import android.os.Looper;
@ -249,6 +252,7 @@ public class VpnTest {
@Before
public void setUp() throws Exception {
assumeTrue(supportedHardware());
mNetwork = null;
mTestContext = getInstrumentation().getContext();
mTargetContext = getInstrumentation().getTargetContext();
@ -879,7 +883,6 @@ public class VpnTest {
@Test @IgnoreUpTo(SC_V2) // TODO: Use to Build.VERSION_CODES.SC_V2 when available
public void testChangeUnderlyingNetworks() throws Exception {
assumeTrue(supportedHardware());
assumeTrue(mPackageManager.hasSystemFeature(FEATURE_WIFI));
assumeTrue(mPackageManager.hasSystemFeature(FEATURE_TELEPHONY));
final TestableNetworkCallback callback = new TestableNetworkCallback();
@ -938,7 +941,6 @@ public class VpnTest {
@Test
public void testDefault() throws Exception {
assumeTrue(supportedHardware());
if (!SdkLevel.isAtLeastS() && (
SystemProperties.getInt("persist.adb.tcp.port", -1) > -1
|| SystemProperties.getInt("service.adb.tcp.port", -1) > -1)) {
@ -1033,8 +1035,6 @@ public class VpnTest {
@Test
public void testAppAllowed() throws Exception {
assumeTrue(supportedHardware());
FileDescriptor fd = openSocketFdInOtherApp(TEST_HOST, 80, TIMEOUT_MS);
// Shell app must not be put in here or it would kill the ADB-over-network use case
@ -1142,8 +1142,6 @@ public class VpnTest {
}
private void doTestAutomaticOnOffKeepaliveMode(final boolean closeSocket) throws Exception {
assumeTrue(supportedHardware());
// Get default network first before starting VPN
final Network defaultNetwork = mCM.getActiveNetwork();
final TestableNetworkCallback cb = new TestableNetworkCallback();
@ -1231,8 +1229,6 @@ public class VpnTest {
@Test
public void testAppDisallowed() throws Exception {
assumeTrue(supportedHardware());
FileDescriptor localFd = openSocketFd(TEST_HOST, 80, TIMEOUT_MS);
FileDescriptor remoteFd = openSocketFdInOtherApp(TEST_HOST, 80, TIMEOUT_MS);
@ -1265,8 +1261,6 @@ public class VpnTest {
@Test
public void testSocketClosed() throws Exception {
assumeTrue(supportedHardware());
final FileDescriptor localFd = openSocketFd(TEST_HOST, 80, TIMEOUT_MS);
final List<FileDescriptor> remoteFds = new ArrayList<>();
@ -1290,7 +1284,6 @@ public class VpnTest {
@Test
public void testExcludedRoutes() throws Exception {
assumeTrue(supportedHardware());
assumeTrue(SdkLevel.isAtLeastT());
// Shell app must not be put in here or it would kill the ADB-over-network use case
@ -1311,8 +1304,6 @@ public class VpnTest {
@Test
public void testIncludedRoutes() throws Exception {
assumeTrue(supportedHardware());
// Shell app must not be put in here or it would kill the ADB-over-network use case
String allowedApps = mRemoteSocketFactoryClient.getPackageName() + "," + mPackageName;
startVpn(new String[]{"192.0.2.2/32", "2001:db8:1:2::ffe/128"} /* addresses */,
@ -1330,7 +1321,6 @@ public class VpnTest {
@Test
public void testInterleavedRoutes() throws Exception {
assumeTrue(supportedHardware());
assumeTrue(SdkLevel.isAtLeastT());
// Shell app must not be put in here or it would kill the ADB-over-network use case
@ -1358,8 +1348,6 @@ public class VpnTest {
@Test
public void testGetConnectionOwnerUidSecurity() throws Exception {
assumeTrue(supportedHardware());
DatagramSocket s;
InetAddress address = InetAddress.getByName("localhost");
s = new DatagramSocket();
@ -1380,7 +1368,6 @@ public class VpnTest {
@Test
public void testSetProxy() throws Exception {
assumeTrue(supportedHardware());
ProxyInfo initialProxy = mCM.getDefaultProxy();
// Receiver for the proxy change broadcast.
BlockingBroadcastReceiver proxyBroadcastReceiver = new ProxyChangeBroadcastReceiver();
@ -1420,7 +1407,6 @@ public class VpnTest {
@Test
public void testSetProxyDisallowedApps() throws Exception {
assumeTrue(supportedHardware());
ProxyInfo initialProxy = mCM.getDefaultProxy();
String disallowedApps = mPackageName;
@ -1446,7 +1432,6 @@ public class VpnTest {
@Test
public void testNoProxy() throws Exception {
assumeTrue(supportedHardware());
ProxyInfo initialProxy = mCM.getDefaultProxy();
BlockingBroadcastReceiver proxyBroadcastReceiver = new ProxyChangeBroadcastReceiver();
proxyBroadcastReceiver.register();
@ -1481,7 +1466,6 @@ public class VpnTest {
@Test
public void testBindToNetworkWithProxy() throws Exception {
assumeTrue(supportedHardware());
String allowedApps = mPackageName;
Network initialNetwork = mCM.getActiveNetwork();
ProxyInfo initialProxy = mCM.getDefaultProxy();
@ -1506,9 +1490,6 @@ public class VpnTest {
@Test
public void testVpnMeterednessWithNoUnderlyingNetwork() throws Exception {
if (!supportedHardware()) {
return;
}
// VPN is not routing any traffic i.e. its underlying networks is an empty array.
ArrayList<Network> underlyingNetworks = new ArrayList<>();
String allowedApps = mPackageName;
@ -1538,9 +1519,6 @@ public class VpnTest {
@Test
public void testVpnMeterednessWithNullUnderlyingNetwork() throws Exception {
if (!supportedHardware()) {
return;
}
Network underlyingNetwork = mCM.getActiveNetwork();
if (underlyingNetwork == null) {
Log.i(TAG, "testVpnMeterednessWithNullUnderlyingNetwork cannot execute"
@ -1567,9 +1545,6 @@ public class VpnTest {
@Test
public void testVpnMeterednessWithNonNullUnderlyingNetwork() throws Exception {
if (!supportedHardware()) {
return;
}
Network underlyingNetwork = mCM.getActiveNetwork();
if (underlyingNetwork == null) {
Log.i(TAG, "testVpnMeterednessWithNonNullUnderlyingNetwork cannot execute"
@ -1609,9 +1584,6 @@ public class VpnTest {
@Test
public void testAlwaysMeteredVpnWithNullUnderlyingNetwork() throws Exception {
if (!supportedHardware()) {
return;
}
Network underlyingNetwork = mCM.getActiveNetwork();
if (underlyingNetwork == null) {
Log.i(TAG, "testAlwaysMeteredVpnWithNullUnderlyingNetwork cannot execute"
@ -1636,9 +1608,6 @@ public class VpnTest {
@Test
public void testAlwaysMeteredVpnWithNonNullUnderlyingNetwork() throws Exception {
if (!supportedHardware()) {
return;
}
Network underlyingNetwork = mCM.getActiveNetwork();
if (underlyingNetwork == null) {
Log.i(TAG, "testAlwaysMeteredVpnWithNonNullUnderlyingNetwork cannot execute"
@ -1676,9 +1645,6 @@ public class VpnTest {
@Test
public void testB141603906() throws Exception {
if (!supportedHardware()) {
return;
}
final InetSocketAddress src = new InetSocketAddress(0);
final InetSocketAddress dst = new InetSocketAddress(0);
final int NUM_THREADS = 8;
@ -1786,8 +1752,6 @@ public class VpnTest {
*/
@Test
public void testDownloadWithDownloadManagerDisallowed() throws Exception {
assumeTrue(supportedHardware());
// Start a VPN with DownloadManager package in disallowed list.
startVpn(new String[] {"192.0.2.2/32", "2001:db8:1:2::ffe/128"},
new String[] {"192.0.2.0/24", "2001:db8::/32"},
@ -1843,7 +1807,6 @@ public class VpnTest {
@Test @IgnoreUpTo(Build.VERSION_CODES.R)
public void testBlockIncomingPackets() throws Exception {
assumeTrue(supportedHardware());
final Network network = mCM.getActiveNetwork();
assertNotNull("Requires a working Internet connection", network);
@ -1912,7 +1875,6 @@ public class VpnTest {
@Test
public void testSetVpnDefaultForUids() throws Exception {
assumeTrue(supportedHardware());
assumeTrue(SdkLevel.isAtLeastU());
final Network defaultNetwork = mCM.getActiveNetwork();
@ -1958,6 +1920,81 @@ public class VpnTest {
});
}
/**
* Check if packets to a VPN interface's IP arriving on a non-VPN interface are dropped or not.
* If the test interface has a different address from the VPN interface, packets must be dropped
* If the test interface has the same address as the VPN interface, packets must not be
* dropped
*
* @param duplicatedAddress true to bring up the test interface with the same address as the VPN
* interface
*/
private void doTestDropPacketToVpnAddress(final boolean duplicatedAddress)
throws Exception {
final NetworkRequest request = new NetworkRequest.Builder()
.removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VPN)
.removeCapability(NetworkCapabilities.NET_CAPABILITY_TRUSTED)
.addTransportType(TRANSPORT_TEST)
.build();
final CtsNetUtils.TestNetworkCallback callback = new CtsNetUtils.TestNetworkCallback();
mCM.requestNetwork(request, callback);
final FileDescriptor srcTunFd = runWithShellPermissionIdentity(() -> {
final TestNetworkManager tnm = mTestContext.getSystemService(TestNetworkManager.class);
List<LinkAddress> linkAddresses = duplicatedAddress
? List.of(new LinkAddress("192.0.2.2/24"),
new LinkAddress("2001:db8:1:2::ffe/64")) :
List.of(new LinkAddress("198.51.100.2/24"),
new LinkAddress("2001:db8:3:4::ffe/64"));
final TestNetworkInterface iface = tnm.createTunInterface(linkAddresses);
tnm.setupTestNetwork(iface.getInterfaceName(), new Binder());
return iface.getFileDescriptor().getFileDescriptor();
}, MANAGE_TEST_NETWORKS);
final Network testNetwork = callback.waitForAvailable();
assertNotNull(testNetwork);
final DatagramSocket dstSock = new DatagramSocket();
testAndCleanup(() -> {
startVpn(new String[] {"192.0.2.2/32", "2001:db8:1:2::ffe/128"} /* addresses */,
new String[]{"0.0.0.0/0", "::/0"} /* routes */,
"" /* allowedApplications */, "" /* disallowedApplications */,
null /* proxyInfo */, null /* underlyingNetworks */,
false /* isAlwaysMetered */);
final FileDescriptor dstUdpFd = dstSock.getFileDescriptor$();
checkBlockUdp(srcTunFd, dstUdpFd,
InetAddresses.parseNumericAddress("192.0.2.2") /* dstAddress */,
InetAddresses.parseNumericAddress("192.0.2.1") /* srcAddress */,
duplicatedAddress ? EXPECT_PASS : EXPECT_BLOCK);
checkBlockUdp(srcTunFd, dstUdpFd,
InetAddresses.parseNumericAddress("2001:db8:1:2::ffe") /* dstAddress */,
InetAddresses.parseNumericAddress("2001:db8:1:2::ffa") /* srcAddress */,
duplicatedAddress ? EXPECT_PASS : EXPECT_BLOCK);
// Traffic on VPN should not be affected
checkTrafficOnVpn();
}, /* cleanup */ () -> {
Os.close(srcTunFd);
dstSock.close();
}, /* cleanup */ () -> {
runWithShellPermissionIdentity(() -> {
mTestContext.getSystemService(TestNetworkManager.class)
.teardownTestNetwork(testNetwork);
}, MANAGE_TEST_NETWORKS);
}, /* cleanup */ () -> {
mCM.unregisterNetworkCallback(callback);
});
}
@Test @IgnoreUpTo(Build.VERSION_CODES.S_V2)
public void testDropPacketToVpnAddress_WithoutDuplicatedAddress() throws Exception {
doTestDropPacketToVpnAddress(false /* duplicatedAddress */);
}
@Test @IgnoreUpTo(Build.VERSION_CODES.S_V2)
public void testDropPacketToVpnAddress_WithDuplicatedAddress() throws Exception {
doTestDropPacketToVpnAddress(true /* duplicatedAddress */);
}
private ByteBuffer buildIpv4UdpPacket(final Inet4Address dstAddr, final Inet4Address srcAddr,
final short dstPort, final short srcPort, final byte[] payload) throws IOException {
@ -2001,7 +2038,8 @@ public class VpnTest {
private void checkBlockUdp(
final FileDescriptor srcTunFd,
final FileDescriptor dstUdpFd,
final boolean ipv6,
final InetAddress dstAddress,
final InetAddress srcAddress,
final boolean expectBlock) throws Exception {
final Random random = new Random();
final byte[] sendData = new byte[100];
@ -2009,15 +2047,15 @@ public class VpnTest {
final short dstPort = (short) ((InetSocketAddress) Os.getsockname(dstUdpFd)).getPort();
ByteBuffer buf;
if (ipv6) {
if (dstAddress instanceof Inet6Address) {
buf = buildIpv6UdpPacket(
(Inet6Address) TEST_IP6_DST_ADDR.getAddress(),
(Inet6Address) TEST_IP6_SRC_ADDR.getAddress(),
(Inet6Address) dstAddress,
(Inet6Address) srcAddress,
dstPort, TEST_SRC_PORT, sendData);
} else {
buf = buildIpv4UdpPacket(
(Inet4Address) TEST_IP4_DST_ADDR.getAddress(),
(Inet4Address) TEST_IP4_SRC_ADDR.getAddress(),
(Inet4Address) dstAddress,
(Inet4Address) srcAddress,
dstPort, TEST_SRC_PORT, sendData);
}
@ -2043,8 +2081,10 @@ public class VpnTest {
final FileDescriptor srcTunFd,
final FileDescriptor dstUdpFd,
final boolean expectBlock) throws Exception {
checkBlockUdp(srcTunFd, dstUdpFd, false /* ipv6 */, expectBlock);
checkBlockUdp(srcTunFd, dstUdpFd, true /* ipv6 */, expectBlock);
checkBlockUdp(srcTunFd, dstUdpFd, TEST_IP4_DST_ADDR.getAddress(),
TEST_IP4_SRC_ADDR.getAddress(), expectBlock);
checkBlockUdp(srcTunFd, dstUdpFd, TEST_IP6_DST_ADDR.getAddress(),
TEST_IP6_SRC_ADDR.getAddress(), expectBlock);
}
private class DetailedBlockedStatusCallback extends TestableNetworkCallback {

View file

@ -171,4 +171,16 @@ public class HostsideVpnTests extends HostsideNetworkTestCase {
public void testSetVpnDefaultForUids() throws Exception {
runDeviceTests(TEST_PKG, TEST_PKG + ".VpnTest", "testSetVpnDefaultForUids");
}
@Test
public void testDropPacketToVpnAddress_WithoutDuplicatedAddress() throws Exception {
runDeviceTests(TEST_PKG, TEST_PKG + ".VpnTest",
"testDropPacketToVpnAddress_WithoutDuplicatedAddress");
}
@Test
public void testDropPacketToVpnAddress_WithDuplicatedAddress() throws Exception {
runDeviceTests(TEST_PKG, TEST_PKG + ".VpnTest",
"testDropPacketToVpnAddress_WithDuplicatedAddress");
}
}