diff options
author | Steven Laver <lavers@google.com> | 2019-05-08 06:18:42 -0700 |
---|---|---|
committer | Steven Laver <lavers@google.com> | 2019-05-08 06:18:42 -0700 |
commit | 4a30c150e58ec67edb92a7bec5ea48cebd6b90d4 (patch) | |
tree | 6d902794d7e59acd54f54ef235feb75becfb01e3 /tests/net/java/com/android/server/ConnectivityServiceTest.java | |
parent | a6c7c402af360214e509d15980957e74fc673f42 (diff) | |
parent | e4f4eabc31685c001c2b46d8fa87b443f4af5b21 (diff) |
Merge QP1A.190501.001
Change-Id: I0f9f887d6a33702a6988709d8296731cfdb8f73d
Diffstat (limited to 'tests/net/java/com/android/server/ConnectivityServiceTest.java')
-rw-r--r-- | tests/net/java/com/android/server/ConnectivityServiceTest.java | 335 |
1 files changed, 279 insertions, 56 deletions
diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java index 44380cce1c77..363ac9ce1d25 100644 --- a/tests/net/java/com/android/server/ConnectivityServiceTest.java +++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java @@ -16,6 +16,8 @@ package com.android.server; +import static android.content.pm.PackageManager.GET_PERMISSIONS; +import static android.content.pm.PackageManager.MATCH_ANY_USER; import static android.net.ConnectivityManager.CONNECTIVITY_ACTION; import static android.net.ConnectivityManager.NETID_UNSET; import static android.net.ConnectivityManager.PRIVATE_DNS_MODE_OFF; @@ -60,11 +62,13 @@ import static android.net.NetworkPolicyManager.RULE_ALLOW_METERED; import static android.net.NetworkPolicyManager.RULE_NONE; import static android.net.NetworkPolicyManager.RULE_REJECT_ALL; import static android.net.NetworkPolicyManager.RULE_REJECT_METERED; +import static android.net.RouteInfo.RTN_UNREACHABLE; import static com.android.internal.util.TestUtils.waitForIdleHandler; import static com.android.internal.util.TestUtils.waitForIdleLooper; import static com.android.internal.util.TestUtils.waitForIdleSerialExecutor; +import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotEquals; @@ -72,12 +76,14 @@ import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; +import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Matchers.anyInt; import static org.mockito.Mockito.any; import static org.mockito.Mockito.atLeastOnce; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.eq; +import static org.mockito.Mockito.inOrder; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.reset; @@ -97,6 +103,10 @@ import android.content.ContentResolver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageInfo; +import android.content.pm.PackageManager; +import android.content.pm.UserInfo; import android.content.res.Resources; import android.net.ConnectivityManager; import android.net.ConnectivityManager.NetworkCallback; @@ -131,6 +141,7 @@ import android.net.NetworkStackClient; import android.net.NetworkState; import android.net.NetworkUtils; import android.net.ProxyInfo; +import android.net.ResolverParamsParcel; import android.net.RouteInfo; import android.net.SocketKeepalive; import android.net.UidRange; @@ -138,6 +149,7 @@ import android.net.metrics.IpConnectivityLog; import android.net.shared.NetworkMonitorUtils; import android.net.shared.PrivateDnsConfig; import android.net.util.MultinetworkPolicyTracker; +import android.os.Binder; import android.os.ConditionVariable; import android.os.Handler; import android.os.HandlerThread; @@ -151,6 +163,7 @@ import android.os.Process; import android.os.RemoteException; import android.os.SystemClock; import android.os.UserHandle; +import android.os.UserManager; import android.provider.Settings; import android.system.Os; import android.test.mock.MockContentResolver; @@ -186,6 +199,7 @@ import org.junit.Ignore; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; +import org.mockito.InOrder; import org.mockito.Mock; import org.mockito.MockitoAnnotations; import org.mockito.Spy; @@ -194,6 +208,7 @@ import org.mockito.stubbing.Answer; import java.io.IOException; import java.net.DatagramSocket; import java.net.Inet4Address; +import java.net.Inet6Address; import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.Socket; @@ -261,8 +276,11 @@ public class ConnectivityServiceTest { @Mock IDnsResolver mMockDnsResolver; @Mock INetd mMockNetd; @Mock NetworkStackClient mNetworkStack; + @Mock PackageManager mPackageManager; + @Mock UserManager mUserManager; - private ArgumentCaptor<String[]> mStringArrayCaptor = ArgumentCaptor.forClass(String[].class); + private ArgumentCaptor<ResolverParamsParcel> mResolverParamsParcelCaptor = + ArgumentCaptor.forClass(ResolverParamsParcel.class); // This class exists to test bindProcessToNetwork and getBoundNetworkForProcess. These methods // do not go through ConnectivityService but talk to netd directly, so they don't automatically @@ -331,6 +349,7 @@ public class ConnectivityServiceTest { if (Context.CONNECTIVITY_SERVICE.equals(name)) return mCm; if (Context.NOTIFICATION_SERVICE.equals(name)) return mock(NotificationManager.class); if (Context.NETWORK_STACK_SERVICE.equals(name)) return mNetworkStack; + if (Context.USER_SERVICE.equals(name)) return mUserManager; return super.getSystemService(name); } @@ -343,7 +362,12 @@ public class ConnectivityServiceTest { public Resources getResources() { return mResources; } - } + + @Override + public PackageManager getPackageManager() { + return mPackageManager; + } + } public void waitForIdle(int timeoutMsAsInt) { long timeoutMs = timeoutMsAsInt; @@ -543,6 +567,16 @@ public class ConnectivityServiceTest { protected void preventAutomaticReconnect() { mPreventReconnectReceived.open(); } + + @Override + protected void addKeepalivePacketFilter(Message msg) { + Log.i(TAG, "Add keepalive packet filter."); + } + + @Override + protected void removeKeepalivePacketFilter(Message msg) { + Log.i(TAG, "Remove keepalive packet filter."); + } }; assertEquals(mNetworkAgent.netId, nmNetworkCaptor.getValue().netId); @@ -1057,7 +1091,7 @@ public class ConnectivityServiceTest { public WrappedConnectivityService(Context context, INetworkManagementService netManager, INetworkStatsService statsService, INetworkPolicyManager policyManager, IpConnectivityLog log, INetd netd, IDnsResolver dnsResolver) { - super(context, netManager, statsService, policyManager, dnsResolver, log); + super(context, netManager, statsService, policyManager, dnsResolver, log, netd); mNetd = netd; mLingerDelayMs = TEST_LINGER_DELAY_MS; } @@ -1196,6 +1230,11 @@ public class ConnectivityServiceTest { fail("ConditionVariable was blocked for more than " + TIMEOUT_MS + "ms"); } + private static final int VPN_USER = 0; + private static final int APP1_UID = UserHandle.getUid(VPN_USER, 10100); + private static final int APP2_UID = UserHandle.getUid(VPN_USER, 10101); + private static final int VPN_UID = UserHandle.getUid(VPN_USER, 10043); + @Before public void setUp() throws Exception { mContext = InstrumentationRegistry.getContext(); @@ -1203,11 +1242,17 @@ public class ConnectivityServiceTest { MockitoAnnotations.initMocks(this); when(mMetricsService.defaultNetworkMetrics()).thenReturn(mDefaultNetworkMetrics); + when(mUserManager.getUsers(eq(true))).thenReturn( + Arrays.asList(new UserInfo[] { + new UserInfo(VPN_USER, "", 0), + })); + // InstrumentationTestRunner prepares a looper, but AndroidJUnitRunner does not. // http://b/25897652 . if (Looper.myLooper() == null) { Looper.prepare(); } + mockDefaultPackages(); FakeSettingsProvider.clearSettingsProvider(); mServiceContext = new MockContext(InstrumentationRegistry.getContext(), @@ -1260,7 +1305,24 @@ public class ConnectivityServiceTest { FakeSettingsProvider.clearSettingsProvider(); } - private static int transportToLegacyType(int transport) { + private void mockDefaultPackages() throws Exception { + final String testPackageName = mContext.getPackageName(); + final PackageInfo testPackageInfo = mContext.getPackageManager().getPackageInfo( + testPackageName, PackageManager.GET_PERMISSIONS); + when(mPackageManager.getPackagesForUid(Binder.getCallingUid())).thenReturn( + new String[] {testPackageName}); + when(mPackageManager.getPackageInfoAsUser(eq(testPackageName), anyInt(), + eq(UserHandle.getCallingUserId()))).thenReturn(testPackageInfo); + + when(mPackageManager.getInstalledPackages(eq(GET_PERMISSIONS | MATCH_ANY_USER))).thenReturn( + Arrays.asList(new PackageInfo[] { + buildPackageInfo(/* SYSTEM */ false, APP1_UID), + buildPackageInfo(/* SYSTEM */ false, APP2_UID), + buildPackageInfo(/* SYSTEM */ false, VPN_UID) + })); + } + + private static int transportToLegacyType(int transport) { switch (transport) { case TRANSPORT_ETHERNET: return TYPE_ETHERNET; @@ -4819,16 +4881,13 @@ public class ConnectivityServiceTest { @Test public void testBasicDnsConfigurationPushed() throws Exception { setPrivateDnsSettings(PRIVATE_DNS_MODE_OPPORTUNISTIC, "ignored.example.com"); - ArgumentCaptor<String[]> tlsServers = ArgumentCaptor.forClass(String[].class); // Clear any interactions that occur as a result of CS starting up. reset(mMockDnsResolver); mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR); waitForIdle(); - verify(mMockDnsResolver, never()).setResolverConfiguration( - anyInt(), eq(EMPTY_STRING_ARRAY), any(), any(), eq(""), - eq(EMPTY_STRING_ARRAY), eq(EMPTY_STRING_ARRAY)); + verify(mMockDnsResolver, never()).setResolverConfiguration(any()); verifyNoMoreInteractions(mMockDnsResolver); final LinkProperties cellLp = new LinkProperties(); @@ -4845,36 +4904,37 @@ public class ConnectivityServiceTest { mCellNetworkAgent.sendLinkProperties(cellLp); mCellNetworkAgent.connect(false); waitForIdle(); - // CS tells netd about the empty DNS config for this network. - verify(mMockDnsResolver, atLeastOnce()).setResolverConfiguration( - anyInt(), eq(EMPTY_STRING_ARRAY), any(), any(), eq(""), - eq(EMPTY_STRING_ARRAY), eq(EMPTY_STRING_ARRAY)); + + verify(mMockDnsResolver, times(1)).createNetworkCache( + eq(mCellNetworkAgent.getNetwork().netId)); + // CS tells dnsresolver about the empty DNS config for this network. + verify(mMockDnsResolver, atLeastOnce()).setResolverConfiguration(any()); reset(mMockDnsResolver); cellLp.addDnsServer(InetAddress.getByName("2001:db8::1")); mCellNetworkAgent.sendLinkProperties(cellLp); waitForIdle(); verify(mMockDnsResolver, atLeastOnce()).setResolverConfiguration( - anyInt(), mStringArrayCaptor.capture(), any(), any(), - eq(""), tlsServers.capture(), eq(EMPTY_STRING_ARRAY)); - assertEquals(1, mStringArrayCaptor.getValue().length); - assertTrue(ArrayUtils.contains(mStringArrayCaptor.getValue(), "2001:db8::1")); + mResolverParamsParcelCaptor.capture()); + ResolverParamsParcel resolvrParams = mResolverParamsParcelCaptor.getValue(); + assertEquals(1, resolvrParams.servers.length); + assertTrue(ArrayUtils.contains(resolvrParams.servers, "2001:db8::1")); // Opportunistic mode. - assertTrue(ArrayUtils.contains(tlsServers.getValue(), "2001:db8::1")); + assertTrue(ArrayUtils.contains(resolvrParams.tlsServers, "2001:db8::1")); reset(mMockDnsResolver); cellLp.addDnsServer(InetAddress.getByName("192.0.2.1")); mCellNetworkAgent.sendLinkProperties(cellLp); waitForIdle(); verify(mMockDnsResolver, atLeastOnce()).setResolverConfiguration( - anyInt(), mStringArrayCaptor.capture(), any(), any(), - eq(""), tlsServers.capture(), eq(EMPTY_STRING_ARRAY)); - assertEquals(2, mStringArrayCaptor.getValue().length); - assertTrue(ArrayUtils.containsAll(mStringArrayCaptor.getValue(), + mResolverParamsParcelCaptor.capture()); + resolvrParams = mResolverParamsParcelCaptor.getValue(); + assertEquals(2, resolvrParams.servers.length); + assertTrue(ArrayUtils.containsAll(resolvrParams.servers, new String[]{"2001:db8::1", "192.0.2.1"})); // Opportunistic mode. - assertEquals(2, tlsServers.getValue().length); - assertTrue(ArrayUtils.containsAll(tlsServers.getValue(), + assertEquals(2, resolvrParams.tlsServers.length); + assertTrue(ArrayUtils.containsAll(resolvrParams.tlsServers, new String[]{"2001:db8::1", "192.0.2.1"})); reset(mMockDnsResolver); @@ -4887,18 +4947,16 @@ public class ConnectivityServiceTest { waitForIdle(); verify(mMockDnsResolver, atLeastOnce()).setResolverConfiguration( - anyInt(), mStringArrayCaptor.capture(), any(), any(), - eq(TLS_SPECIFIER), eq(TLS_SERVERS), eq(EMPTY_STRING_ARRAY)); - assertEquals(2, mStringArrayCaptor.getValue().length); - assertTrue(ArrayUtils.containsAll(mStringArrayCaptor.getValue(), + mResolverParamsParcelCaptor.capture()); + resolvrParams = mResolverParamsParcelCaptor.getValue(); + assertEquals(2, resolvrParams.servers.length); + assertTrue(ArrayUtils.containsAll(resolvrParams.servers, new String[]{"2001:db8::1", "192.0.2.1"})); reset(mMockDnsResolver); } @Test public void testPrivateDnsSettingsChange() throws Exception { - ArgumentCaptor<String[]> tlsServers = ArgumentCaptor.forClass(String[].class); - // Clear any interactions that occur as a result of CS starting up. reset(mMockDnsResolver); @@ -4913,9 +4971,7 @@ public class ConnectivityServiceTest { mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR); waitForIdle(); // CS tells netd about the empty DNS config for this network. - verify(mMockDnsResolver, never()).setResolverConfiguration( - anyInt(), eq(EMPTY_STRING_ARRAY), any(), any(), eq(""), - eq(EMPTY_STRING_ARRAY), eq(EMPTY_STRING_ARRAY)); + verify(mMockDnsResolver, never()).setResolverConfiguration(any()); verifyNoMoreInteractions(mMockDnsResolver); final LinkProperties cellLp = new LinkProperties(); @@ -4935,15 +4991,17 @@ public class ConnectivityServiceTest { mCellNetworkAgent.sendLinkProperties(cellLp); mCellNetworkAgent.connect(false); waitForIdle(); + verify(mMockDnsResolver, times(1)).createNetworkCache( + eq(mCellNetworkAgent.getNetwork().netId)); verify(mMockDnsResolver, atLeastOnce()).setResolverConfiguration( - anyInt(), mStringArrayCaptor.capture(), any(), any(), - eq(""), tlsServers.capture(), eq(EMPTY_STRING_ARRAY)); - assertEquals(2, mStringArrayCaptor.getValue().length); - assertTrue(ArrayUtils.containsAll(mStringArrayCaptor.getValue(), + mResolverParamsParcelCaptor.capture()); + ResolverParamsParcel resolvrParams = mResolverParamsParcelCaptor.getValue(); + assertEquals(2, resolvrParams.tlsServers.length); + assertTrue(ArrayUtils.containsAll(resolvrParams.tlsServers, new String[]{"2001:db8::1", "192.0.2.1"})); // Opportunistic mode. - assertEquals(2, tlsServers.getValue().length); - assertTrue(ArrayUtils.containsAll(tlsServers.getValue(), + assertEquals(2, resolvrParams.tlsServers.length); + assertTrue(ArrayUtils.containsAll(resolvrParams.tlsServers, new String[]{"2001:db8::1", "192.0.2.1"})); reset(mMockDnsResolver); cellNetworkCallback.expectCallback(CallbackState.AVAILABLE, mCellNetworkAgent); @@ -4958,23 +5016,23 @@ public class ConnectivityServiceTest { setPrivateDnsSettings(PRIVATE_DNS_MODE_OFF, "ignored.example.com"); verify(mMockDnsResolver, times(1)).setResolverConfiguration( - anyInt(), mStringArrayCaptor.capture(), any(), any(), - eq(""), eq(EMPTY_STRING_ARRAY), eq(EMPTY_STRING_ARRAY)); - assertEquals(2, mStringArrayCaptor.getValue().length); - assertTrue(ArrayUtils.containsAll(mStringArrayCaptor.getValue(), + mResolverParamsParcelCaptor.capture()); + resolvrParams = mResolverParamsParcelCaptor.getValue(); + assertEquals(2, resolvrParams.servers.length); + assertTrue(ArrayUtils.containsAll(resolvrParams.servers, new String[]{"2001:db8::1", "192.0.2.1"})); reset(mMockDnsResolver); cellNetworkCallback.assertNoCallback(); setPrivateDnsSettings(PRIVATE_DNS_MODE_OPPORTUNISTIC, "ignored.example.com"); verify(mMockDnsResolver, atLeastOnce()).setResolverConfiguration( - anyInt(), mStringArrayCaptor.capture(), any(), any(), - eq(""), tlsServers.capture(), eq(EMPTY_STRING_ARRAY)); - assertEquals(2, mStringArrayCaptor.getValue().length); - assertTrue(ArrayUtils.containsAll(mStringArrayCaptor.getValue(), + mResolverParamsParcelCaptor.capture()); + resolvrParams = mResolverParamsParcelCaptor.getValue(); + assertEquals(2, resolvrParams.servers.length); + assertTrue(ArrayUtils.containsAll(resolvrParams.servers, new String[]{"2001:db8::1", "192.0.2.1"})); - assertEquals(2, tlsServers.getValue().length); - assertTrue(ArrayUtils.containsAll(tlsServers.getValue(), + assertEquals(2, resolvrParams.tlsServers.length); + assertTrue(ArrayUtils.containsAll(resolvrParams.tlsServers, new String[]{"2001:db8::1", "192.0.2.1"})); reset(mMockDnsResolver); cellNetworkCallback.assertNoCallback(); @@ -5808,12 +5866,17 @@ public class ConnectivityServiceTest { cellLp.addRoute(new RouteInfo(myIpv6, null, MOBILE_IFNAME)); reset(mNetworkManagementService); reset(mMockDnsResolver); + reset(mMockNetd); when(mNetworkManagementService.getInterfaceConfig(CLAT_PREFIX + MOBILE_IFNAME)) .thenReturn(getClatInterfaceConfig(myIpv4)); // Connect with ipv6 link properties. Expect prefix discovery to be started. mCellNetworkAgent.sendLinkProperties(cellLp); mCellNetworkAgent.connect(true); + + verify(mMockNetd, times(1)).networkCreatePhysical(eq(cellNetId), anyInt()); + verify(mMockDnsResolver, times(1)).createNetworkCache(eq(cellNetId)); + networkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent); verify(mMockDnsResolver, times(1)).startPrefix64Discovery(cellNetId); @@ -5826,9 +5889,7 @@ public class ConnectivityServiceTest { mCellNetworkAgent.sendLinkProperties(cellLp); networkCallback.expectCallback(CallbackState.LINK_PROPERTIES, mCellNetworkAgent); verify(mMockDnsResolver, times(1)).stopPrefix64Discovery(cellNetId); - verify(mMockDnsResolver, atLeastOnce()).setResolverConfiguration( - eq(cellNetId), eq(EMPTY_STRING_ARRAY), any(), any(), - eq(""), eq(EMPTY_STRING_ARRAY), eq(EMPTY_STRING_ARRAY)); + verify(mMockDnsResolver, atLeastOnce()).setResolverConfiguration(any()); verifyNoMoreInteractions(mMockNetd); verifyNoMoreInteractions(mMockDnsResolver); @@ -5871,10 +5932,10 @@ public class ConnectivityServiceTest { assertEquals(makeClatLinkProperties(myIpv4), stackedLpsAfterChange.get(0)); verify(mMockDnsResolver, times(1)).setResolverConfiguration( - eq(cellNetId), mStringArrayCaptor.capture(), any(), any(), - eq(""), eq(EMPTY_STRING_ARRAY), eq(EMPTY_STRING_ARRAY)); - assertEquals(1, mStringArrayCaptor.getValue().length); - assertTrue(ArrayUtils.contains(mStringArrayCaptor.getValue(), "8.8.8.8")); + mResolverParamsParcelCaptor.capture()); + ResolverParamsParcel resolvrParams = mResolverParamsParcelCaptor.getValue(); + assertEquals(1, resolvrParams.servers.length); + assertTrue(ArrayUtils.contains(resolvrParams.servers, "8.8.8.8")); // Add ipv4 address, expect that clatd and prefix discovery are stopped and stacked // linkproperties are cleaned up. @@ -6007,7 +6068,7 @@ public class ConnectivityServiceTest { verify(mNetworkManagementService, times(0)).removeIdleTimer(eq(MOBILE_IFNAME)); verify(mMockNetd, times(1)).networkDestroy(eq(mCellNetworkAgent.getNetwork().netId)); verify(mMockDnsResolver, times(1)) - .clearResolverConfiguration(eq(mCellNetworkAgent.getNetwork().netId)); + .destroyNetworkCache(eq(mCellNetworkAgent.getNetwork().netId)); // Disconnect wifi ConditionVariable cv = waitForConnectivityBroadcasts(1); @@ -6129,4 +6190,166 @@ public class ConnectivityServiceTest { assertEquals(testProxyInfo, mService.getProxyForNetwork(mWiFiNetworkAgent.getNetwork())); assertEquals(testProxyInfo, mService.getProxyForNetwork(null)); } + + @Test + public void testFullyRoutedVpnResultsInInterfaceFilteringRules() throws Exception { + LinkProperties lp = new LinkProperties(); + lp.setInterfaceName("tun0"); + lp.addRoute(new RouteInfo(new IpPrefix(Inet4Address.ANY, 0), null)); + // The uid range needs to cover the test app so the network is visible to it. + final Set<UidRange> vpnRange = Collections.singleton(UidRange.createForUser(VPN_USER)); + final MockNetworkAgent vpnNetworkAgent = establishVpn(lp, VPN_UID, vpnRange); + + // Connected VPN should have interface rules set up. There are two expected invocations, + // one during VPN uid update, one during VPN LinkProperties update + ArgumentCaptor<int[]> uidCaptor = ArgumentCaptor.forClass(int[].class); + verify(mMockNetd, times(2)).firewallAddUidInterfaceRules(eq("tun0"), uidCaptor.capture()); + assertContainsExactly(uidCaptor.getAllValues().get(0), APP1_UID, APP2_UID); + assertContainsExactly(uidCaptor.getAllValues().get(1), APP1_UID, APP2_UID); + assertTrue(mService.mPermissionMonitor.getVpnUidRanges("tun0").equals(vpnRange)); + + vpnNetworkAgent.disconnect(); + waitForIdle(); + + // Disconnected VPN should have interface rules removed + verify(mMockNetd).firewallRemoveUidInterfaceRules(uidCaptor.capture()); + assertContainsExactly(uidCaptor.getValue(), APP1_UID, APP2_UID); + assertNull(mService.mPermissionMonitor.getVpnUidRanges("tun0")); + } + + @Test + public void testLegacyVpnDoesNotResultInInterfaceFilteringRule() throws Exception { + LinkProperties lp = new LinkProperties(); + lp.setInterfaceName("tun0"); + lp.addRoute(new RouteInfo(new IpPrefix(Inet4Address.ANY, 0), null)); + // The uid range needs to cover the test app so the network is visible to it. + final Set<UidRange> vpnRange = Collections.singleton(UidRange.createForUser(VPN_USER)); + final MockNetworkAgent vpnNetworkAgent = establishVpn(lp, Process.SYSTEM_UID, vpnRange); + + // Legacy VPN should not have interface rules set up + verify(mMockNetd, never()).firewallAddUidInterfaceRules(any(), any()); + } + + @Test + public void testLocalIpv4OnlyVpnDoesNotResultInInterfaceFilteringRule() + throws Exception { + LinkProperties lp = new LinkProperties(); + lp.setInterfaceName("tun0"); + lp.addRoute(new RouteInfo(new IpPrefix("192.0.2.0/24"), null, "tun0")); + lp.addRoute(new RouteInfo(new IpPrefix(Inet6Address.ANY, 0), RTN_UNREACHABLE)); + // The uid range needs to cover the test app so the network is visible to it. + final Set<UidRange> vpnRange = Collections.singleton(UidRange.createForUser(VPN_USER)); + final MockNetworkAgent vpnNetworkAgent = establishVpn(lp, Process.SYSTEM_UID, vpnRange); + + // IPv6 unreachable route should not be misinterpreted as a default route + verify(mMockNetd, never()).firewallAddUidInterfaceRules(any(), any()); + } + + @Test + public void testVpnHandoverChangesInterfaceFilteringRule() throws Exception { + LinkProperties lp = new LinkProperties(); + lp.setInterfaceName("tun0"); + lp.addRoute(new RouteInfo(new IpPrefix(Inet4Address.ANY, 0), null)); + // The uid range needs to cover the test app so the network is visible to it. + final Set<UidRange> vpnRange = Collections.singleton(UidRange.createForUser(VPN_USER)); + final MockNetworkAgent vpnNetworkAgent = establishVpn(lp, VPN_UID, vpnRange); + + // Connected VPN should have interface rules set up. There are two expected invocations, + // one during VPN uid update, one during VPN LinkProperties update + ArgumentCaptor<int[]> uidCaptor = ArgumentCaptor.forClass(int[].class); + verify(mMockNetd, times(2)).firewallAddUidInterfaceRules(eq("tun0"), uidCaptor.capture()); + assertContainsExactly(uidCaptor.getAllValues().get(0), APP1_UID, APP2_UID); + assertContainsExactly(uidCaptor.getAllValues().get(1), APP1_UID, APP2_UID); + + reset(mMockNetd); + InOrder inOrder = inOrder(mMockNetd); + lp.setInterfaceName("tun1"); + vpnNetworkAgent.sendLinkProperties(lp); + waitForIdle(); + // VPN handover (switch to a new interface) should result in rules being updated (old rules + // removed first, then new rules added) + inOrder.verify(mMockNetd).firewallRemoveUidInterfaceRules(uidCaptor.capture()); + assertContainsExactly(uidCaptor.getValue(), APP1_UID, APP2_UID); + inOrder.verify(mMockNetd).firewallAddUidInterfaceRules(eq("tun1"), uidCaptor.capture()); + assertContainsExactly(uidCaptor.getValue(), APP1_UID, APP2_UID); + + reset(mMockNetd); + lp = new LinkProperties(); + lp.setInterfaceName("tun1"); + lp.addRoute(new RouteInfo(new IpPrefix("192.0.2.0/24"), null, "tun1")); + vpnNetworkAgent.sendLinkProperties(lp); + waitForIdle(); + // VPN not routing everything should no longer have interface filtering rules + verify(mMockNetd).firewallRemoveUidInterfaceRules(uidCaptor.capture()); + assertContainsExactly(uidCaptor.getValue(), APP1_UID, APP2_UID); + + reset(mMockNetd); + lp = new LinkProperties(); + lp.setInterfaceName("tun1"); + lp.addRoute(new RouteInfo(new IpPrefix(Inet6Address.ANY, 0), null)); + vpnNetworkAgent.sendLinkProperties(lp); + waitForIdle(); + // Back to routing all IPv6 traffic should have filtering rules + verify(mMockNetd).firewallAddUidInterfaceRules(eq("tun1"), uidCaptor.capture()); + assertContainsExactly(uidCaptor.getValue(), APP1_UID, APP2_UID); + } + + @Test + public void testUidUpdateChangesInterfaceFilteringRule() throws Exception { + LinkProperties lp = new LinkProperties(); + lp.setInterfaceName("tun0"); + lp.addRoute(new RouteInfo(new IpPrefix(Inet6Address.ANY, 0), null)); + // The uid range needs to cover the test app so the network is visible to it. + final UidRange vpnRange = UidRange.createForUser(VPN_USER); + final MockNetworkAgent vpnNetworkAgent = establishVpn(lp, VPN_UID, + Collections.singleton(vpnRange)); + + reset(mMockNetd); + InOrder inOrder = inOrder(mMockNetd); + + // Update to new range which is old range minus APP1, i.e. only APP2 + final Set<UidRange> newRanges = new HashSet<>(Arrays.asList( + new UidRange(vpnRange.start, APP1_UID - 1), + new UidRange(APP1_UID + 1, vpnRange.stop))); + vpnNetworkAgent.setUids(newRanges); + waitForIdle(); + + ArgumentCaptor<int[]> uidCaptor = ArgumentCaptor.forClass(int[].class); + // Verify old rules are removed before new rules are added + inOrder.verify(mMockNetd).firewallRemoveUidInterfaceRules(uidCaptor.capture()); + assertContainsExactly(uidCaptor.getValue(), APP1_UID, APP2_UID); + inOrder.verify(mMockNetd).firewallAddUidInterfaceRules(eq("tun0"), uidCaptor.capture()); + assertContainsExactly(uidCaptor.getValue(), APP2_UID); + } + + + private MockNetworkAgent establishVpn(LinkProperties lp, int establishingUid, + Set<UidRange> vpnRange) { + final MockNetworkAgent vpnNetworkAgent = new MockNetworkAgent(TRANSPORT_VPN, lp); + vpnNetworkAgent.getNetworkCapabilities().setEstablishingVpnAppUid(establishingUid); + mMockVpn.setNetworkAgent(vpnNetworkAgent); + mMockVpn.connect(); + mMockVpn.setUids(vpnRange); + vpnNetworkAgent.connect(true); + waitForIdle(); + return vpnNetworkAgent; + } + + private void assertContainsExactly(int[] actual, int... expected) { + int[] sortedActual = Arrays.copyOf(actual, actual.length); + int[] sortedExpected = Arrays.copyOf(expected, expected.length); + Arrays.sort(sortedActual); + Arrays.sort(sortedExpected); + assertArrayEquals(sortedExpected, sortedActual); + } + + private static PackageInfo buildPackageInfo(boolean hasSystemPermission, int uid) { + final PackageInfo packageInfo = new PackageInfo(); + packageInfo.requestedPermissions = new String[0]; + packageInfo.applicationInfo = new ApplicationInfo(); + packageInfo.applicationInfo.privateFlags = 0; + packageInfo.applicationInfo.uid = UserHandle.getUid(UserHandle.USER_SYSTEM, + UserHandle.getAppId(uid)); + return packageInfo; + } } |