1 /* 2 * Copyright (C) 2014 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.cts.net.hostside; 18 19 import static android.Manifest.permission.MANAGE_TEST_NETWORKS; 20 import static android.Manifest.permission.NETWORK_SETTINGS; 21 import static android.Manifest.permission.READ_DEVICE_CONFIG; 22 import static android.Manifest.permission.WRITE_DEVICE_CONFIG; 23 import static android.Manifest.permission.WRITE_ALLOWLISTED_DEVICE_CONFIG; 24 import static android.content.Context.RECEIVER_EXPORTED; 25 import static android.content.pm.PackageManager.FEATURE_TELEPHONY; 26 import static android.content.pm.PackageManager.FEATURE_WIFI; 27 import static android.net.ConnectivityManager.BLOCKED_REASON_LOCKDOWN_VPN; 28 import static android.net.ConnectivityManager.BLOCKED_REASON_NONE; 29 import static android.net.ConnectivityManager.FIREWALL_CHAIN_BACKGROUND; 30 import static android.net.ConnectivityManager.TYPE_VPN; 31 import static android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED; 32 import static android.net.NetworkCapabilities.TRANSPORT_TEST; 33 import static android.net.NetworkCapabilities.TRANSPORT_VPN; 34 import static android.os.Process.INVALID_UID; 35 import static android.system.OsConstants.AF_INET; 36 import static android.system.OsConstants.AF_INET6; 37 import static android.system.OsConstants.ECONNABORTED; 38 import static android.system.OsConstants.IPPROTO_ICMP; 39 import static android.system.OsConstants.IPPROTO_ICMPV6; 40 import static android.system.OsConstants.IPPROTO_TCP; 41 import static android.system.OsConstants.POLLIN; 42 import static android.system.OsConstants.SOCK_DGRAM; 43 import static android.test.MoreAsserts.assertNotEqual; 44 45 import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation; 46 47 import static com.android.compatibility.common.util.SystemUtil.runWithShellPermissionIdentity; 48 import static com.android.cts.net.hostside.VpnTest.TestSocketKeepaliveCallback.CallbackType.ON_DATA_RECEIVED; 49 import static com.android.cts.net.hostside.VpnTest.TestSocketKeepaliveCallback.CallbackType.ON_ERROR; 50 import static com.android.cts.net.hostside.VpnTest.TestSocketKeepaliveCallback.CallbackType.ON_PAUSED; 51 import static com.android.cts.net.hostside.VpnTest.TestSocketKeepaliveCallback.CallbackType.ON_RESUMED; 52 import static com.android.cts.net.hostside.VpnTest.TestSocketKeepaliveCallback.CallbackType.ON_STARTED; 53 import static com.android.cts.net.hostside.VpnTest.TestSocketKeepaliveCallback.CallbackType.ON_STOPPED; 54 import static com.android.testutils.Cleanup.testAndCleanup; 55 import static com.android.testutils.RecorderCallback.CallbackEntry.BLOCKED_STATUS_INT; 56 import static com.android.testutils.TestPermissionUtil.runAsShell; 57 58 import static org.junit.Assert.assertEquals; 59 import static org.junit.Assert.assertFalse; 60 import static org.junit.Assert.assertNotNull; 61 import static org.junit.Assert.assertTrue; 62 import static org.junit.Assert.fail; 63 import static org.junit.Assume.assumeTrue; 64 65 import android.annotation.Nullable; 66 import android.app.Activity; 67 import android.app.DownloadManager; 68 import android.app.DownloadManager.Query; 69 import android.app.DownloadManager.Request; 70 import android.content.BroadcastReceiver; 71 import android.content.ContentResolver; 72 import android.content.Context; 73 import android.content.Intent; 74 import android.content.IntentFilter; 75 import android.content.pm.PackageManager; 76 import android.database.Cursor; 77 import android.net.ConnectivityManager; 78 import android.net.ConnectivityManager.NetworkCallback; 79 import android.net.InetAddresses; 80 import android.net.IpSecManager; 81 import android.net.LinkAddress; 82 import android.net.LinkProperties; 83 import android.net.Network; 84 import android.net.NetworkCapabilities; 85 import android.net.NetworkRequest; 86 import android.net.Proxy; 87 import android.net.ProxyInfo; 88 import android.net.SocketKeepalive; 89 import android.net.TestNetworkInterface; 90 import android.net.TestNetworkManager; 91 import android.net.TransportInfo; 92 import android.net.Uri; 93 import android.net.VpnManager; 94 import android.net.VpnService; 95 import android.net.VpnTransportInfo; 96 import android.net.cts.util.CtsNetUtils; 97 import android.net.util.KeepaliveUtils; 98 import android.net.wifi.WifiManager; 99 import android.os.Binder; 100 import android.os.Build; 101 import android.os.Handler; 102 import android.os.Looper; 103 import android.os.ParcelFileDescriptor; 104 import android.os.Process; 105 import android.os.SystemProperties; 106 import android.os.UserHandle; 107 import android.provider.DeviceConfig; 108 import android.provider.Settings; 109 import android.system.ErrnoException; 110 import android.system.Os; 111 import android.system.OsConstants; 112 import android.system.StructPollfd; 113 import android.test.MoreAsserts; 114 import android.text.TextUtils; 115 import android.util.ArraySet; 116 import android.util.Log; 117 import android.util.Range; 118 119 import androidx.test.ext.junit.runners.AndroidJUnit4; 120 import androidx.test.uiautomator.UiDevice; 121 import androidx.test.uiautomator.UiObject; 122 import androidx.test.uiautomator.UiSelector; 123 124 import com.android.compatibility.common.util.BlockingBroadcastReceiver; 125 import com.android.modules.utils.build.SdkLevel; 126 import com.android.net.module.util.ArrayTrackRecord; 127 import com.android.net.module.util.CollectionUtils; 128 import com.android.net.module.util.PacketBuilder; 129 import com.android.testutils.AutoReleaseNetworkCallbackRule; 130 import com.android.testutils.ConnectUtil; 131 import com.android.testutils.DevSdkIgnoreRule; 132 import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo; 133 import com.android.testutils.RecorderCallback; 134 import com.android.testutils.RecorderCallback.CallbackEntry; 135 import com.android.testutils.TestableNetworkCallback; 136 137 import org.junit.After; 138 import org.junit.Before; 139 import org.junit.Rule; 140 import org.junit.Test; 141 import org.junit.runner.RunWith; 142 143 import java.io.Closeable; 144 import java.io.FileDescriptor; 145 import java.io.IOException; 146 import java.io.InputStream; 147 import java.io.OutputStream; 148 import java.net.DatagramPacket; 149 import java.net.DatagramSocket; 150 import java.net.Inet4Address; 151 import java.net.Inet6Address; 152 import java.net.InetAddress; 153 import java.net.InetSocketAddress; 154 import java.net.ServerSocket; 155 import java.net.Socket; 156 import java.net.UnknownHostException; 157 import java.nio.ByteBuffer; 158 import java.nio.charset.StandardCharsets; 159 import java.util.ArrayList; 160 import java.util.List; 161 import java.util.Objects; 162 import java.util.Random; 163 import java.util.UUID; 164 import java.util.concurrent.CompletableFuture; 165 import java.util.concurrent.Executor; 166 import java.util.concurrent.TimeUnit; 167 168 /** 169 * Tests for the VpnService API. 170 * 171 * These tests establish a VPN via the VpnService API, and have the service reflect the packets back 172 * to the device without causing any network traffic. This allows testing the local VPN data path 173 * without a network connection or a VPN server. 174 * 175 * Note: in Lollipop, VPN functionality relies on kernel support for UID-based routing. If these 176 * tests fail, it may be due to the lack of kernel support. The necessary patches can be 177 * cherry-picked from the Android common kernel trees: 178 * 179 * android-3.10: 180 * https://android-review.googlesource.com/#/c/99220/ 181 * https://android-review.googlesource.com/#/c/100545/ 182 * 183 * android-3.4: 184 * https://android-review.googlesource.com/#/c/99225/ 185 * https://android-review.googlesource.com/#/c/100557/ 186 * 187 * To ensure that the kernel has the required commits, run the kernel unit 188 * tests described at: 189 * 190 * https://source.android.com/devices/tech/config/kernel_network_tests.html 191 * 192 */ 193 @RunWith(AndroidJUnit4.class) 194 public class VpnTest { 195 196 // These are neither public nor @TestApi. 197 // TODO: add them to @TestApi. 198 private static final String PRIVATE_DNS_MODE_SETTING = "private_dns_mode"; 199 private static final String PRIVATE_DNS_MODE_PROVIDER_HOSTNAME = "hostname"; 200 private static final String PRIVATE_DNS_MODE_OPPORTUNISTIC = "opportunistic"; 201 private static final String PRIVATE_DNS_SPECIFIER_SETTING = "private_dns_specifier"; 202 private static final int NETWORK_CALLBACK_TIMEOUT_MS = 30_000; 203 204 private static final LinkAddress TEST_IP4_DST_ADDR = new LinkAddress("198.51.100.1/24"); 205 private static final LinkAddress TEST_IP4_SRC_ADDR = new LinkAddress("198.51.100.2/24"); 206 private static final LinkAddress TEST_IP6_DST_ADDR = new LinkAddress("2001:db8:1:3::1/64"); 207 private static final LinkAddress TEST_IP6_SRC_ADDR = new LinkAddress("2001:db8:1:3::2/64"); 208 private static final short TEST_SRC_PORT = 5555; 209 210 public static String TAG = "VpnTest"; 211 public static int TIMEOUT_MS = 3 * 1000; 212 public static int SOCKET_TIMEOUT_MS = 100; 213 public static String TEST_HOST = "connectivitycheck.gstatic.com"; 214 215 private static final String AUTOMATIC_ON_OFF_KEEPALIVE_VERSION = 216 "automatic_on_off_keepalive_version"; 217 private static final String INGRESS_TO_VPN_ADDRESS_FILTERING = 218 "ingress_to_vpn_address_filtering"; 219 // Enabled since version 1 means it's always enabled because the version is always above 1 220 private static final String AUTOMATIC_ON_OFF_KEEPALIVE_ENABLED = "1"; 221 private static final long TEST_TCP_POLLING_TIMER_EXPIRED_PERIOD_MS = 60_000L; 222 223 private UiDevice mDevice; 224 private MyActivity mActivity; 225 private String mPackageName; 226 private ConnectivityManager mCM; 227 private WifiManager mWifiManager; 228 private RemoteSocketFactoryClient mRemoteSocketFactoryClient; 229 private CtsNetUtils mCtsNetUtils; 230 private ConnectUtil mConnectUtil; 231 private PackageManager mPackageManager; 232 private Context mTestContext; 233 private Context mTargetContext; 234 Network mNetwork; 235 final Object mLock = new Object(); 236 final Object mLockShutdown = new Object(); 237 238 private String mOldPrivateDnsMode; 239 private String mOldPrivateDnsSpecifier; 240 241 // The registered callbacks. 242 private List<NetworkCallback> mRegisteredCallbacks = new ArrayList<>(); 243 244 @Rule(order = 1) 245 public final DevSdkIgnoreRule mDevSdkIgnoreRule = new DevSdkIgnoreRule(); 246 247 @Rule(order = 2) 248 public final AutoReleaseNetworkCallbackRule 249 mNetworkCallbackRule = new AutoReleaseNetworkCallbackRule(); 250 supportedHardware()251 private boolean supportedHardware() { 252 final PackageManager pm = getInstrumentation().getContext().getPackageManager(); 253 return !pm.hasSystemFeature("android.hardware.type.watch"); 254 } 255 launchActivity(String packageName, Class<T> activityClass)256 public final <T extends Activity> T launchActivity(String packageName, Class<T> activityClass) { 257 final Intent intent = new Intent(Intent.ACTION_MAIN); 258 intent.setClassName(packageName, activityClass.getName()); 259 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 260 final T activity = (T) getInstrumentation().startActivitySync(intent); 261 getInstrumentation().waitForIdleSync(); 262 return activity; 263 } 264 265 @Before setUp()266 public void setUp() throws Exception { 267 mNetwork = null; 268 mTestContext = getInstrumentation().getContext(); 269 mTargetContext = getInstrumentation().getTargetContext(); 270 getInstrumentation() 271 .getUiAutomation() 272 .grantRuntimePermission( 273 "com.android.cts.net.hostside", 274 "android.permission.NEARBY_WIFI_DEVICES"); 275 storePrivateDnsSetting(); 276 mDevice = UiDevice.getInstance(getInstrumentation()); 277 mActivity = launchActivity(mTargetContext.getPackageName(), MyActivity.class); 278 mPackageName = mActivity.getPackageName(); 279 mCM = (ConnectivityManager) mActivity.getSystemService(Context.CONNECTIVITY_SERVICE); 280 mWifiManager = (WifiManager) mActivity.getSystemService(Context.WIFI_SERVICE); 281 mRemoteSocketFactoryClient = new RemoteSocketFactoryClient(mActivity); 282 mRemoteSocketFactoryClient.bind(); 283 mDevice.waitForIdle(); 284 mCtsNetUtils = new CtsNetUtils(mTestContext); 285 mConnectUtil = new ConnectUtil(mTestContext); 286 mPackageManager = mTestContext.getPackageManager(); 287 assumeTrue(supportedHardware()); 288 } 289 290 @After tearDown()291 public void tearDown() throws Exception { 292 restorePrivateDnsSetting(); 293 mRemoteSocketFactoryClient.unbind(); 294 Log.i(TAG, "Stopping VPN"); 295 stopVpn(); 296 unregisterRegisteredCallbacks(); 297 mActivity.finish(); 298 } 299 registerNetworkCallback(NetworkRequest request, NetworkCallback callback)300 private void registerNetworkCallback(NetworkRequest request, NetworkCallback callback) { 301 mCM.registerNetworkCallback(request, callback); 302 mRegisteredCallbacks.add(callback); 303 } 304 registerDefaultNetworkCallback(NetworkCallback callback)305 private void registerDefaultNetworkCallback(NetworkCallback callback) { 306 mCM.registerDefaultNetworkCallback(callback); 307 mRegisteredCallbacks.add(callback); 308 } 309 registerSystemDefaultNetworkCallback(NetworkCallback callback, Handler h)310 private void registerSystemDefaultNetworkCallback(NetworkCallback callback, Handler h) { 311 mCM.registerSystemDefaultNetworkCallback(callback, h); 312 mRegisteredCallbacks.add(callback); 313 } 314 registerDefaultNetworkCallbackForUid(int uid, NetworkCallback callback, Handler h)315 private void registerDefaultNetworkCallbackForUid(int uid, NetworkCallback callback, 316 Handler h) { 317 mCM.registerDefaultNetworkCallbackForUid(uid, callback, h); 318 mRegisteredCallbacks.add(callback); 319 } 320 unregisterRegisteredCallbacks()321 private void unregisterRegisteredCallbacks() { 322 for (NetworkCallback callback: mRegisteredCallbacks) { 323 mCM.unregisterNetworkCallback(callback); 324 } 325 } 326 prepareVpn()327 private void prepareVpn() throws Exception { 328 final int REQUEST_ID = 42; 329 330 // Attempt to prepare. 331 Log.i(TAG, "Preparing VPN"); 332 Intent intent = VpnService.prepare(mActivity); 333 334 if (intent != null) { 335 // Start the confirmation dialog and click OK. 336 mActivity.startActivityForResult(intent, REQUEST_ID); 337 mDevice.waitForIdle(); 338 339 String packageName = intent.getComponent().getPackageName(); 340 String resourceIdRegex = "android:id/button1$|button_start_vpn"; 341 final UiObject okButton = new UiObject(new UiSelector() 342 .className("android.widget.Button") 343 .packageName(packageName) 344 .resourceIdMatches(resourceIdRegex)); 345 if (okButton.waitForExists(TIMEOUT_MS) == false) { 346 mActivity.finishActivity(REQUEST_ID); 347 fail("VpnService.prepare returned an Intent for '" + intent.getComponent() + "' " + 348 "to display the VPN confirmation dialog, but this test could not find the " + 349 "button to allow the VPN application to connect. Please ensure that the " + 350 "component displays a button with a resource ID matching the regexp: '" + 351 resourceIdRegex + "'."); 352 } 353 354 // Click the button and wait for RESULT_OK. 355 okButton.click(); 356 try { 357 int result = mActivity.getResult(TIMEOUT_MS); 358 if (result != MyActivity.RESULT_OK) { 359 fail("The VPN confirmation dialog did not return RESULT_OK when clicking on " + 360 "the button matching the regular expression '" + resourceIdRegex + 361 "' of " + intent.getComponent() + "'. Please ensure that clicking on " + 362 "that button allows the VPN application to connect. " + 363 "Return value: " + result); 364 } 365 } catch (InterruptedException e) { 366 fail("VPN confirmation dialog did not return after " + TIMEOUT_MS + "ms"); 367 } 368 369 // Now we should be prepared. 370 intent = VpnService.prepare(mActivity); 371 if (intent != null) { 372 fail("VpnService.prepare returned non-null even after the VPN dialog " + 373 intent.getComponent() + "returned RESULT_OK."); 374 } 375 } 376 } 377 updateUnderlyingNetworks(@ullable ArrayList<Network> underlyingNetworks)378 private void updateUnderlyingNetworks(@Nullable ArrayList<Network> underlyingNetworks) 379 throws Exception { 380 final Intent intent = new Intent(mActivity, MyVpnService.class) 381 .putExtra(mPackageName + ".cmd", MyVpnService.CMD_UPDATE_UNDERLYING_NETWORKS) 382 .putParcelableArrayListExtra( 383 mPackageName + ".underlyingNetworks", underlyingNetworks); 384 mActivity.startService(intent); 385 } 386 establishVpn(String[] addresses, String[] routes, String[] excludedRoutes, String allowedApplications, String disallowedApplications, @Nullable ProxyInfo proxyInfo, @Nullable ArrayList<Network> underlyingNetworks, boolean isAlwaysMetered, boolean addRoutesByIpPrefix)387 private void establishVpn(String[] addresses, String[] routes, String[] excludedRoutes, 388 String allowedApplications, String disallowedApplications, 389 @Nullable ProxyInfo proxyInfo, @Nullable ArrayList<Network> underlyingNetworks, 390 boolean isAlwaysMetered, boolean addRoutesByIpPrefix) 391 throws Exception { 392 final Intent intent = new Intent(mActivity, MyVpnService.class) 393 .putExtra(mPackageName + ".cmd", MyVpnService.CMD_CONNECT) 394 .putExtra(mPackageName + ".addresses", TextUtils.join(",", addresses)) 395 .putExtra(mPackageName + ".routes", TextUtils.join(",", routes)) 396 .putExtra(mPackageName + ".excludedRoutes", TextUtils.join(",", excludedRoutes)) 397 .putExtra(mPackageName + ".allowedapplications", allowedApplications) 398 .putExtra(mPackageName + ".disallowedapplications", disallowedApplications) 399 .putExtra(mPackageName + ".httpProxy", proxyInfo) 400 .putParcelableArrayListExtra( 401 mPackageName + ".underlyingNetworks", underlyingNetworks) 402 .putExtra(mPackageName + ".isAlwaysMetered", isAlwaysMetered) 403 .putExtra(mPackageName + ".addRoutesByIpPrefix", addRoutesByIpPrefix); 404 mActivity.startService(intent); 405 } 406 407 // TODO: Consider replacing arguments with a Builder. startVpn( String[] addresses, String[] routes, String allowedApplications, String disallowedApplications, @Nullable ProxyInfo proxyInfo, @Nullable ArrayList<Network> underlyingNetworks, boolean isAlwaysMetered)408 private void startVpn( 409 String[] addresses, String[] routes, String allowedApplications, 410 String disallowedApplications, @Nullable ProxyInfo proxyInfo, 411 @Nullable ArrayList<Network> underlyingNetworks, boolean isAlwaysMetered) 412 throws Exception { 413 startVpn(addresses, routes, new String[0] /* excludedRoutes */, allowedApplications, 414 disallowedApplications, proxyInfo, underlyingNetworks, isAlwaysMetered); 415 } 416 startVpn( String[] addresses, String[] routes, String[] excludedRoutes, String allowedApplications, String disallowedApplications, @Nullable ProxyInfo proxyInfo, @Nullable ArrayList<Network> underlyingNetworks, boolean isAlwaysMetered)417 private void startVpn( 418 String[] addresses, String[] routes, String[] excludedRoutes, 419 String allowedApplications, String disallowedApplications, 420 @Nullable ProxyInfo proxyInfo, 421 @Nullable ArrayList<Network> underlyingNetworks, boolean isAlwaysMetered) 422 throws Exception { 423 startVpn(addresses, routes, excludedRoutes, allowedApplications, disallowedApplications, 424 proxyInfo, underlyingNetworks, isAlwaysMetered, false /* addRoutesByIpPrefix */); 425 } 426 startVpn( String[] addresses, String[] routes, String[] excludedRoutes, String allowedApplications, String disallowedApplications, @Nullable ProxyInfo proxyInfo, @Nullable ArrayList<Network> underlyingNetworks, boolean isAlwaysMetered, boolean addRoutesByIpPrefix)427 private void startVpn( 428 String[] addresses, String[] routes, String[] excludedRoutes, 429 String allowedApplications, String disallowedApplications, 430 @Nullable ProxyInfo proxyInfo, 431 @Nullable ArrayList<Network> underlyingNetworks, boolean isAlwaysMetered, 432 boolean addRoutesByIpPrefix) 433 throws Exception { 434 prepareVpn(); 435 436 // Register a callback so we will be notified when our VPN comes up. 437 final NetworkRequest request = new NetworkRequest.Builder() 438 .addTransportType(NetworkCapabilities.TRANSPORT_VPN) 439 .removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VPN) 440 .removeCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET) 441 .build(); 442 final NetworkCallback callback = new NetworkCallback() { 443 public void onAvailable(Network network) { 444 synchronized (mLock) { 445 Log.i(TAG, "Got available callback for network=" + network); 446 mNetwork = network; 447 mLock.notify(); 448 } 449 } 450 }; 451 registerNetworkCallback(request, callback); 452 453 // Start the service and wait up for TIMEOUT_MS ms for the VPN to come up. 454 establishVpn(addresses, routes, excludedRoutes, allowedApplications, disallowedApplications, 455 proxyInfo, underlyingNetworks, isAlwaysMetered, addRoutesByIpPrefix); 456 synchronized (mLock) { 457 if (mNetwork == null) { 458 Log.i(TAG, "bf mLock"); 459 mLock.wait(TIMEOUT_MS); 460 Log.i(TAG, "af mLock"); 461 } 462 } 463 464 if (mNetwork == null) { 465 fail("VPN did not become available after " + TIMEOUT_MS + "ms"); 466 } 467 } 468 stopVpn()469 private void stopVpn() { 470 // Register a callback so we will be notified when our VPN comes up. 471 final NetworkRequest request = new NetworkRequest.Builder() 472 .addTransportType(NetworkCapabilities.TRANSPORT_VPN) 473 .removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VPN) 474 .removeCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET) 475 .build(); 476 final NetworkCallback callback = new NetworkCallback() { 477 public void onLost(Network network) { 478 synchronized (mLockShutdown) { 479 Log.i(TAG, "Got lost callback for network=" + network 480 + ",mNetwork = " + mNetwork); 481 if( mNetwork == network){ 482 mLockShutdown.notify(); 483 } 484 } 485 } 486 }; 487 registerNetworkCallback(request, callback); 488 // Simply calling mActivity.stopService() won't stop the service, because the system binds 489 // to the service for the purpose of sending it a revoke command if another VPN comes up, 490 // and stopping a bound service has no effect. Instead, "start" the service again with an 491 // Intent that tells it to disconnect. 492 Intent intent = new Intent(mActivity, MyVpnService.class) 493 .putExtra(mPackageName + ".cmd", MyVpnService.CMD_DISCONNECT); 494 mActivity.startService(intent); 495 synchronized (mLockShutdown) { 496 try { 497 Log.i(TAG, "bf mLockShutdown"); 498 mLockShutdown.wait(TIMEOUT_MS); 499 Log.i(TAG, "af mLockShutdown"); 500 } catch(InterruptedException e) {} 501 } 502 } 503 closeQuietly(Closeable c)504 private static void closeQuietly(Closeable c) { 505 if (c != null) { 506 try { 507 c.close(); 508 } catch (IOException e) { 509 } 510 } 511 } 512 checkPing(String to)513 private static void checkPing(String to) throws IOException, ErrnoException { 514 InetAddress address = InetAddress.getByName(to); 515 FileDescriptor s; 516 final int LENGTH = 64; 517 byte[] packet = new byte[LENGTH]; 518 byte[] header; 519 520 // Construct a ping packet. 521 Random random = new Random(); 522 random.nextBytes(packet); 523 if (address instanceof Inet6Address) { 524 s = Os.socket(AF_INET6, SOCK_DGRAM, IPPROTO_ICMPV6); 525 header = new byte[] { (byte) 0x80, (byte) 0x00, (byte) 0x00, (byte) 0x00 }; 526 } else { 527 // Note that this doesn't actually work due to http://b/18558481 . 528 s = Os.socket(AF_INET, SOCK_DGRAM, IPPROTO_ICMP); 529 header = new byte[] { (byte) 0x08, (byte) 0x00, (byte) 0x00, (byte) 0x00 }; 530 } 531 System.arraycopy(header, 0, packet, 0, header.length); 532 533 // Send the packet. 534 int port = random.nextInt(65534) + 1; 535 Os.connect(s, address, port); 536 Os.write(s, packet, 0, packet.length); 537 538 // Expect a reply. 539 StructPollfd pollfd = new StructPollfd(); 540 pollfd.events = (short) POLLIN; // "error: possible loss of precision" 541 pollfd.fd = s; 542 int ret = Os.poll(new StructPollfd[] { pollfd }, SOCKET_TIMEOUT_MS); 543 assertEquals("Expected reply after sending ping", 1, ret); 544 545 byte[] reply = new byte[LENGTH]; 546 int read = Os.read(s, reply, 0, LENGTH); 547 assertEquals(LENGTH, read); 548 549 // Find out what the kernel set the ICMP ID to. 550 InetSocketAddress local = (InetSocketAddress) Os.getsockname(s); 551 port = local.getPort(); 552 packet[4] = (byte) ((port >> 8) & 0xff); 553 packet[5] = (byte) (port & 0xff); 554 555 // Check the contents. 556 if (packet[0] == (byte) 0x80) { 557 packet[0] = (byte) 0x81; 558 } else { 559 packet[0] = 0; 560 } 561 // Zero out the checksum in the reply so it matches the uninitialized checksum in packet. 562 reply[2] = reply[3] = 0; 563 MoreAsserts.assertEquals(packet, reply); 564 } 565 566 // Writes data to out and checks that it appears identically on in. writeAndCheckData( OutputStream out, InputStream in, byte[] data)567 private static void writeAndCheckData( 568 OutputStream out, InputStream in, byte[] data) throws IOException { 569 out.write(data, 0, data.length); 570 out.flush(); 571 572 byte[] read = new byte[data.length]; 573 int bytesRead = 0, totalRead = 0; 574 do { 575 bytesRead = in.read(read, totalRead, read.length - totalRead); 576 totalRead += bytesRead; 577 } while (bytesRead >= 0 && totalRead < data.length); 578 assertEquals(totalRead, data.length); 579 MoreAsserts.assertEquals(data, read); 580 } 581 checkTcpReflection(String to, String expectedFrom)582 private void checkTcpReflection(String to, String expectedFrom) throws IOException { 583 // Exercise TCP over the VPN by "connecting to ourselves". We open a server socket and a 584 // client socket, and connect the client socket to a remote host, with the port of the 585 // server socket. The PacketReflector reflects the packets, changing the source addresses 586 // but not the ports, so our client socket is connected to our server socket, though both 587 // sockets think their peers are on the "remote" IP address. 588 589 // Open a listening socket. 590 ServerSocket listen = new ServerSocket(0, 10, InetAddress.getByName("::")); 591 592 // Connect the client socket to it. 593 InetAddress toAddr = InetAddress.getByName(to); 594 Socket client = new Socket(); 595 try { 596 client.connect(new InetSocketAddress(toAddr, listen.getLocalPort()), SOCKET_TIMEOUT_MS); 597 if (expectedFrom == null) { 598 closeQuietly(listen); 599 closeQuietly(client); 600 fail("Expected connection to fail, but it succeeded."); 601 } 602 } catch (IOException e) { 603 if (expectedFrom != null) { 604 closeQuietly(listen); 605 fail("Expected connection to succeed, but it failed."); 606 } else { 607 // We expected the connection to fail, and it did, so there's nothing more to test. 608 return; 609 } 610 } 611 612 // The connection succeeded, and we expected it to succeed. Send some data; if things are 613 // working, the data will be sent to the VPN, reflected by the PacketReflector, and arrive 614 // at our server socket. For good measure, send some data in the other direction. 615 Socket server = null; 616 try { 617 // Accept the connection on the server side. 618 listen.setSoTimeout(SOCKET_TIMEOUT_MS); 619 server = listen.accept(); 620 checkConnectionOwnerUidTcp(client); 621 checkConnectionOwnerUidTcp(server); 622 // Check that the source and peer addresses are as expected. 623 assertEquals(expectedFrom, client.getLocalAddress().getHostAddress()); 624 assertEquals(expectedFrom, server.getLocalAddress().getHostAddress()); 625 assertEquals( 626 new InetSocketAddress(toAddr, client.getLocalPort()), 627 server.getRemoteSocketAddress()); 628 assertEquals( 629 new InetSocketAddress(toAddr, server.getLocalPort()), 630 client.getRemoteSocketAddress()); 631 632 // Now write some data. 633 final int LENGTH = 32768; 634 byte[] data = new byte[LENGTH]; 635 new Random().nextBytes(data); 636 637 // Make sure our writes don't block or time out, because we're single-threaded and can't 638 // read and write at the same time. 639 server.setReceiveBufferSize(LENGTH * 2); 640 client.setSendBufferSize(LENGTH * 2); 641 client.setSoTimeout(SOCKET_TIMEOUT_MS); 642 server.setSoTimeout(SOCKET_TIMEOUT_MS); 643 644 // Send some data from client to server, then from server to client. 645 writeAndCheckData(client.getOutputStream(), server.getInputStream(), data); 646 writeAndCheckData(server.getOutputStream(), client.getInputStream(), data); 647 } finally { 648 closeQuietly(listen); 649 closeQuietly(client); 650 closeQuietly(server); 651 } 652 } 653 checkConnectionOwnerUidUdp(DatagramSocket s, boolean expectSuccess)654 private void checkConnectionOwnerUidUdp(DatagramSocket s, boolean expectSuccess) { 655 final int expectedUid = expectSuccess ? Process.myUid() : INVALID_UID; 656 InetSocketAddress loc = new InetSocketAddress(s.getLocalAddress(), s.getLocalPort()); 657 InetSocketAddress rem = new InetSocketAddress(s.getInetAddress(), s.getPort()); 658 int uid = mCM.getConnectionOwnerUid(OsConstants.IPPROTO_UDP, loc, rem); 659 assertEquals(expectedUid, uid); 660 } 661 checkConnectionOwnerUidTcp(Socket s)662 private void checkConnectionOwnerUidTcp(Socket s) { 663 final int expectedUid = Process.myUid(); 664 InetSocketAddress loc = new InetSocketAddress(s.getLocalAddress(), s.getLocalPort()); 665 InetSocketAddress rem = new InetSocketAddress(s.getInetAddress(), s.getPort()); 666 int uid = mCM.getConnectionOwnerUid(OsConstants.IPPROTO_TCP, loc, rem); 667 assertEquals(expectedUid, uid); 668 } 669 checkUdpEcho(String to, String expectedFrom)670 private void checkUdpEcho(String to, String expectedFrom) throws IOException { 671 checkUdpEcho(to, expectedFrom, expectedFrom != null); 672 } 673 checkUdpEcho(String to, String expectedFrom, boolean expectConnectionOwnerIsVisible)674 private void checkUdpEcho(String to, String expectedFrom, 675 boolean expectConnectionOwnerIsVisible) 676 throws IOException { 677 DatagramSocket s; 678 InetAddress address = InetAddress.getByName(to); 679 if (address instanceof Inet6Address) { // http://b/18094870 680 s = new DatagramSocket(0, InetAddress.getByName("::")); 681 } else { 682 s = new DatagramSocket(); 683 } 684 s.setSoTimeout(SOCKET_TIMEOUT_MS); 685 686 Random random = new Random(); 687 byte[] data = new byte[random.nextInt(1650)]; 688 random.nextBytes(data); 689 DatagramPacket p = new DatagramPacket(data, data.length); 690 s.connect(address, 7); 691 692 if (expectedFrom != null) { 693 assertEquals("Unexpected source address: ", 694 expectedFrom, s.getLocalAddress().getHostAddress()); 695 } 696 697 try { 698 if (expectedFrom != null) { 699 s.send(p); 700 checkConnectionOwnerUidUdp(s, expectConnectionOwnerIsVisible); 701 s.receive(p); 702 MoreAsserts.assertEquals(data, p.getData()); 703 } else { 704 try { 705 s.send(p); 706 s.receive(p); 707 fail("Received unexpected reply"); 708 } catch (IOException expected) { 709 checkConnectionOwnerUidUdp(s, expectConnectionOwnerIsVisible); 710 } 711 } 712 } finally { 713 s.close(); 714 } 715 } 716 checkTrafficOnVpn(String destination)717 private void checkTrafficOnVpn(String destination) throws Exception { 718 final InetAddress address = InetAddress.getByName(destination); 719 720 if (address instanceof Inet6Address) { 721 checkUdpEcho(destination, "2001:db8:1:2::ffe"); 722 checkPing(destination); 723 checkTcpReflection(destination, "2001:db8:1:2::ffe"); 724 } else { 725 checkUdpEcho(destination, "192.0.2.2"); 726 checkTcpReflection(destination, "192.0.2.2"); 727 } 728 729 } 730 checkNoTrafficOnVpn(String destination)731 private void checkNoTrafficOnVpn(String destination) throws IOException { 732 checkUdpEcho(destination, null); 733 checkTcpReflection(destination, null); 734 } 735 checkTrafficOnVpn()736 private void checkTrafficOnVpn() throws Exception { 737 checkTrafficOnVpn("192.0.2.251"); 738 checkTrafficOnVpn("2001:db8:dead:beef::f00"); 739 } 740 checkNoTrafficOnVpn()741 private void checkNoTrafficOnVpn() throws Exception { 742 checkNoTrafficOnVpn("192.0.2.251"); 743 checkNoTrafficOnVpn("2001:db8:dead:beef::f00"); 744 } 745 checkTrafficBypassesVpn(String destination)746 private void checkTrafficBypassesVpn(String destination) throws Exception { 747 checkUdpEcho(destination, null, true /* expectVpnOwnedConnection */); 748 checkTcpReflection(destination, null); 749 } 750 openSocketFd(String host, int port, int timeoutMs)751 private FileDescriptor openSocketFd(String host, int port, int timeoutMs) throws Exception { 752 Socket s = new Socket(host, port); 753 s.setSoTimeout(timeoutMs); 754 // Dup the filedescriptor so ParcelFileDescriptor's finalizer doesn't garbage collect it 755 // and cause our fd to become invalid. http://b/35927643 . 756 FileDescriptor fd = Os.dup(ParcelFileDescriptor.fromSocket(s).getFileDescriptor()); 757 s.close(); 758 return fd; 759 } 760 openSocketFdInOtherApp( String host, int port, int timeoutMs)761 private FileDescriptor openSocketFdInOtherApp( 762 String host, int port, int timeoutMs) throws Exception { 763 Log.d(TAG, String.format("Creating test socket in UID=%d, my UID=%d", 764 mRemoteSocketFactoryClient.getUid(), Os.getuid())); 765 FileDescriptor fd = mRemoteSocketFactoryClient.openSocketFd(host, port, TIMEOUT_MS); 766 return fd; 767 } 768 sendRequest(FileDescriptor fd, String host)769 private void sendRequest(FileDescriptor fd, String host) throws Exception { 770 String request = "GET /generate_204 HTTP/1.1\r\n" + 771 "Host: " + host + "\r\n" + 772 "Connection: keep-alive\r\n\r\n"; 773 byte[] requestBytes = request.getBytes(StandardCharsets.UTF_8); 774 int ret = Os.write(fd, requestBytes, 0, requestBytes.length); 775 Log.d(TAG, "Wrote " + ret + "bytes"); 776 777 String expected = "HTTP/1.1 204 No Content\r\n"; 778 byte[] response = new byte[expected.length()]; 779 Os.read(fd, response, 0, response.length); 780 781 String actual = new String(response, StandardCharsets.UTF_8); 782 assertEquals(expected, actual); 783 Log.d(TAG, "Got response: " + actual); 784 } 785 assertSocketStillOpen(FileDescriptor fd, String host)786 private void assertSocketStillOpen(FileDescriptor fd, String host) throws Exception { 787 try { 788 assertTrue(fd.valid()); 789 sendRequest(fd, host); 790 assertTrue(fd.valid()); 791 } finally { 792 Os.close(fd); 793 } 794 } 795 assertSocketClosed(FileDescriptor fd, String host)796 private void assertSocketClosed(FileDescriptor fd, String host) throws Exception { 797 try { 798 assertTrue(fd.valid()); 799 sendRequest(fd, host); 800 fail("Socket opened before VPN connects should be closed when VPN connects"); 801 } catch (ErrnoException expected) { 802 assertEquals(ECONNABORTED, expected.errno); 803 assertTrue(fd.valid()); 804 } finally { 805 Os.close(fd); 806 } 807 } 808 getContentResolver()809 private ContentResolver getContentResolver() { 810 return mTestContext.getContentResolver(); 811 } 812 isPrivateDnsInStrictMode()813 private boolean isPrivateDnsInStrictMode() { 814 return PRIVATE_DNS_MODE_PROVIDER_HOSTNAME.equals( 815 Settings.Global.getString(getContentResolver(), PRIVATE_DNS_MODE_SETTING)); 816 } 817 storePrivateDnsSetting()818 private void storePrivateDnsSetting() { 819 mOldPrivateDnsMode = Settings.Global.getString(getContentResolver(), 820 PRIVATE_DNS_MODE_SETTING); 821 mOldPrivateDnsSpecifier = Settings.Global.getString(getContentResolver(), 822 PRIVATE_DNS_SPECIFIER_SETTING); 823 } 824 restorePrivateDnsSetting()825 private void restorePrivateDnsSetting() { 826 Settings.Global.putString(getContentResolver(), PRIVATE_DNS_MODE_SETTING, 827 mOldPrivateDnsMode); 828 Settings.Global.putString(getContentResolver(), PRIVATE_DNS_SPECIFIER_SETTING, 829 mOldPrivateDnsSpecifier); 830 } 831 expectPrivateDnsHostname(final String hostname)832 private void expectPrivateDnsHostname(final String hostname) throws Exception { 833 for (Network network : mCtsNetUtils.getTestableNetworks()) { 834 // Wait for private DNS setting to propagate. 835 mCtsNetUtils.awaitPrivateDnsSetting("Test wait private DNS setting timeout", 836 network, hostname, false); 837 } 838 } 839 setAndVerifyPrivateDns(boolean strictMode)840 private void setAndVerifyPrivateDns(boolean strictMode) throws Exception { 841 final ContentResolver cr = mTestContext.getContentResolver(); 842 String privateDnsHostname; 843 844 if (strictMode) { 845 privateDnsHostname = "vpncts-nx.metric.gstatic.com"; 846 Settings.Global.putString(cr, PRIVATE_DNS_SPECIFIER_SETTING, privateDnsHostname); 847 Settings.Global.putString(cr, PRIVATE_DNS_MODE_SETTING, 848 PRIVATE_DNS_MODE_PROVIDER_HOSTNAME); 849 } else { 850 Settings.Global.putString(cr, PRIVATE_DNS_MODE_SETTING, PRIVATE_DNS_MODE_OPPORTUNISTIC); 851 privateDnsHostname = null; 852 } 853 854 expectPrivateDnsHostname(privateDnsHostname); 855 856 String randomName = "vpncts-" + new Random().nextInt(1000000000) + "-ds.metric.gstatic.com"; 857 if (strictMode) { 858 // Strict mode private DNS is enabled. DNS lookups should fail, because the private DNS 859 // server name is invalid. 860 try { 861 InetAddress.getByName(randomName); 862 fail("VPN DNS lookup should fail with private DNS enabled"); 863 } catch (UnknownHostException expected) { 864 } 865 } else { 866 // Strict mode private DNS is disabled. DNS lookup should succeed, because the VPN 867 // provides no DNS servers, and thus DNS falls through to the default network. 868 assertNotNull("VPN DNS lookup should succeed with private DNS disabled", 869 InetAddress.getByName(randomName)); 870 } 871 } 872 873 // Tests that strict mode private DNS is used on VPNs. checkStrictModePrivateDns()874 private void checkStrictModePrivateDns() throws Exception { 875 final boolean initialMode = isPrivateDnsInStrictMode(); 876 setAndVerifyPrivateDns(!initialMode); 877 setAndVerifyPrivateDns(initialMode); 878 } 879 makeVpnNetworkRequest()880 private NetworkRequest makeVpnNetworkRequest() { 881 return new NetworkRequest.Builder() 882 .addTransportType(NetworkCapabilities.TRANSPORT_VPN) 883 .removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VPN) 884 .build(); 885 } 886 expectUnderlyingNetworks(TestableNetworkCallback callback, @Nullable List<Network> expectUnderlyingNetworks)887 private void expectUnderlyingNetworks(TestableNetworkCallback callback, 888 @Nullable List<Network> expectUnderlyingNetworks) { 889 callback.eventuallyExpect(RecorderCallback.CallbackEntry.NETWORK_CAPS_UPDATED, 890 NETWORK_CALLBACK_TIMEOUT_MS, 891 entry -> (Objects.equals(expectUnderlyingNetworks, 892 entry.getCaps().getUnderlyingNetworks()))); 893 } 894 expectVpnNetwork(TestableNetworkCallback callback)895 private void expectVpnNetwork(TestableNetworkCallback callback) { 896 callback.eventuallyExpect(RecorderCallback.CallbackEntry.NETWORK_CAPS_UPDATED, 897 NETWORK_CALLBACK_TIMEOUT_MS, 898 entry -> entry.getCaps().hasTransport(TRANSPORT_VPN)); 899 } 900 901 @Test @IgnoreUpTo(Build.VERSION_CODES.S_V2) testChangeUnderlyingNetworks()902 public void testChangeUnderlyingNetworks() throws Exception { 903 assumeTrue(mPackageManager.hasSystemFeature(FEATURE_WIFI)); 904 assumeTrue(mPackageManager.hasSystemFeature(FEATURE_TELEPHONY)); 905 final TestableNetworkCallback callback = new TestableNetworkCallback(); 906 final boolean isWifiEnabled = mWifiManager.isWifiEnabled(); 907 testAndCleanup(() -> { 908 // Ensure both of wifi and mobile data are connected. 909 final Network wifiNetwork = mConnectUtil.ensureWifiValidated(); 910 final Network cellNetwork = mNetworkCallbackRule.requestCell(); 911 // Store current default network. 912 final Network defaultNetwork = mCM.getActiveNetwork(); 913 // Start VPN and set empty array as its underlying networks. 914 startVpn(new String[] {"192.0.2.2/32", "2001:db8:1:2::ffe/128"} /* addresses */, 915 new String[] {"0.0.0.0/0", "::/0"} /* routes */, 916 "" /* allowedApplications */, "" /* disallowedApplications */, 917 null /* proxyInfo */, new ArrayList<>() /* underlyingNetworks */, 918 false /* isAlwaysMetered */); 919 // Acquire the NETWORK_SETTINGS permission for getting the underlying networks. 920 runWithShellPermissionIdentity(() -> { 921 registerNetworkCallback(makeVpnNetworkRequest(), callback); 922 // Check that this VPN doesn't have any underlying networks. 923 expectUnderlyingNetworks(callback, new ArrayList<Network>()); 924 925 // Update the underlying networks to null and the underlying networks should follow 926 // the system default network. 927 updateUnderlyingNetworks(null); 928 expectUnderlyingNetworks(callback, List.of(defaultNetwork)); 929 930 // Update the underlying networks to mobile data. 931 updateUnderlyingNetworks(new ArrayList<>(List.of(cellNetwork))); 932 // Check the underlying networks of NetworkCapabilities which comes from 933 // onCapabilitiesChanged is mobile data. 934 expectUnderlyingNetworks(callback, List.of(cellNetwork)); 935 936 // Update the underlying networks to wifi. 937 updateUnderlyingNetworks(new ArrayList<>(List.of(wifiNetwork))); 938 // Check the underlying networks of NetworkCapabilities which comes from 939 // onCapabilitiesChanged is wifi. 940 expectUnderlyingNetworks(callback, List.of(wifiNetwork)); 941 942 // Update the underlying networks to wifi and mobile data. 943 updateUnderlyingNetworks(new ArrayList<>(List.of(wifiNetwork, cellNetwork))); 944 // Check the underlying networks of NetworkCapabilities which comes from 945 // onCapabilitiesChanged is wifi and mobile data. 946 expectUnderlyingNetworks(callback, List.of(wifiNetwork, cellNetwork)); 947 }, NETWORK_SETTINGS); 948 }, () -> { 949 if (isWifiEnabled) { 950 mCtsNetUtils.ensureWifiConnected(); 951 } else { 952 mCtsNetUtils.ensureWifiDisconnected(null); 953 } 954 }); 955 } 956 957 @Test testDefault()958 public void testDefault() throws Exception { 959 if (!SdkLevel.isAtLeastS() && ( 960 SystemProperties.getInt("persist.adb.tcp.port", -1) > -1 961 || SystemProperties.getInt("service.adb.tcp.port", -1) > -1)) { 962 // If adb TCP port opened, this test may running by adb over network. 963 // All of socket would be destroyed in this test. So this test don't 964 // support adb over network, see b/119382723. 965 // This is fixed in S, but still affects previous Android versions, 966 // and this test must be backwards compatible. 967 // TODO: Delete this code entirely when R is no longer supported. 968 Log.i(TAG, "adb is running over the network, so skip this test"); 969 return; 970 } 971 972 final BlockingBroadcastReceiver receiver = new BlockingBroadcastReceiver( 973 mTargetContext, MyVpnService.ACTION_ESTABLISHED); 974 receiver.register(); 975 976 // Test the behaviour of a variety of types of network callbacks. 977 final Network defaultNetwork = mCM.getActiveNetwork(); 978 final TestableNetworkCallback systemDefaultCallback = new TestableNetworkCallback(); 979 final TestableNetworkCallback otherUidCallback = new TestableNetworkCallback(); 980 final TestableNetworkCallback myUidCallback = new TestableNetworkCallback(); 981 if (SdkLevel.isAtLeastS()) { 982 // Using the same appId with the test to make sure otherUid has the internet permission. 983 // This works because the UID permission map only stores the app ID and not the whole 984 // UID. If the otherUid does not have the internet permission, network access from 985 // otherUid could be considered blocked on V+. 986 final int appId = UserHandle.getAppId(Process.myUid()); 987 final int otherUid = UserHandle.of(5 /* userId */).getUid(appId); 988 final Handler h = new Handler(Looper.getMainLooper()); 989 runWithShellPermissionIdentity(() -> { 990 registerSystemDefaultNetworkCallback(systemDefaultCallback, h); 991 registerDefaultNetworkCallbackForUid(otherUid, otherUidCallback, h); 992 registerDefaultNetworkCallbackForUid(Process.myUid(), myUidCallback, h); 993 }, NETWORK_SETTINGS); 994 for (TestableNetworkCallback callback : List.of(systemDefaultCallback, myUidCallback)) { 995 callback.expectAvailableCallbacks(defaultNetwork, false /* suspended */, 996 true /* validated */, false /* blocked */, TIMEOUT_MS); 997 } 998 // On V+, ConnectivityService generates blockedReasons based on bpf map contents even if 999 // the otherUid does not exist on device. So if the background chain is enabled, 1000 // otherUid is blocked. 1001 final boolean isOtherUidBlocked = SdkLevel.isAtLeastV() 1002 && runAsShell(NETWORK_SETTINGS, () -> mCM.getFirewallChainEnabled( 1003 FIREWALL_CHAIN_BACKGROUND)); 1004 otherUidCallback.expectAvailableCallbacks(defaultNetwork, false /* suspended */, 1005 true /* validated */, isOtherUidBlocked, TIMEOUT_MS); 1006 } else { 1007 // R does not have per-UID callback or system default callback APIs, and sends an 1008 // additional CAP_CHANGED callback. 1009 registerDefaultNetworkCallback(myUidCallback); 1010 myUidCallback.expectAvailableCallbacks(defaultNetwork, false /* suspended */, 1011 true /* validated */, false /* blocked */, TIMEOUT_MS); 1012 myUidCallback.expect(CallbackEntry.NETWORK_CAPS_UPDATED, defaultNetwork); 1013 } 1014 1015 FileDescriptor fd = openSocketFdInOtherApp(TEST_HOST, 80, TIMEOUT_MS); 1016 1017 startVpn(new String[] {"192.0.2.2/32", "2001:db8:1:2::ffe/128"}, 1018 new String[] {"0.0.0.0/0", "::/0"}, 1019 "", "", null, null /* underlyingNetworks */, false /* isAlwaysMetered */); 1020 1021 final Intent intent = receiver.awaitForBroadcast(TimeUnit.MINUTES.toMillis(1)); 1022 assertNotNull("Failed to receive broadcast from VPN service", intent); 1023 assertFalse("Wrong VpnService#isAlwaysOn", 1024 intent.getBooleanExtra(MyVpnService.EXTRA_ALWAYS_ON, true)); 1025 assertFalse("Wrong VpnService#isLockdownEnabled", 1026 intent.getBooleanExtra(MyVpnService.EXTRA_LOCKDOWN_ENABLED, true)); 1027 1028 assertSocketClosed(fd, TEST_HOST); 1029 1030 checkTrafficOnVpn(); 1031 1032 final Network vpnNetwork = mCM.getActiveNetwork(); 1033 myUidCallback.eventuallyExpect(CallbackEntry.NETWORK_CAPS_UPDATED, 1034 NETWORK_CALLBACK_TIMEOUT_MS, 1035 entry -> entry.getNetwork().equals(vpnNetwork) 1036 && entry.getCaps().hasCapability(NET_CAPABILITY_VALIDATED)); 1037 assertEquals(vpnNetwork, mCM.getActiveNetwork()); 1038 assertNotEqual(defaultNetwork, vpnNetwork); 1039 maybeExpectVpnTransportInfo(vpnNetwork); 1040 assertEquals(TYPE_VPN, mCM.getNetworkInfo(vpnNetwork).getType()); 1041 1042 if (SdkLevel.isAtLeastT()) { 1043 runWithShellPermissionIdentity(() -> { 1044 final NetworkCapabilities nc = mCM.getNetworkCapabilities(vpnNetwork); 1045 assertNotNull(nc); 1046 assertNotNull(nc.getUnderlyingNetworks()); 1047 assertEquals(defaultNetwork, new ArrayList<>(nc.getUnderlyingNetworks()).get(0)); 1048 }, NETWORK_SETTINGS); 1049 } 1050 1051 if (SdkLevel.isAtLeastS()) { 1052 // Check that system default network callback has not seen any network changes, even 1053 // though the app's default network changed. Also check that otherUidCallback saw no 1054 // network changes, because otherUid is in a different user and not subject to the VPN. 1055 // This needs to be done before testing private DNS because checkStrictModePrivateDns 1056 // will set the private DNS server to a nonexistent name, which will cause validation to 1057 // fail and could cause the default network to switch (e.g., from wifi to cellular). 1058 assertNoCallbackExceptCapOrLpChange(systemDefaultCallback); 1059 assertNoCallbackExceptCapOrLpChange(otherUidCallback); 1060 } 1061 1062 checkStrictModePrivateDns(); 1063 1064 receiver.unregisterQuietly(); 1065 } 1066 assertNoCallbackExceptCapOrLpChange(TestableNetworkCallback callback)1067 private void assertNoCallbackExceptCapOrLpChange(TestableNetworkCallback callback) { 1068 callback.assertNoCallback(c -> !(c instanceof CallbackEntry.CapabilitiesChanged 1069 || c instanceof CallbackEntry.LinkPropertiesChanged)); 1070 } 1071 1072 @Test testAppAllowed()1073 public void testAppAllowed() throws Exception { 1074 FileDescriptor fd = openSocketFdInOtherApp(TEST_HOST, 80, TIMEOUT_MS); 1075 1076 // Shell app must not be put in here or it would kill the ADB-over-network use case 1077 String allowedApps = mRemoteSocketFactoryClient.getPackageName() + "," + mPackageName; 1078 startVpn(new String[] {"192.0.2.2/32", "2001:db8:1:2::ffe/128"}, 1079 new String[] {"192.0.2.0/24", "2001:db8::/32"}, 1080 allowedApps, "", null, null /* underlyingNetworks */, false /* isAlwaysMetered */); 1081 1082 assertSocketClosed(fd, TEST_HOST); 1083 1084 checkTrafficOnVpn(); 1085 1086 maybeExpectVpnTransportInfo(mCM.getActiveNetwork()); 1087 1088 checkStrictModePrivateDns(); 1089 } 1090 getSupportedKeepalives(NetworkCapabilities nc)1091 private int getSupportedKeepalives(NetworkCapabilities nc) throws Exception { 1092 // Get number of supported concurrent keepalives for testing network. 1093 final int[] keepalivesPerTransport = KeepaliveUtils.getSupportedKeepalives( 1094 mTargetContext); 1095 return KeepaliveUtils.getSupportedKeepalivesForNetworkCapabilities( 1096 keepalivesPerTransport, nc); 1097 } 1098 1099 // This class can't be private, otherwise the constants can't be static imported. 1100 static class TestSocketKeepaliveCallback extends SocketKeepalive.Callback { 1101 // This must be larger than the alarm delay in AutomaticOnOffKeepaliveTracker. 1102 private static final int KEEPALIVE_TIMEOUT_MS = 10_000; 1103 public enum CallbackType { 1104 ON_STARTED, 1105 ON_RESUMED, 1106 ON_STOPPED, 1107 ON_PAUSED, 1108 ON_ERROR, 1109 ON_DATA_RECEIVED 1110 } 1111 private ArrayTrackRecord<CallbackType> mHistory = new ArrayTrackRecord<>(); 1112 private ArrayTrackRecord<CallbackType>.ReadHead mEvents = mHistory.newReadHead(); 1113 1114 @Override onStarted()1115 public void onStarted() { 1116 mHistory.add(ON_STARTED); 1117 } 1118 1119 @Override onResumed()1120 public void onResumed() { 1121 mHistory.add(ON_RESUMED); 1122 } 1123 1124 @Override onStopped()1125 public void onStopped() { 1126 mHistory.add(ON_STOPPED); 1127 } 1128 1129 @Override onPaused()1130 public void onPaused() { 1131 mHistory.add(ON_PAUSED); 1132 } 1133 1134 @Override onError(final int error)1135 public void onError(final int error) { 1136 mHistory.add(ON_ERROR); 1137 } 1138 1139 @Override onDataReceived()1140 public void onDataReceived() { 1141 mHistory.add(ON_DATA_RECEIVED); 1142 } 1143 poll()1144 public CallbackType poll() { 1145 return mEvents.poll(KEEPALIVE_TIMEOUT_MS, it -> true); 1146 } 1147 } 1148 getV4AddrByName(final String hostname)1149 private InetAddress getV4AddrByName(final String hostname) throws Exception { 1150 final InetAddress[] allAddrs = InetAddress.getAllByName(hostname); 1151 for (InetAddress addr : allAddrs) { 1152 if (addr instanceof Inet4Address) return addr; 1153 } 1154 return null; 1155 } 1156 1157 @Test @IgnoreUpTo(Build.VERSION_CODES.TIRAMISU) // Automatic keepalives were added in U. testAutomaticOnOffKeepaliveModeNoClose()1158 public void testAutomaticOnOffKeepaliveModeNoClose() throws Exception { 1159 doTestAutomaticOnOffKeepaliveMode(false); 1160 } 1161 1162 @Test @IgnoreUpTo(Build.VERSION_CODES.TIRAMISU) // Automatic keepalives were added in U. testAutomaticOnOffKeepaliveModeClose()1163 public void testAutomaticOnOffKeepaliveModeClose() throws Exception { 1164 doTestAutomaticOnOffKeepaliveMode(true); 1165 } 1166 startKeepalive(SocketKeepalive kp, TestSocketKeepaliveCallback callback)1167 private void startKeepalive(SocketKeepalive kp, TestSocketKeepaliveCallback callback) { 1168 runWithShellPermissionIdentity(() -> { 1169 // Only SocketKeepalive.start() requires READ_DEVICE_CONFIG because feature is protected 1170 // by a feature flag. But also verify ON_STARTED callback received here to ensure 1171 // keepalive is indeed started because start() runs in the executor thread and shell 1172 // permission may be dropped before reading DeviceConfig. 1173 kp.start(10 /* intervalSec */, SocketKeepalive.FLAG_AUTOMATIC_ON_OFF, mNetwork); 1174 1175 // Verify callback status. 1176 assertEquals(ON_STARTED, callback.poll()); 1177 }, READ_DEVICE_CONFIG); 1178 } 1179 doTestAutomaticOnOffKeepaliveMode(final boolean closeSocket)1180 private void doTestAutomaticOnOffKeepaliveMode(final boolean closeSocket) throws Exception { 1181 // Get default network first before starting VPN 1182 final Network defaultNetwork = mCM.getActiveNetwork(); 1183 final TestableNetworkCallback cb = new TestableNetworkCallback(); 1184 registerDefaultNetworkCallback(cb); 1185 cb.expect(CallbackEntry.AVAILABLE, defaultNetwork); 1186 final NetworkCapabilities cap = 1187 cb.expect(CallbackEntry.NETWORK_CAPS_UPDATED, defaultNetwork).getCaps(); 1188 final LinkProperties lp = 1189 cb.expect(CallbackEntry.LINK_PROPERTIES_CHANGED, defaultNetwork).getLp(); 1190 cb.expect(CallbackEntry.BLOCKED_STATUS, defaultNetwork); 1191 1192 // Setup VPN 1193 final FileDescriptor fd = openSocketFdInOtherApp(TEST_HOST, 80, TIMEOUT_MS); 1194 final String allowedApps = mRemoteSocketFactoryClient.getPackageName() + "," + mPackageName; 1195 startVpn(new String[]{"192.0.2.2/32", "2001:db8:1:2::ffe/128"}, 1196 new String[]{"192.0.2.0/24", "2001:db8::/32"}, 1197 allowedApps, "" /* disallowedApplications */, null /* proxyInfo */, 1198 null /* underlyingNetworks */, false /* isAlwaysMetered */); 1199 assertSocketClosed(fd, TEST_HOST); 1200 1201 // Decrease the TCP polling timer for testing. 1202 runWithShellPermissionIdentity(() -> mCM.setTestLowTcpPollingTimerForKeepalive( 1203 System.currentTimeMillis() + TEST_TCP_POLLING_TIMER_EXPIRED_PERIOD_MS), 1204 NETWORK_SETTINGS); 1205 1206 // Setup keepalive 1207 final int supported = getSupportedKeepalives(cap); 1208 assumeTrue("Network " + defaultNetwork + " does not support keepalive", supported != 0); 1209 final InetAddress srcAddr = CollectionUtils.findFirst(lp.getAddresses(), 1210 it -> it instanceof Inet4Address); 1211 assumeTrue("This test requires native IPv4", srcAddr != null); 1212 1213 final TestSocketKeepaliveCallback callback = new TestSocketKeepaliveCallback(); 1214 1215 final String origMode = runWithShellPermissionIdentity(() -> { 1216 final String mode = DeviceConfig.getProperty( 1217 DeviceConfig.NAMESPACE_TETHERING, AUTOMATIC_ON_OFF_KEEPALIVE_VERSION); 1218 DeviceConfig.setProperty(DeviceConfig.NAMESPACE_TETHERING, 1219 AUTOMATIC_ON_OFF_KEEPALIVE_VERSION, 1220 AUTOMATIC_ON_OFF_KEEPALIVE_ENABLED, false /* makeDefault */); 1221 return mode; 1222 }, READ_DEVICE_CONFIG, WRITE_DEVICE_CONFIG, WRITE_ALLOWLISTED_DEVICE_CONFIG); 1223 1224 final IpSecManager ipSec = mTargetContext.getSystemService(IpSecManager.class); 1225 SocketKeepalive kp = null; 1226 try (IpSecManager.UdpEncapsulationSocket nattSocket = ipSec.openUdpEncapsulationSocket()) { 1227 final InetAddress dstAddr = getV4AddrByName(TEST_HOST); 1228 assertNotNull(dstAddr); 1229 1230 // Start keepalive with dynamic keepalive mode enabled. 1231 final Executor executor = mTargetContext.getMainExecutor(); 1232 kp = mCM.createSocketKeepalive(defaultNetwork, nattSocket, 1233 srcAddr, dstAddr, executor, callback); 1234 startKeepalive(kp, callback); 1235 1236 // There should be no open sockets on the VPN network, because any 1237 // open sockets were closed when startVpn above was called. So the 1238 // first TCP poll should trigger ON_PAUSED. 1239 assertEquals(ON_PAUSED, callback.poll()); 1240 1241 final Socket s = new Socket(); 1242 mNetwork.bindSocket(s); 1243 s.connect(new InetSocketAddress(dstAddr, 80)); 1244 assertEquals(ON_RESUMED, callback.poll()); 1245 1246 if (closeSocket) { 1247 s.close(); 1248 assertEquals(ON_PAUSED, callback.poll()); 1249 } 1250 1251 kp.stop(); 1252 assertEquals(ON_STOPPED, callback.poll()); 1253 } finally { 1254 if (kp != null) kp.stop(); 1255 1256 runWithShellPermissionIdentity(() -> { 1257 DeviceConfig.setProperty( 1258 DeviceConfig.NAMESPACE_TETHERING, 1259 AUTOMATIC_ON_OFF_KEEPALIVE_VERSION, 1260 origMode, false); 1261 mCM.setTestLowTcpPollingTimerForKeepalive(0); 1262 }, WRITE_DEVICE_CONFIG, WRITE_ALLOWLISTED_DEVICE_CONFIG, NETWORK_SETTINGS); 1263 } 1264 } 1265 1266 @Test testAppDisallowed()1267 public void testAppDisallowed() throws Exception { 1268 FileDescriptor localFd = openSocketFd(TEST_HOST, 80, TIMEOUT_MS); 1269 FileDescriptor remoteFd = openSocketFdInOtherApp(TEST_HOST, 80, TIMEOUT_MS); 1270 1271 String disallowedApps = mRemoteSocketFactoryClient.getPackageName() + "," + mPackageName; 1272 if (!SdkLevel.isAtLeastS()) { 1273 // If adb TCP port opened, this test may running by adb over TCP. 1274 // Add com.android.shell application into disallowedApps to exclude adb socket for VPN 1275 // test, see b/119382723 (the test doesn't support adb over TCP when adb runs as root). 1276 // 1277 // This is fixed in S, but still affects previous Android versions, 1278 // and this test must be backwards compatible. 1279 // TODO: Delete this code entirely when R is no longer supported. 1280 disallowedApps = disallowedApps + ",com.android.shell"; 1281 } 1282 Log.i(TAG, "Append shell app to disallowedApps: " + disallowedApps); 1283 startVpn(new String[] {"192.0.2.2/32", "2001:db8:1:2::ffe/128"}, 1284 new String[] {"192.0.2.0/24", "2001:db8::/32"}, 1285 "", disallowedApps, null, null /* underlyingNetworks */, 1286 false /* isAlwaysMetered */); 1287 1288 assertSocketStillOpen(localFd, TEST_HOST); 1289 assertSocketStillOpen(remoteFd, TEST_HOST); 1290 1291 checkNoTrafficOnVpn(); 1292 1293 final Network network = mCM.getActiveNetwork(); 1294 final NetworkCapabilities nc = mCM.getNetworkCapabilities(network); 1295 assertFalse(nc.hasTransport(TRANSPORT_VPN)); 1296 } 1297 1298 @Test testSocketClosed()1299 public void testSocketClosed() throws Exception { 1300 final FileDescriptor localFd = openSocketFd(TEST_HOST, 80, TIMEOUT_MS); 1301 final List<FileDescriptor> remoteFds = new ArrayList<>(); 1302 1303 for (int i = 0; i < 30; i++) { 1304 remoteFds.add(openSocketFdInOtherApp(TEST_HOST, 80, TIMEOUT_MS)); 1305 } 1306 1307 final String allowedApps = mRemoteSocketFactoryClient.getPackageName() + "," + mPackageName; 1308 startVpn(new String[] {"192.0.2.2/32", "2001:db8:1:2::ffe/128"}, 1309 new String[] {"192.0.2.0/24", "2001:db8::/32"}, 1310 allowedApps, "", null, null /* underlyingNetworks */, false /* isAlwaysMetered */); 1311 1312 // Socket owned by VPN uid is not closed 1313 assertSocketStillOpen(localFd, TEST_HOST); 1314 1315 // Sockets not owned by VPN uid are closed 1316 for (final FileDescriptor remoteFd: remoteFds) { 1317 assertSocketClosed(remoteFd, TEST_HOST); 1318 } 1319 } 1320 1321 @Test testExcludedRoutes()1322 public void testExcludedRoutes() throws Exception { 1323 assumeTrue(SdkLevel.isAtLeastT()); 1324 1325 // Shell app must not be put in here or it would kill the ADB-over-network use case 1326 String allowedApps = mRemoteSocketFactoryClient.getPackageName() + "," + mPackageName; 1327 startVpn(new String[]{"192.0.2.2/32", "2001:db8:1:2::ffe/128"} /* addresses */, 1328 new String[]{"0.0.0.0/0", "::/0"} /* routes */, 1329 new String[]{"192.0.2.0/24", "2001:db8::/32"} /* excludedRoutes */, 1330 allowedApps, "" /* disallowedApplications */, null /* proxyInfo */, 1331 null /* underlyingNetworks */, false /* isAlwaysMetered */); 1332 1333 // Excluded routes should bypass VPN. 1334 checkTrafficBypassesVpn("192.0.2.1"); 1335 checkTrafficBypassesVpn("2001:db8:dead:beef::f00"); 1336 // Other routes should go through VPN, since default routes are included. 1337 checkTrafficOnVpn("198.51.100.1"); 1338 checkTrafficOnVpn("2002:db8::1"); 1339 } 1340 1341 @Test testIncludedRoutes()1342 public void testIncludedRoutes() throws Exception { 1343 // Shell app must not be put in here or it would kill the ADB-over-network use case 1344 String allowedApps = mRemoteSocketFactoryClient.getPackageName() + "," + mPackageName; 1345 startVpn(new String[]{"192.0.2.2/32", "2001:db8:1:2::ffe/128"} /* addresses */, 1346 new String[]{"192.0.2.0/24", "2001:db8::/32"} /* routes */, 1347 allowedApps, "" /* disallowedApplications */, null /* proxyInfo */, 1348 null /* underlyingNetworks */, false /* isAlwaysMetered */); 1349 1350 // Included routes should go through VPN. 1351 checkTrafficOnVpn("192.0.2.1"); 1352 checkTrafficOnVpn("2001:db8:dead:beef::f00"); 1353 // Other routes should bypass VPN, since default routes are not included. 1354 checkTrafficBypassesVpn("198.51.100.1"); 1355 checkTrafficBypassesVpn("2002:db8::1"); 1356 } 1357 1358 @Test testInterleavedRoutes()1359 public void testInterleavedRoutes() throws Exception { 1360 assumeTrue(SdkLevel.isAtLeastT()); 1361 1362 // Shell app must not be put in here or it would kill the ADB-over-network use case 1363 String allowedApps = mRemoteSocketFactoryClient.getPackageName() + "," + mPackageName; 1364 startVpn(new String[]{"192.0.2.2/32", "2001:db8:1:2::ffe/128"} /* addresses */, 1365 new String[]{"0.0.0.0/0", "192.0.2.0/32", "::/0", "2001:db8::/128"} /* routes */, 1366 new String[]{"192.0.2.0/24", "2001:db8::/32"} /* excludedRoutes */, 1367 allowedApps, "" /* disallowedApplications */, null /* proxyInfo */, 1368 null /* underlyingNetworks */, false /* isAlwaysMetered */, 1369 true /* addRoutesByIpPrefix */); 1370 1371 // Excluded routes should bypass VPN. 1372 checkTrafficBypassesVpn("192.0.2.1"); 1373 checkTrafficBypassesVpn("2001:db8:dead:beef::f00"); 1374 1375 // Included routes inside excluded routes should go through VPN, since the longest common 1376 // prefix precedes. 1377 checkTrafficOnVpn("192.0.2.0"); 1378 checkTrafficOnVpn("2001:db8::"); 1379 1380 // Other routes should go through VPN, since default routes are included. 1381 checkTrafficOnVpn("198.51.100.1"); 1382 checkTrafficOnVpn("2002:db8::1"); 1383 } 1384 1385 @Test testGetConnectionOwnerUidSecurity()1386 public void testGetConnectionOwnerUidSecurity() throws Exception { 1387 DatagramSocket s; 1388 InetAddress address = InetAddress.getByName("localhost"); 1389 s = new DatagramSocket(); 1390 s.setSoTimeout(SOCKET_TIMEOUT_MS); 1391 s.connect(address, 7); 1392 InetSocketAddress loc = new InetSocketAddress(s.getLocalAddress(), s.getLocalPort()); 1393 InetSocketAddress rem = new InetSocketAddress(s.getInetAddress(), s.getPort()); 1394 try { 1395 int uid = mCM.getConnectionOwnerUid(OsConstants.IPPROTO_TCP, loc, rem); 1396 assertEquals("Only an active VPN app should see connection information", 1397 INVALID_UID, uid); 1398 } catch (SecurityException acceptable) { 1399 // R and below throw SecurityException if a non-active VPN calls this method. 1400 // As long as we can't actually get socket information, either behaviour is fine. 1401 return; 1402 } 1403 } 1404 1405 @Test testSetProxy()1406 public void testSetProxy() throws Exception { 1407 ProxyInfo initialProxy = mCM.getDefaultProxy(); 1408 // Receiver for the proxy change broadcast. 1409 BlockingBroadcastReceiver proxyBroadcastReceiver = new ProxyChangeBroadcastReceiver(); 1410 proxyBroadcastReceiver.register(); 1411 1412 String allowedApps = mPackageName; 1413 ProxyInfo testProxyInfo = ProxyInfo.buildDirectProxy("10.0.0.1", 8888); 1414 startVpn(new String[] {"192.0.2.2/32", "2001:db8:1:2::ffe/128"}, 1415 new String[] {"0.0.0.0/0", "::/0"}, allowedApps, "", 1416 testProxyInfo, null /* underlyingNetworks */, false /* isAlwaysMetered */); 1417 1418 // Check that the proxy change broadcast is received 1419 try { 1420 assertNotNull("No proxy change was broadcast when VPN is connected.", 1421 proxyBroadcastReceiver.awaitForBroadcast()); 1422 } finally { 1423 proxyBroadcastReceiver.unregisterQuietly(); 1424 } 1425 1426 // Proxy is set correctly in network and in link properties. 1427 assertNetworkHasExpectedProxy(testProxyInfo, mNetwork); 1428 assertDefaultProxy(testProxyInfo); 1429 1430 proxyBroadcastReceiver = new ProxyChangeBroadcastReceiver(); 1431 proxyBroadcastReceiver.register(); 1432 stopVpn(); 1433 try { 1434 assertNotNull("No proxy change was broadcast when VPN was disconnected.", 1435 proxyBroadcastReceiver.awaitForBroadcast()); 1436 } finally { 1437 proxyBroadcastReceiver.unregisterQuietly(); 1438 } 1439 1440 // After disconnecting from VPN, the proxy settings are the ones of the initial network. 1441 assertDefaultProxy(initialProxy); 1442 } 1443 1444 @Test testSetProxyDisallowedApps()1445 public void testSetProxyDisallowedApps() throws Exception { 1446 ProxyInfo initialProxy = mCM.getDefaultProxy(); 1447 1448 String disallowedApps = mPackageName; 1449 if (!SdkLevel.isAtLeastS()) { 1450 // If adb TCP port opened, this test may running by adb over TCP. 1451 // Add com.android.shell application into disallowedApps to exclude adb socket for VPN 1452 // test, see b/119382723 (the test doesn't support adb over TCP when adb runs as root). 1453 // 1454 // This is fixed in S, but still affects previous Android versions, 1455 // and this test must be backwards compatible. 1456 // TODO: Delete this code entirely when R is no longer supported. 1457 disallowedApps += ",com.android.shell"; 1458 } 1459 ProxyInfo testProxyInfo = ProxyInfo.buildDirectProxy("10.0.0.1", 8888); 1460 startVpn(new String[] {"192.0.2.2/32", "2001:db8:1:2::ffe/128"}, 1461 new String[] {"0.0.0.0/0", "::/0"}, "", disallowedApps, 1462 testProxyInfo, null /* underlyingNetworks */, false /* isAlwaysMetered */); 1463 1464 // The disallowed app does has the proxy configs of the default network. 1465 assertNetworkHasExpectedProxy(initialProxy, mCM.getActiveNetwork()); 1466 assertDefaultProxy(initialProxy); 1467 } 1468 1469 @Test testNoProxy()1470 public void testNoProxy() throws Exception { 1471 ProxyInfo initialProxy = mCM.getDefaultProxy(); 1472 BlockingBroadcastReceiver proxyBroadcastReceiver = new ProxyChangeBroadcastReceiver(); 1473 proxyBroadcastReceiver.register(); 1474 String allowedApps = mPackageName; 1475 startVpn(new String[] {"192.0.2.2/32", "2001:db8:1:2::ffe/128"}, 1476 new String[] {"0.0.0.0/0", "::/0"}, allowedApps, "", null, 1477 null /* underlyingNetworks */, false /* isAlwaysMetered */); 1478 1479 try { 1480 assertNotNull("No proxy change was broadcast.", 1481 proxyBroadcastReceiver.awaitForBroadcast()); 1482 } finally { 1483 proxyBroadcastReceiver.unregisterQuietly(); 1484 } 1485 1486 // The VPN network has no proxy set. 1487 assertNetworkHasExpectedProxy(null, mNetwork); 1488 1489 proxyBroadcastReceiver = new ProxyChangeBroadcastReceiver(); 1490 proxyBroadcastReceiver.register(); 1491 stopVpn(); 1492 try { 1493 assertNotNull("No proxy change was broadcast.", 1494 proxyBroadcastReceiver.awaitForBroadcast()); 1495 } finally { 1496 proxyBroadcastReceiver.unregisterQuietly(); 1497 } 1498 // After disconnecting from VPN, the proxy settings are the ones of the initial network. 1499 assertDefaultProxy(initialProxy); 1500 assertNetworkHasExpectedProxy(initialProxy, mCM.getActiveNetwork()); 1501 } 1502 1503 @Test testBindToNetworkWithProxy()1504 public void testBindToNetworkWithProxy() throws Exception { 1505 String allowedApps = mPackageName; 1506 Network initialNetwork = mCM.getActiveNetwork(); 1507 ProxyInfo initialProxy = mCM.getDefaultProxy(); 1508 ProxyInfo testProxyInfo = ProxyInfo.buildDirectProxy("10.0.0.1", 8888); 1509 // Receiver for the proxy change broadcast. 1510 BlockingBroadcastReceiver proxyBroadcastReceiver = new ProxyChangeBroadcastReceiver(); 1511 proxyBroadcastReceiver.register(); 1512 startVpn(new String[] {"192.0.2.2/32", "2001:db8:1:2::ffe/128"}, 1513 new String[] {"0.0.0.0/0", "::/0"}, allowedApps, "", 1514 testProxyInfo, null /* underlyingNetworks */, false /* isAlwaysMetered */); 1515 1516 assertDefaultProxy(testProxyInfo); 1517 mCM.bindProcessToNetwork(initialNetwork); 1518 try { 1519 assertNotNull("No proxy change was broadcast.", 1520 proxyBroadcastReceiver.awaitForBroadcast()); 1521 } finally { 1522 proxyBroadcastReceiver.unregisterQuietly(); 1523 } 1524 assertDefaultProxy(initialProxy); 1525 } 1526 1527 @Test testVpnMeterednessWithNoUnderlyingNetwork()1528 public void testVpnMeterednessWithNoUnderlyingNetwork() throws Exception { 1529 // VPN is not routing any traffic i.e. its underlying networks is an empty array. 1530 ArrayList<Network> underlyingNetworks = new ArrayList<>(); 1531 String allowedApps = mPackageName; 1532 1533 startVpn(new String[] {"192.0.2.2/32", "2001:db8:1:2::ffe/128"}, 1534 new String[] {"0.0.0.0/0", "::/0"}, allowedApps, "", null, 1535 underlyingNetworks, false /* isAlwaysMetered */); 1536 1537 // VPN should now be the active network. 1538 assertEquals(mNetwork, mCM.getActiveNetwork()); 1539 assertVpnTransportContains(NetworkCapabilities.TRANSPORT_VPN); 1540 // VPN with no underlying networks should be metered by default. 1541 assertTrue(isNetworkMetered(mNetwork)); 1542 assertTrue(mCM.isActiveNetworkMetered()); 1543 1544 maybeExpectVpnTransportInfo(mCM.getActiveNetwork()); 1545 1546 if (SdkLevel.isAtLeastT()) { 1547 runWithShellPermissionIdentity(() -> { 1548 final NetworkCapabilities nc = mCM.getNetworkCapabilities(mNetwork); 1549 assertNotNull(nc); 1550 assertNotNull(nc.getUnderlyingNetworks()); 1551 assertEquals(underlyingNetworks, new ArrayList<>(nc.getUnderlyingNetworks())); 1552 }, NETWORK_SETTINGS); 1553 } 1554 } 1555 1556 @Test testVpnMeterednessWithNullUnderlyingNetwork()1557 public void testVpnMeterednessWithNullUnderlyingNetwork() throws Exception { 1558 Network underlyingNetwork = mCM.getActiveNetwork(); 1559 if (underlyingNetwork == null) { 1560 Log.i(TAG, "testVpnMeterednessWithNullUnderlyingNetwork cannot execute" 1561 + " unless there is an active network"); 1562 return; 1563 } 1564 // VPN tracks platform default. 1565 ArrayList<Network> underlyingNetworks = null; 1566 String allowedApps = mPackageName; 1567 1568 startVpn(new String[] {"192.0.2.2/32", "2001:db8:1:2::ffe/128"}, 1569 new String[] {"0.0.0.0/0", "::/0"}, allowedApps, "", null, 1570 underlyingNetworks, false /*isAlwaysMetered */); 1571 1572 // Ensure VPN transports contains underlying network's transports. 1573 assertVpnTransportContains(underlyingNetwork); 1574 // Its meteredness should be same as that of underlying network. 1575 assertEquals(isNetworkMetered(underlyingNetwork), isNetworkMetered(mNetwork)); 1576 // Meteredness based on VPN capabilities and CM#isActiveNetworkMetered should be in sync. 1577 assertEquals(isNetworkMetered(mNetwork), mCM.isActiveNetworkMetered()); 1578 1579 maybeExpectVpnTransportInfo(mCM.getActiveNetwork()); 1580 } 1581 1582 @Test testVpnMeterednessWithNonNullUnderlyingNetwork()1583 public void testVpnMeterednessWithNonNullUnderlyingNetwork() throws Exception { 1584 Network underlyingNetwork = mCM.getActiveNetwork(); 1585 if (underlyingNetwork == null) { 1586 Log.i(TAG, "testVpnMeterednessWithNonNullUnderlyingNetwork cannot execute" 1587 + " unless there is an active network"); 1588 return; 1589 } 1590 // VPN explicitly declares WiFi to be its underlying network. 1591 ArrayList<Network> underlyingNetworks = new ArrayList<>(1); 1592 underlyingNetworks.add(underlyingNetwork); 1593 String allowedApps = mPackageName; 1594 1595 startVpn(new String[] {"192.0.2.2/32", "2001:db8:1:2::ffe/128"}, 1596 new String[] {"0.0.0.0/0", "::/0"}, allowedApps, "", null, 1597 underlyingNetworks, false /* isAlwaysMetered */); 1598 1599 // Ensure VPN transports contains underlying network's transports. 1600 assertVpnTransportContains(underlyingNetwork); 1601 // Its meteredness should be same as that of underlying network. 1602 assertEquals(isNetworkMetered(underlyingNetwork), isNetworkMetered(mNetwork)); 1603 // Meteredness based on VPN capabilities and CM#isActiveNetworkMetered should be in sync. 1604 assertEquals(isNetworkMetered(mNetwork), mCM.isActiveNetworkMetered()); 1605 1606 maybeExpectVpnTransportInfo(mCM.getActiveNetwork()); 1607 1608 if (SdkLevel.isAtLeastT()) { 1609 final Network vpnNetwork = mCM.getActiveNetwork(); 1610 assertNotEqual(underlyingNetwork, vpnNetwork); 1611 runWithShellPermissionIdentity(() -> { 1612 final NetworkCapabilities nc = mCM.getNetworkCapabilities(vpnNetwork); 1613 assertNotNull(nc); 1614 assertNotNull(nc.getUnderlyingNetworks()); 1615 final List<Network> underlying = nc.getUnderlyingNetworks(); 1616 assertEquals(underlyingNetwork, underlying.get(0)); 1617 }, NETWORK_SETTINGS); 1618 } 1619 } 1620 1621 @Test testAlwaysMeteredVpnWithNullUnderlyingNetwork()1622 public void testAlwaysMeteredVpnWithNullUnderlyingNetwork() throws Exception { 1623 Network underlyingNetwork = mCM.getActiveNetwork(); 1624 if (underlyingNetwork == null) { 1625 Log.i(TAG, "testAlwaysMeteredVpnWithNullUnderlyingNetwork cannot execute" 1626 + " unless there is an active network"); 1627 return; 1628 } 1629 // VPN tracks platform default. 1630 ArrayList<Network> underlyingNetworks = null; 1631 String allowedApps = mPackageName; 1632 boolean isAlwaysMetered = true; 1633 1634 startVpn(new String[] {"192.0.2.2/32", "2001:db8:1:2::ffe/128"}, 1635 new String[] {"0.0.0.0/0", "::/0"}, allowedApps, "", null, 1636 underlyingNetworks, isAlwaysMetered); 1637 1638 // VPN's meteredness does not depend on underlying network since it is always metered. 1639 assertTrue(isNetworkMetered(mNetwork)); 1640 assertTrue(mCM.isActiveNetworkMetered()); 1641 1642 maybeExpectVpnTransportInfo(mCM.getActiveNetwork()); 1643 } 1644 1645 @Test testAlwaysMeteredVpnWithNonNullUnderlyingNetwork()1646 public void testAlwaysMeteredVpnWithNonNullUnderlyingNetwork() throws Exception { 1647 Network underlyingNetwork = mCM.getActiveNetwork(); 1648 if (underlyingNetwork == null) { 1649 Log.i(TAG, "testAlwaysMeteredVpnWithNonNullUnderlyingNetwork cannot execute" 1650 + " unless there is an active network"); 1651 return; 1652 } 1653 // VPN explicitly declares its underlying network. 1654 ArrayList<Network> underlyingNetworks = new ArrayList<>(1); 1655 underlyingNetworks.add(underlyingNetwork); 1656 String allowedApps = mPackageName; 1657 boolean isAlwaysMetered = true; 1658 1659 startVpn(new String[] {"192.0.2.2/32", "2001:db8:1:2::ffe/128"}, 1660 new String[] {"0.0.0.0/0", "::/0"}, allowedApps, "", null, 1661 underlyingNetworks, isAlwaysMetered); 1662 1663 // VPN's meteredness does not depend on underlying network since it is always metered. 1664 assertTrue(isNetworkMetered(mNetwork)); 1665 assertTrue(mCM.isActiveNetworkMetered()); 1666 1667 maybeExpectVpnTransportInfo(mCM.getActiveNetwork()); 1668 1669 if (SdkLevel.isAtLeastT()) { 1670 final Network vpnNetwork = mCM.getActiveNetwork(); 1671 assertNotEqual(underlyingNetwork, vpnNetwork); 1672 runWithShellPermissionIdentity(() -> { 1673 final NetworkCapabilities nc = mCM.getNetworkCapabilities(vpnNetwork); 1674 assertNotNull(nc); 1675 assertNotNull(nc.getUnderlyingNetworks()); 1676 final List<Network> underlying = nc.getUnderlyingNetworks(); 1677 assertEquals(underlyingNetwork, underlying.get(0)); 1678 }, NETWORK_SETTINGS); 1679 } 1680 } 1681 1682 @Test testB141603906()1683 public void testB141603906() throws Exception { 1684 final InetSocketAddress src = new InetSocketAddress(0); 1685 final InetSocketAddress dst = new InetSocketAddress(0); 1686 final int NUM_THREADS = 8; 1687 final int NUM_SOCKETS = 5000; 1688 final Thread[] threads = new Thread[NUM_THREADS]; 1689 startVpn(new String[] {"192.0.2.2/32", "2001:db8:1:2::ffe/128"}, 1690 new String[] {"0.0.0.0/0", "::/0"}, 1691 "" /* allowedApplications */, "com.android.shell" /* disallowedApplications */, 1692 null /* proxyInfo */, null /* underlyingNetworks */, false /* isAlwaysMetered */); 1693 1694 for (int i = 0; i < NUM_THREADS; i++) { 1695 threads[i] = new Thread(() -> { 1696 for (int j = 0; j < NUM_SOCKETS; j++) { 1697 mCM.getConnectionOwnerUid(IPPROTO_TCP, src, dst); 1698 } 1699 }); 1700 } 1701 for (Thread thread : threads) { 1702 thread.start(); 1703 } 1704 for (Thread thread : threads) { 1705 thread.join(); 1706 } 1707 stopVpn(); 1708 } 1709 isNetworkMetered(Network network)1710 private boolean isNetworkMetered(Network network) { 1711 NetworkCapabilities nc = mCM.getNetworkCapabilities(network); 1712 return !nc.hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED); 1713 } 1714 assertVpnTransportContains(Network underlyingNetwork)1715 private void assertVpnTransportContains(Network underlyingNetwork) { 1716 int[] transports = mCM.getNetworkCapabilities(underlyingNetwork).getTransportTypes(); 1717 assertVpnTransportContains(transports); 1718 } 1719 assertVpnTransportContains(int... transports)1720 private void assertVpnTransportContains(int... transports) { 1721 NetworkCapabilities vpnCaps = mCM.getNetworkCapabilities(mNetwork); 1722 for (int transport : transports) { 1723 assertTrue(vpnCaps.hasTransport(transport)); 1724 } 1725 } 1726 maybeExpectVpnTransportInfo(Network network)1727 private void maybeExpectVpnTransportInfo(Network network) { 1728 // VpnTransportInfo was only added in S. 1729 if (!SdkLevel.isAtLeastS()) return; 1730 final NetworkCapabilities vpnNc = mCM.getNetworkCapabilities(network); 1731 assertTrue(vpnNc.hasTransport(TRANSPORT_VPN)); 1732 final TransportInfo ti = vpnNc.getTransportInfo(); 1733 assertTrue(ti instanceof VpnTransportInfo); 1734 assertEquals(VpnManager.TYPE_VPN_SERVICE, ((VpnTransportInfo) ti).getType()); 1735 } 1736 assertDefaultProxy(ProxyInfo expected)1737 private void assertDefaultProxy(ProxyInfo expected) throws Exception { 1738 assertEquals("Incorrect proxy config.", expected, mCM.getDefaultProxy()); 1739 String expectedHost = expected == null ? null : expected.getHost(); 1740 String expectedPort = expected == null ? null : String.valueOf(expected.getPort()); 1741 1742 // ActivityThread may not have time to set it in the properties yet which will cause flakes. 1743 // Wait for some time to deflake the test. 1744 int attempt = 0; 1745 while (!(Objects.equals(expectedHost, System.getProperty("http.proxyHost")) 1746 && Objects.equals(expectedPort, System.getProperty("http.proxyPort"))) 1747 && attempt < 300) { 1748 attempt++; 1749 Log.d(TAG, "Wait for proxy being updated, attempt=" + attempt); 1750 Thread.sleep(100); 1751 } 1752 assertEquals("Incorrect proxy host system property.", expectedHost, 1753 System.getProperty("http.proxyHost")); 1754 assertEquals("Incorrect proxy port system property.", expectedPort, 1755 System.getProperty("http.proxyPort")); 1756 } 1757 assertNetworkHasExpectedProxy(ProxyInfo expected, Network network)1758 private void assertNetworkHasExpectedProxy(ProxyInfo expected, Network network) { 1759 LinkProperties lp = mCM.getLinkProperties(network); 1760 assertNotNull("The network link properties object is null.", lp); 1761 assertEquals("Incorrect proxy config.", expected, lp.getHttpProxy()); 1762 1763 assertEquals(expected, mCM.getProxyForNetwork(network)); 1764 } 1765 1766 class ProxyChangeBroadcastReceiver extends BlockingBroadcastReceiver { 1767 private boolean received; 1768 ProxyChangeBroadcastReceiver()1769 public ProxyChangeBroadcastReceiver() { 1770 super(mTestContext, Proxy.PROXY_CHANGE_ACTION); 1771 received = false; 1772 } 1773 1774 @Override onReceive(Context context, Intent intent)1775 public void onReceive(Context context, Intent intent) { 1776 if (!received) { 1777 // Do not call onReceive() more than once. 1778 super.onReceive(context, intent); 1779 } 1780 received = true; 1781 } 1782 } 1783 1784 /** 1785 * Verifies that DownloadManager has CONNECTIVITY_USE_RESTRICTED_NETWORKS permission that can 1786 * bind socket to VPN when it is in VPN disallowed list but requested downloading app is in VPN 1787 * allowed list. 1788 * See b/165774987. 1789 */ 1790 @Test testDownloadWithDownloadManagerDisallowed()1791 public void testDownloadWithDownloadManagerDisallowed() throws Exception { 1792 // Start a VPN with DownloadManager package in disallowed list. 1793 startVpn(new String[] {"192.0.2.2/32", "2001:db8:1:2::ffe/128"}, 1794 new String[] {"192.0.2.0/24", "2001:db8::/32"}, 1795 "" /* allowedApps */, "com.android.providers.downloads", null /* proxyInfo */, 1796 null /* underlyingNetworks */, false /* isAlwaysMetered */); 1797 1798 final DownloadManager dm = mTestContext.getSystemService(DownloadManager.class); 1799 final DownloadCompleteReceiver receiver = new DownloadCompleteReceiver(); 1800 try { 1801 final int flags = SdkLevel.isAtLeastT() ? RECEIVER_EXPORTED : 0; 1802 mTestContext.registerReceiver(receiver, 1803 new IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE), flags); 1804 1805 // Enqueue a request and check only one download. 1806 final long id = dm.enqueue(new Request( 1807 Uri.parse("https://google-ipv6test.appspot.com/ip.js?fmt=text"))); 1808 assertEquals(1, getTotalNumberDownloads(dm, new Query())); 1809 assertEquals(1, getTotalNumberDownloads(dm, new Query().setFilterById(id))); 1810 1811 // Wait for download complete and check status. 1812 assertEquals(id, receiver.get(TIMEOUT_MS, TimeUnit.MILLISECONDS)); 1813 assertEquals(1, getTotalNumberDownloads(dm, 1814 new Query().setFilterByStatus(DownloadManager.STATUS_SUCCESSFUL))); 1815 1816 // Remove download. 1817 assertEquals(1, dm.remove(id)); 1818 assertEquals(0, getTotalNumberDownloads(dm, new Query())); 1819 } finally { 1820 mTestContext.unregisterReceiver(receiver); 1821 } 1822 } 1823 getTotalNumberDownloads(final DownloadManager dm, final Query query)1824 private static int getTotalNumberDownloads(final DownloadManager dm, final Query query) { 1825 try (Cursor cursor = dm.query(query)) { return cursor.getCount(); } 1826 } 1827 1828 private static class DownloadCompleteReceiver extends BroadcastReceiver { 1829 private final CompletableFuture<Long> future = new CompletableFuture<>(); 1830 1831 @Override onReceive(Context context, Intent intent)1832 public void onReceive(Context context, Intent intent) { 1833 future.complete(intent.getLongExtra( 1834 DownloadManager.EXTRA_DOWNLOAD_ID, -1 /* defaultValue */)); 1835 } 1836 get(long timeout, TimeUnit unit)1837 public long get(long timeout, TimeUnit unit) throws Exception { 1838 return future.get(timeout, unit); 1839 } 1840 } 1841 1842 private static final boolean EXPECT_PASS = false; 1843 private static final boolean EXPECT_BLOCK = true; 1844 1845 @Test @IgnoreUpTo(Build.VERSION_CODES.R) testBlockIncomingPackets()1846 public void testBlockIncomingPackets() throws Exception { 1847 final Network network = mCM.getActiveNetwork(); 1848 assertNotNull("Requires a working Internet connection", network); 1849 1850 final int remoteUid = mRemoteSocketFactoryClient.getUid(); 1851 final List<Range<Integer>> lockdownRange = List.of(new Range<>(remoteUid, remoteUid)); 1852 final DetailedBlockedStatusCallback remoteUidCallback = new DetailedBlockedStatusCallback(); 1853 1854 // Create a TUN interface 1855 final ParcelFileDescriptor tunFd = runWithShellPermissionIdentity(() -> { 1856 final TestNetworkManager tnm = mTestContext.getSystemService(TestNetworkManager.class); 1857 final TestNetworkInterface iface = tnm.createTunInterface(List.of( 1858 TEST_IP4_DST_ADDR, TEST_IP6_DST_ADDR)); 1859 return iface.getFileDescriptor(); 1860 }, MANAGE_TEST_NETWORKS); 1861 1862 // Create a remote UDP socket 1863 final FileDescriptor remoteUdpFd = mRemoteSocketFactoryClient.openDatagramSocketFd(); 1864 1865 testAndCleanup(() -> { 1866 runWithShellPermissionIdentity(() -> { 1867 registerDefaultNetworkCallbackForUid(remoteUid, remoteUidCallback, 1868 new Handler(Looper.getMainLooper())); 1869 }, NETWORK_SETTINGS); 1870 remoteUidCallback.expectAvailableCallbacksWithBlockedReasonNone(network); 1871 1872 // The remote UDP socket can receive packets coming from the TUN interface 1873 checkBlockIncomingPacket(tunFd.getFileDescriptor(), remoteUdpFd, EXPECT_PASS); 1874 1875 // Lockdown uid that has the remote UDP socket 1876 runWithShellPermissionIdentity(() -> { 1877 mCM.setRequireVpnForUids(true /* requireVpn */, lockdownRange); 1878 }, NETWORK_SETTINGS); 1879 1880 // setRequireVpnForUids setup a lockdown rule asynchronously. So it needs to wait for 1881 // BlockedStatusCallback to be fired before checking the blocking status of incoming 1882 // packets. 1883 remoteUidCallback.expect(BLOCKED_STATUS_INT, network, 1884 cb -> cb.getReason() == BLOCKED_REASON_LOCKDOWN_VPN); 1885 1886 if (SdkLevel.isAtLeastT()) { 1887 // On T and above, lockdown rule drop packets not coming from lo regardless of the 1888 // VPN connectivity. 1889 checkBlockIncomingPacket(tunFd.getFileDescriptor(), remoteUdpFd, EXPECT_BLOCK); 1890 } 1891 1892 // Start the VPN that has default routes. This VPN should have interface filtering rule 1893 // for incoming packet and drop packets not coming from lo nor the VPN interface. 1894 final String allowedApps = 1895 mRemoteSocketFactoryClient.getPackageName() + "," + mPackageName; 1896 startVpn(new String[]{"192.0.2.2/32", "2001:db8:1:2::ffe/128"}, 1897 new String[]{"0.0.0.0/0", "::/0"}, allowedApps, "" /* disallowedApplications */, 1898 null /* proxyInfo */, null /* underlyingNetworks */, 1899 false /* isAlwaysMetered */); 1900 1901 checkBlockIncomingPacket(tunFd.getFileDescriptor(), remoteUdpFd, EXPECT_BLOCK); 1902 }, /* cleanup */ () -> { 1903 tunFd.close(); 1904 }, /* cleanup */ () -> { 1905 Os.close(remoteUdpFd); 1906 }, /* cleanup */ () -> { 1907 runWithShellPermissionIdentity(() -> { 1908 mCM.setRequireVpnForUids(false /* requireVpn */, lockdownRange); 1909 }, NETWORK_SETTINGS); 1910 }); 1911 } 1912 1913 @Test testSetVpnDefaultForUids()1914 public void testSetVpnDefaultForUids() throws Exception { 1915 assumeTrue(SdkLevel.isAtLeastU()); 1916 1917 final Network defaultNetwork = mCM.getActiveNetwork(); 1918 assertNotNull("There must be a default network", defaultNetwork); 1919 1920 final TestableNetworkCallback defaultNetworkCallback = new TestableNetworkCallback(); 1921 final String session = UUID.randomUUID().toString(); 1922 final int myUid = Process.myUid(); 1923 1924 testAndCleanup(() -> { 1925 registerDefaultNetworkCallback(defaultNetworkCallback); 1926 defaultNetworkCallback.expectAvailableCallbacks(defaultNetwork); 1927 1928 final Range<Integer> myUidRange = new Range<>(myUid, myUid); 1929 runWithShellPermissionIdentity(() -> { 1930 mCM.setVpnDefaultForUids(session, List.of(myUidRange)); 1931 }, NETWORK_SETTINGS); 1932 1933 // The VPN will be the only default network for the app, so it's expected to receive 1934 // onLost() callback. 1935 defaultNetworkCallback.eventuallyExpect(CallbackEntry.LOST); 1936 1937 final ArrayList<Network> underlyingNetworks = new ArrayList<>(); 1938 underlyingNetworks.add(defaultNetwork); 1939 startVpn(new String[] {"192.0.2.2/32", "2001:db8:1:2::ffe/128"} /* addresses */, 1940 new String[] {"0.0.0.0/0", "::/0"} /* routes */, 1941 "" /* allowedApplications */, "" /* disallowedApplications */, 1942 null /* proxyInfo */, underlyingNetworks, false /* isAlwaysMetered */); 1943 1944 expectVpnNetwork(defaultNetworkCallback); 1945 }, /* cleanup */ () -> { 1946 stopVpn(); 1947 defaultNetworkCallback.eventuallyExpect(CallbackEntry.LOST); 1948 }, /* cleanup */ () -> { 1949 runWithShellPermissionIdentity(() -> { 1950 mCM.setVpnDefaultForUids(session, new ArraySet<>()); 1951 }, NETWORK_SETTINGS); 1952 // The default network of the app will be changed back to wifi when the VPN network 1953 // preference feature is disabled. 1954 defaultNetworkCallback.eventuallyExpect(CallbackEntry.AVAILABLE, 1955 NETWORK_CALLBACK_TIMEOUT_MS, 1956 entry -> defaultNetwork.equals(entry.getNetwork())); 1957 }); 1958 } 1959 1960 /** 1961 * Check if packets to a VPN interface's IP arriving on a non-VPN interface are dropped or not. 1962 * If the test interface has a different address from the VPN interface, packets must be dropped 1963 * If the test interface has the same address as the VPN interface, packets must not be 1964 * dropped 1965 * 1966 * @param duplicatedAddress true to bring up the test interface with the same address as the VPN 1967 * interface 1968 */ doTestDropPacketToVpnAddress(final boolean duplicatedAddress)1969 private void doTestDropPacketToVpnAddress(final boolean duplicatedAddress) 1970 throws Exception { 1971 assumeTrue(mCM.isConnectivityServiceFeatureEnabledForTesting( 1972 INGRESS_TO_VPN_ADDRESS_FILTERING)); 1973 1974 final NetworkRequest request = new NetworkRequest.Builder() 1975 .removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VPN) 1976 .removeCapability(NetworkCapabilities.NET_CAPABILITY_TRUSTED) 1977 .addTransportType(TRANSPORT_TEST) 1978 .build(); 1979 final CtsNetUtils.TestNetworkCallback callback = new CtsNetUtils.TestNetworkCallback(); 1980 mCM.requestNetwork(request, callback); 1981 final ParcelFileDescriptor srcTunFd = runWithShellPermissionIdentity(() -> { 1982 final TestNetworkManager tnm = mTestContext.getSystemService(TestNetworkManager.class); 1983 List<LinkAddress> linkAddresses = duplicatedAddress 1984 ? List.of(new LinkAddress("192.0.2.2/24"), 1985 new LinkAddress("2001:db8:1:2::ffe/64")) : 1986 List.of(new LinkAddress("198.51.100.2/24"), 1987 new LinkAddress("2001:db8:3:4::ffe/64")); 1988 final TestNetworkInterface iface = tnm.createTunInterface(linkAddresses); 1989 tnm.setupTestNetwork(iface.getInterfaceName(), new Binder()); 1990 return iface.getFileDescriptor(); 1991 }, MANAGE_TEST_NETWORKS); 1992 final Network testNetwork = callback.waitForAvailable(); 1993 assertNotNull(testNetwork); 1994 final DatagramSocket dstSock = new DatagramSocket(); 1995 1996 testAndCleanup(() -> { 1997 startVpn(new String[] {"192.0.2.2/32", "2001:db8:1:2::ffe/128"} /* addresses */, 1998 new String[]{"0.0.0.0/0", "::/0"} /* routes */, 1999 "" /* allowedApplications */, "" /* disallowedApplications */, 2000 null /* proxyInfo */, null /* underlyingNetworks */, 2001 false /* isAlwaysMetered */); 2002 2003 final FileDescriptor dstUdpFd = dstSock.getFileDescriptor$(); 2004 checkBlockUdp(srcTunFd.getFileDescriptor(), dstUdpFd, 2005 InetAddresses.parseNumericAddress("192.0.2.2") /* dstAddress */, 2006 InetAddresses.parseNumericAddress("192.0.2.1") /* srcAddress */, 2007 duplicatedAddress ? EXPECT_PASS : EXPECT_BLOCK); 2008 checkBlockUdp(srcTunFd.getFileDescriptor(), dstUdpFd, 2009 InetAddresses.parseNumericAddress("2001:db8:1:2::ffe") /* dstAddress */, 2010 InetAddresses.parseNumericAddress("2001:db8:1:2::ffa") /* srcAddress */, 2011 duplicatedAddress ? EXPECT_PASS : EXPECT_BLOCK); 2012 2013 // Traffic on VPN should not be affected 2014 checkTrafficOnVpn(); 2015 }, /* cleanup */ () -> { 2016 srcTunFd.close(); 2017 dstSock.close(); 2018 }, /* cleanup */ () -> { 2019 runWithShellPermissionIdentity(() -> { 2020 mTestContext.getSystemService(TestNetworkManager.class) 2021 .teardownTestNetwork(testNetwork); 2022 }, MANAGE_TEST_NETWORKS); 2023 }, /* cleanup */ () -> { 2024 mCM.unregisterNetworkCallback(callback); 2025 }); 2026 } 2027 2028 @Test @IgnoreUpTo(Build.VERSION_CODES.S_V2) testDropPacketToVpnAddress_WithoutDuplicatedAddress()2029 public void testDropPacketToVpnAddress_WithoutDuplicatedAddress() throws Exception { 2030 doTestDropPacketToVpnAddress(false /* duplicatedAddress */); 2031 } 2032 2033 @Test @IgnoreUpTo(Build.VERSION_CODES.S_V2) testDropPacketToVpnAddress_WithDuplicatedAddress()2034 public void testDropPacketToVpnAddress_WithDuplicatedAddress() throws Exception { 2035 doTestDropPacketToVpnAddress(true /* duplicatedAddress */); 2036 } 2037 buildIpv4UdpPacket(final Inet4Address dstAddr, final Inet4Address srcAddr, final short dstPort, final short srcPort, final byte[] payload)2038 private ByteBuffer buildIpv4UdpPacket(final Inet4Address dstAddr, final Inet4Address srcAddr, 2039 final short dstPort, final short srcPort, final byte[] payload) throws IOException { 2040 2041 final ByteBuffer buffer = PacketBuilder.allocate(false /* hasEther */, 2042 OsConstants.IPPROTO_IP, OsConstants.IPPROTO_UDP, payload.length); 2043 final PacketBuilder packetBuilder = new PacketBuilder(buffer); 2044 2045 packetBuilder.writeIpv4Header( 2046 (byte) 0 /* TOS */, 2047 (short) 27149 /* ID */, 2048 (short) 0x4000 /* flags=DF, offset=0 */, 2049 (byte) 64 /* TTL */, 2050 (byte) OsConstants.IPPROTO_UDP, 2051 srcAddr, 2052 dstAddr); 2053 packetBuilder.writeUdpHeader(srcPort, dstPort); 2054 buffer.put(payload); 2055 2056 return packetBuilder.finalizePacket(); 2057 } 2058 buildIpv6UdpPacket(final Inet6Address dstAddr, final Inet6Address srcAddr, final short dstPort, final short srcPort, final byte[] payload)2059 private ByteBuffer buildIpv6UdpPacket(final Inet6Address dstAddr, final Inet6Address srcAddr, 2060 final short dstPort, final short srcPort, final byte[] payload) throws IOException { 2061 2062 final ByteBuffer buffer = PacketBuilder.allocate(false /* hasEther */, 2063 OsConstants.IPPROTO_IPV6, OsConstants.IPPROTO_UDP, payload.length); 2064 final PacketBuilder packetBuilder = new PacketBuilder(buffer); 2065 2066 packetBuilder.writeIpv6Header( 2067 0x60000000 /* version=6, traffic class=0, flow label=0 */, 2068 (byte) OsConstants.IPPROTO_UDP, 2069 (short) 64 /* hop limit */, 2070 srcAddr, 2071 dstAddr); 2072 packetBuilder.writeUdpHeader(srcPort, dstPort); 2073 buffer.put(payload); 2074 2075 return packetBuilder.finalizePacket(); 2076 } 2077 checkBlockUdp( final FileDescriptor srcTunFd, final FileDescriptor dstUdpFd, final InetAddress dstAddress, final InetAddress srcAddress, final boolean expectBlock)2078 private void checkBlockUdp( 2079 final FileDescriptor srcTunFd, 2080 final FileDescriptor dstUdpFd, 2081 final InetAddress dstAddress, 2082 final InetAddress srcAddress, 2083 final boolean expectBlock) throws Exception { 2084 final Random random = new Random(); 2085 final byte[] sendData = new byte[100]; 2086 random.nextBytes(sendData); 2087 final short dstPort = (short) ((InetSocketAddress) Os.getsockname(dstUdpFd)).getPort(); 2088 2089 ByteBuffer buf; 2090 if (dstAddress instanceof Inet6Address) { 2091 buf = buildIpv6UdpPacket( 2092 (Inet6Address) dstAddress, 2093 (Inet6Address) srcAddress, 2094 dstPort, TEST_SRC_PORT, sendData); 2095 } else { 2096 buf = buildIpv4UdpPacket( 2097 (Inet4Address) dstAddress, 2098 (Inet4Address) srcAddress, 2099 dstPort, TEST_SRC_PORT, sendData); 2100 } 2101 2102 Os.write(srcTunFd, buf); 2103 2104 final StructPollfd pollfd = new StructPollfd(); 2105 pollfd.events = (short) POLLIN; 2106 pollfd.fd = dstUdpFd; 2107 final int ret = Os.poll(new StructPollfd[]{pollfd}, SOCKET_TIMEOUT_MS); 2108 2109 if (expectBlock) { 2110 assertEquals("Expect not to receive a packet but received a packet", 0, ret); 2111 } else { 2112 assertEquals("Expect to receive a packet but did not receive a packet", 1, ret); 2113 final byte[] recvData = new byte[sendData.length]; 2114 final int readSize = Os.read(dstUdpFd, recvData, 0 /* byteOffset */, recvData.length); 2115 assertEquals(recvData.length, readSize); 2116 MoreAsserts.assertEquals(sendData, recvData); 2117 } 2118 } 2119 checkBlockIncomingPacket( final FileDescriptor srcTunFd, final FileDescriptor dstUdpFd, final boolean expectBlock)2120 private void checkBlockIncomingPacket( 2121 final FileDescriptor srcTunFd, 2122 final FileDescriptor dstUdpFd, 2123 final boolean expectBlock) throws Exception { 2124 checkBlockUdp(srcTunFd, dstUdpFd, TEST_IP4_DST_ADDR.getAddress(), 2125 TEST_IP4_SRC_ADDR.getAddress(), expectBlock); 2126 checkBlockUdp(srcTunFd, dstUdpFd, TEST_IP6_DST_ADDR.getAddress(), 2127 TEST_IP6_SRC_ADDR.getAddress(), expectBlock); 2128 } 2129 2130 private class DetailedBlockedStatusCallback extends TestableNetworkCallback { expectAvailableCallbacksWithBlockedReasonNone(Network network)2131 public void expectAvailableCallbacksWithBlockedReasonNone(Network network) { 2132 super.expectAvailableCallbacks(network, false /* suspended */, true /* validated */, 2133 BLOCKED_REASON_NONE, NETWORK_CALLBACK_TIMEOUT_MS); 2134 } onBlockedStatusChanged(Network network, int blockedReasons)2135 public void onBlockedStatusChanged(Network network, int blockedReasons) { 2136 getHistory().add(new CallbackEntry.BlockedStatusInt(network, blockedReasons)); 2137 } 2138 } 2139 } 2140