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