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