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