1 /* 2 * Copyright (C) 2016 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.deviceandprofileowner.vpn; 18 19 import android.annotation.TargetApi; 20 import android.app.admin.DevicePolicyManager; 21 import android.content.ComponentName; 22 import android.content.Context; 23 import android.content.pm.PackageManager; 24 import android.net.ConnectivityManager; 25 import android.net.Network; 26 import android.net.NetworkCapabilities; 27 import android.net.NetworkInfo; 28 import android.net.NetworkRequest; 29 import android.os.Build.VERSION_CODES; 30 import android.system.ErrnoException; 31 import android.system.Os; 32 import android.system.StructPollfd; 33 34 import com.android.cts.deviceandprofileowner.BaseDeviceAdminTest; 35 36 import java.io.ByteArrayOutputStream; 37 import java.io.DataOutputStream; 38 import java.io.FileDescriptor; 39 import java.io.IOException; 40 import java.net.InetAddress; 41 import java.net.InetSocketAddress; 42 import java.util.Arrays; 43 import java.util.concurrent.CountDownLatch; 44 import java.util.concurrent.TimeUnit; 45 46 import static android.system.OsConstants.AF_INET; 47 import static android.system.OsConstants.IPPROTO_ICMP; 48 import static android.system.OsConstants.POLLIN; 49 import static android.system.OsConstants.SOCK_DGRAM; 50 import static junit.framework.Assert.assertEquals; 51 import static junit.framework.Assert.assertTrue; 52 import static junit.framework.Assert.fail; 53 54 /** 55 * Helper class to test vpn status 56 */ 57 @TargetApi(VERSION_CODES.N) 58 public class VpnTestHelper { 59 public static final String VPN_PACKAGE = "com.android.cts.vpnfirewall"; 60 61 // IP address reserved for documentation by rfc5737 62 public static final String TEST_ADDRESS = "192.0.2.4"; 63 64 // HACK (TODO issue 31585407) to wait for the network to actually be usable 65 private static final int NETWORK_SETTLE_GRACE_MS = 200; 66 67 private static final int SOCKET_TIMEOUT_MS = 5000; 68 private static final int ICMP_ECHO_REQUEST = 0x08; 69 private static final int ICMP_ECHO_REPLY = 0x00; 70 private static final int NETWORK_TIMEOUT_MS = 5000; 71 private static final ComponentName ADMIN_RECEIVER_COMPONENT = 72 BaseDeviceAdminTest.ADMIN_RECEIVER_COMPONENT; 73 74 public static void setAndWaitForVpn(Context context, String packageName, boolean usable) { 75 ConnectivityManager connectivityManager = 76 context.getSystemService(ConnectivityManager.class); 77 DevicePolicyManager dpm = context.getSystemService(DevicePolicyManager.class); 78 final CountDownLatch vpnLatch = new CountDownLatch(1); 79 final NetworkRequest request = new NetworkRequest.Builder() 80 .addTransportType(NetworkCapabilities.TRANSPORT_VPN) 81 .removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VPN) 82 .removeCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET) 83 .build(); 84 final ConnectivityManager.NetworkCallback callback 85 = new ConnectivityManager.NetworkCallback() { 86 @Override 87 public void onAvailable(Network net) { 88 vpnLatch.countDown(); 89 } 90 }; 91 connectivityManager.registerNetworkCallback(request, callback); 92 try { 93 dpm.setAlwaysOnVpnPackage(ADMIN_RECEIVER_COMPONENT, packageName, true); 94 assertEquals(packageName, dpm.getAlwaysOnVpnPackage(ADMIN_RECEIVER_COMPONENT)); 95 if (!vpnLatch.await(NETWORK_TIMEOUT_MS, TimeUnit.MILLISECONDS)) { 96 fail("Took too long waiting to establish a VPN-backed connection"); 97 } 98 Thread.sleep(NETWORK_SETTLE_GRACE_MS); 99 } catch (InterruptedException | PackageManager.NameNotFoundException e) { 100 fail("Failed to send ping: " + e); 101 } finally { 102 connectivityManager.unregisterNetworkCallback(callback); 103 } 104 105 // Do we have a network? 106 NetworkInfo vpnInfo = connectivityManager.getNetworkInfo(ConnectivityManager.TYPE_VPN); 107 assertTrue(vpnInfo != null); 108 109 // Is it usable? 110 assertEquals(usable, vpnInfo.isConnected()); 111 } 112 113 114 public static boolean isNetworkVpn(Context context) { 115 ConnectivityManager connectivityManager = 116 (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE); 117 Network network = connectivityManager.getActiveNetwork(); 118 NetworkCapabilities capabilities = connectivityManager.getNetworkCapabilities(network); 119 return capabilities != null && capabilities.hasTransport(NetworkCapabilities.TRANSPORT_VPN); 120 } 121 122 public static void checkPing(String host) throws ErrnoException, IOException { 123 FileDescriptor socket = Os.socket(AF_INET, SOCK_DGRAM, IPPROTO_ICMP); 124 125 // Create an ICMP message 126 final int identifier = 0x7E57; 127 final String message = "test packet"; 128 byte[] echo = createIcmpMessage(ICMP_ECHO_REQUEST, 0x00, identifier, 0, message.getBytes()); 129 130 // Send the echo packet. 131 int port = new InetSocketAddress(0).getPort(); 132 Os.connect(socket, InetAddress.getByName(host), port); 133 Os.write(socket, echo, 0, echo.length); 134 135 // Expect a reply. 136 StructPollfd pollfd = new StructPollfd(); 137 pollfd.events = (short) POLLIN; 138 pollfd.fd = socket; 139 int ret = Os.poll(new StructPollfd[] { pollfd }, SOCKET_TIMEOUT_MS); 140 assertEquals("Expected reply after sending ping", 1, ret); 141 142 byte[] reply = new byte[echo.length]; 143 int read = Os.read(socket, reply, 0, echo.length); 144 assertEquals(echo.length, read); 145 146 // Ignore control type differences since echo=8, reply=0. 147 assertEquals(echo[0], ICMP_ECHO_REQUEST); 148 assertEquals(reply[0], ICMP_ECHO_REPLY); 149 echo[0] = 0; 150 reply[0] = 0; 151 152 // Fix ICMP ID which kernel will have changed on the way out. 153 InetSocketAddress local = (InetSocketAddress) Os.getsockname(socket); 154 port = local.getPort(); 155 echo[4] = (byte) ((port >> 8) & 0xFF); 156 echo[5] = (byte) (port & 0xFF); 157 158 // Ignore checksum differences since the types are not supposed to match. 159 echo[2] = echo[3] = 0; 160 reply[2] = reply[3] = 0; 161 162 assertTrue("Packet contents do not match." 163 + "\nEcho packet: " + Arrays.toString(echo) 164 + "\nReply packet: " + Arrays.toString(reply), Arrays.equals(echo, reply)); 165 166 // Close socket if the test pass. Otherwise, any error will kill the process. 167 Os.close(socket); 168 } 169 170 public static void tryPosixConnect(String host) throws ErrnoException, IOException { 171 FileDescriptor socket = null; 172 try { 173 socket = Os.socket(AF_INET, SOCK_DGRAM, IPPROTO_ICMP); 174 int port = new InetSocketAddress(0).getPort(); 175 Os.connect(socket, InetAddress.getByName(host), port); 176 } finally { 177 if (socket != null) { 178 Os.close(socket); 179 } 180 } 181 } 182 183 private static byte[] createIcmpMessage(int type, int code, int extra1, int extra2, 184 byte[] data) throws IOException { 185 ByteArrayOutputStream output = new ByteArrayOutputStream(); 186 DataOutputStream stream = new DataOutputStream(output); 187 stream.writeByte(type); 188 stream.writeByte(code); 189 stream.writeShort(/* checksum */ 0); 190 stream.writeShort((short) extra1); 191 stream.writeShort((short) extra2); 192 stream.write(data, 0, data.length); 193 return output.toByteArray(); 194 } 195 } 196