Merge "Add a test for starting the legacy VPN." am: 20c118c89d

Original change: https://android-review.googlesource.com/c/platform/frameworks/base/+/1372896

Change-Id: I31352ae32ddeba4a37cc37bdcf6d7568f63fd70a
This commit is contained in:
Chalard Jean
2020-08-06 13:10:20 +00:00
committed by Automerger Merge Worker

View File

@@ -30,6 +30,7 @@ import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
import static android.net.NetworkCapabilities.TRANSPORT_VPN; import static android.net.NetworkCapabilities.TRANSPORT_VPN;
import static android.net.NetworkCapabilities.TRANSPORT_WIFI; import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertTrue;
@@ -49,6 +50,7 @@ import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when; import static org.mockito.Mockito.when;
import android.annotation.NonNull;
import android.annotation.UserIdInt; import android.annotation.UserIdInt;
import android.app.AppOpsManager; import android.app.AppOpsManager;
import android.app.NotificationManager; import android.app.NotificationManager;
@@ -65,6 +67,7 @@ import android.net.InetAddresses;
import android.net.IpPrefix; import android.net.IpPrefix;
import android.net.IpSecManager; import android.net.IpSecManager;
import android.net.LinkProperties; import android.net.LinkProperties;
import android.net.LocalSocket;
import android.net.Network; import android.net.Network;
import android.net.NetworkCapabilities; import android.net.NetworkCapabilities;
import android.net.NetworkInfo.DetailedState; import android.net.NetworkInfo.DetailedState;
@@ -74,6 +77,7 @@ import android.net.VpnManager;
import android.net.VpnService; import android.net.VpnService;
import android.os.Build.VERSION_CODES; import android.os.Build.VERSION_CODES;
import android.os.Bundle; import android.os.Bundle;
import android.os.ConditionVariable;
import android.os.INetworkManagementService; import android.os.INetworkManagementService;
import android.os.Looper; import android.os.Looper;
import android.os.Process; import android.os.Process;
@@ -94,6 +98,7 @@ import com.android.internal.net.VpnProfile;
import com.android.server.IpSecService; import com.android.server.IpSecService;
import org.junit.Before; import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
import org.mockito.Answers; import org.mockito.Answers;
@@ -101,13 +106,20 @@ import org.mockito.InOrder;
import org.mockito.Mock; import org.mockito.Mock;
import org.mockito.MockitoAnnotations; import org.mockito.MockitoAnnotations;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.net.Inet4Address; import java.net.Inet4Address;
import java.net.InetAddress;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collections; import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;
import java.util.stream.Stream; import java.util.stream.Stream;
/** /**
@@ -133,7 +145,8 @@ public class VpnTest {
managedProfileA.profileGroupId = primaryUser.id; managedProfileA.profileGroupId = primaryUser.id;
} }
static final String TEST_VPN_PKG = "com.dummy.vpn"; static final String EGRESS_IFACE = "wlan0";
static final String TEST_VPN_PKG = "com.testvpn.vpn";
private static final String TEST_VPN_SERVER = "1.2.3.4"; private static final String TEST_VPN_SERVER = "1.2.3.4";
private static final String TEST_VPN_IDENTITY = "identity"; private static final String TEST_VPN_IDENTITY = "identity";
private static final byte[] TEST_VPN_PSK = "psk".getBytes(); private static final byte[] TEST_VPN_PSK = "psk".getBytes();
@@ -1012,31 +1025,191 @@ public class VpnTest {
// a subsequent CL. // a subsequent CL.
} }
@Test public Vpn startLegacyVpn(final VpnProfile vpnProfile) throws Exception {
public void testStartLegacyVpn() throws Exception {
final Vpn vpn = createVpn(primaryUser.id); final Vpn vpn = createVpn(primaryUser.id);
setMockedUsers(primaryUser); setMockedUsers(primaryUser);
// Dummy egress interface // Dummy egress interface
final String egressIface = "DUMMY0";
final LinkProperties lp = new LinkProperties(); final LinkProperties lp = new LinkProperties();
lp.setInterfaceName(egressIface); lp.setInterfaceName(EGRESS_IFACE);
final RouteInfo defaultRoute = new RouteInfo(new IpPrefix(Inet4Address.ANY, 0), final RouteInfo defaultRoute = new RouteInfo(new IpPrefix(Inet4Address.ANY, 0),
InetAddresses.parseNumericAddress("192.0.2.0"), egressIface); InetAddresses.parseNumericAddress("192.0.2.0"), EGRESS_IFACE);
lp.addRoute(defaultRoute); lp.addRoute(defaultRoute);
vpn.startLegacyVpn(mVpnProfile, mKeyStore, lp); vpn.startLegacyVpn(vpnProfile, mKeyStore, lp);
return vpn;
}
@Test
public void testStartPlatformVpn() throws Exception {
startLegacyVpn(mVpnProfile);
// TODO: Test the Ikev2VpnRunner started up properly. Relies on utility methods added in // TODO: Test the Ikev2VpnRunner started up properly. Relies on utility methods added in
// a subsequent CL. // a subsequent patch.
}
@Test
public void testStartRacoonNumericAddress() throws Exception {
startRacoon("1.2.3.4", "1.2.3.4");
}
@Test
@Ignore("b/158974172") // remove when the bug is fixed
public void testStartRacoonHostname() throws Exception {
startRacoon("hostname", "5.6.7.8"); // address returned by deps.resolve
}
public void startRacoon(final String serverAddr, final String expectedAddr)
throws Exception {
final ConditionVariable legacyRunnerReady = new ConditionVariable();
final VpnProfile profile = new VpnProfile("testProfile" /* key */);
profile.type = VpnProfile.TYPE_L2TP_IPSEC_PSK;
profile.name = "testProfileName";
profile.username = "userName";
profile.password = "thePassword";
profile.server = serverAddr;
profile.ipsecIdentifier = "id";
profile.ipsecSecret = "secret";
profile.l2tpSecret = "l2tpsecret";
when(mConnectivityManager.getAllNetworks())
.thenReturn(new Network[] { new Network(101) });
when(mConnectivityManager.registerNetworkAgent(any(), any(), any(), any(),
anyInt(), any(), anyInt())).thenAnswer(invocation -> {
// The runner has registered an agent and is now ready.
legacyRunnerReady.open();
return new Network(102);
});
final Vpn vpn = startLegacyVpn(profile);
final TestDeps deps = (TestDeps) vpn.mDeps;
try {
// udppsk and 1701 are the values for TYPE_L2TP_IPSEC_PSK
assertArrayEquals(
new String[] { EGRESS_IFACE, expectedAddr, "udppsk",
profile.ipsecIdentifier, profile.ipsecSecret, "1701" },
deps.racoonArgs.get(10, TimeUnit.SECONDS));
// literal values are hardcoded in Vpn.java for mtpd args
assertArrayEquals(
new String[] { EGRESS_IFACE, "l2tp", expectedAddr, "1701", profile.l2tpSecret,
"name", profile.username, "password", profile.password,
"linkname", "vpn", "refuse-eap", "nodefaultroute", "usepeerdns",
"idle", "1800", "mtu", "1400", "mru", "1400" },
deps.mtpdArgs.get(10, TimeUnit.SECONDS));
// Now wait for the runner to be ready before testing for the route.
legacyRunnerReady.block(10_000);
// In this test the expected address is always v4 so /32
final RouteInfo expectedRoute = new RouteInfo(new IpPrefix(expectedAddr + "/32"),
RouteInfo.RTN_THROW);
assertTrue("Routes lack the expected throw route (" + expectedRoute + ") : "
+ vpn.mConfig.routes,
vpn.mConfig.routes.contains(expectedRoute));
} finally {
// Now interrupt the thread, unblock the runner and clean up.
vpn.mVpnRunner.exitVpnRunner();
deps.getStateFile().delete(); // set to delete on exit, but this deletes it earlier
vpn.mVpnRunner.join(10_000); // wait for up to 10s for the runner to die and cleanup
}
}
private static final class TestDeps extends Vpn.Dependencies {
public final CompletableFuture<String[]> racoonArgs = new CompletableFuture();
public final CompletableFuture<String[]> mtpdArgs = new CompletableFuture();
public final File mStateFile;
private final HashMap<String, Boolean> mRunningServices = new HashMap<>();
TestDeps() {
try {
mStateFile = File.createTempFile("vpnTest", ".tmp");
mStateFile.deleteOnExit();
} catch (final IOException e) {
throw new RuntimeException(e);
}
}
@Override
public void startService(final String serviceName) {
mRunningServices.put(serviceName, true);
}
@Override
public void stopService(final String serviceName) {
mRunningServices.put(serviceName, false);
}
@Override
public boolean isServiceRunning(final String serviceName) {
return mRunningServices.getOrDefault(serviceName, false);
}
@Override
public boolean isServiceStopped(final String serviceName) {
return !isServiceRunning(serviceName);
}
@Override
public File getStateFile() {
return mStateFile;
}
@Override
public void sendArgumentsToDaemon(
final String daemon, final LocalSocket socket, final String[] arguments,
final Vpn.RetryScheduler interruptChecker) throws IOException {
if ("racoon".equals(daemon)) {
racoonArgs.complete(arguments);
} else if ("mtpd".equals(daemon)) {
writeStateFile(arguments);
mtpdArgs.complete(arguments);
} else {
throw new UnsupportedOperationException("Unsupported daemon : " + daemon);
}
}
private void writeStateFile(final String[] arguments) throws IOException {
mStateFile.delete();
mStateFile.createNewFile();
mStateFile.deleteOnExit();
final BufferedWriter writer = new BufferedWriter(
new FileWriter(mStateFile, false /* append */));
writer.write(EGRESS_IFACE);
writer.write("\n");
// addresses
writer.write("10.0.0.1/24\n");
// routes
writer.write("192.168.6.0/24\n");
// dns servers
writer.write("192.168.6.1\n");
// search domains
writer.write("vpn.searchdomains.com\n");
// endpoint - intentionally empty
writer.write("\n");
writer.flush();
writer.close();
}
@Override
@NonNull
public InetAddress resolve(final String endpoint) {
try {
// If a numeric IP address, return it.
return InetAddress.parseNumericAddress(endpoint);
} catch (IllegalArgumentException e) {
// Otherwise, return some token IP to test for.
return InetAddress.parseNumericAddress("5.6.7.8");
}
}
@Override
public boolean checkInterfacePresent(final Vpn vpn, final String iface) {
return true;
}
} }
/** /**
* Mock some methods of vpn object. * Mock some methods of vpn object.
*/ */
private Vpn createVpn(@UserIdInt int userId) { private Vpn createVpn(@UserIdInt int userId) {
return new Vpn(Looper.myLooper(), mContext, mNetService, return new Vpn(Looper.myLooper(), mContext, new TestDeps(), mNetService,
userId, mKeyStore, mSystemServices, mIkev2SessionCreator); userId, mKeyStore, mSystemServices, mIkev2SessionCreator);
} }