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 android.content.Intent; 20 import android.content.pm.PackageManager.NameNotFoundException; 21 import android.net.IpPrefix; 22 import android.net.Network; 23 import android.net.NetworkUtils; 24 import android.net.ProxyInfo; 25 import android.net.VpnService; 26 import android.os.ParcelFileDescriptor; 27 import android.text.TextUtils; 28 import android.util.Log; 29 import android.util.Pair; 30 31 import com.android.modules.utils.build.SdkLevel; 32 import com.android.networkstack.apishim.VpnServiceBuilderShimImpl; 33 import com.android.networkstack.apishim.common.UnsupportedApiLevelException; 34 import com.android.networkstack.apishim.common.VpnServiceBuilderShim; 35 import com.android.testutils.PacketReflector; 36 37 import java.io.IOException; 38 import java.net.InetAddress; 39 import java.util.ArrayList; 40 import java.util.function.BiConsumer; 41 import java.util.function.Consumer; 42 43 public class MyVpnService extends VpnService { 44 45 private static String TAG = "MyVpnService"; 46 private static int MTU = 1799; 47 48 public static final String ACTION_ESTABLISHED = "com.android.cts.net.hostside.ESTABNLISHED"; 49 public static final String EXTRA_ALWAYS_ON = "is-always-on"; 50 public static final String EXTRA_LOCKDOWN_ENABLED = "is-lockdown-enabled"; 51 public static final String CMD_CONNECT = "connect"; 52 public static final String CMD_DISCONNECT = "disconnect"; 53 public static final String CMD_UPDATE_UNDERLYING_NETWORKS = "update_underlying_networks"; 54 55 private ParcelFileDescriptor mFd = null; 56 private PacketReflector mPacketReflector = null; 57 58 @Override onStartCommand(Intent intent, int flags, int startId)59 public int onStartCommand(Intent intent, int flags, int startId) { 60 String packageName = getPackageName(); 61 String cmd = intent.getStringExtra(packageName + ".cmd"); 62 if (CMD_DISCONNECT.equals(cmd)) { 63 stop(); 64 } else if (CMD_CONNECT.equals(cmd)) { 65 start(packageName, intent); 66 } else if (CMD_UPDATE_UNDERLYING_NETWORKS.equals(cmd)) { 67 updateUnderlyingNetworks(packageName, intent); 68 } 69 70 return START_NOT_STICKY; 71 } 72 updateUnderlyingNetworks(String packageName, Intent intent)73 private void updateUnderlyingNetworks(String packageName, Intent intent) { 74 final ArrayList<Network> underlyingNetworks = 75 intent.getParcelableArrayListExtra(packageName + ".underlyingNetworks"); 76 setUnderlyingNetworks( 77 (underlyingNetworks != null) ? underlyingNetworks.toArray(new Network[0]) : null); 78 } 79 parseIpAndMaskListArgument(String packageName, Intent intent, String argName, BiConsumer<InetAddress, Integer> consumer)80 private String parseIpAndMaskListArgument(String packageName, Intent intent, String argName, 81 BiConsumer<InetAddress, Integer> consumer) { 82 final String addresses = intent.getStringExtra(packageName + "." + argName); 83 84 if (TextUtils.isEmpty(addresses)) { 85 return null; 86 } 87 88 final String[] addressesArray = addresses.split(","); 89 for (String address : addressesArray) { 90 final Pair<InetAddress, Integer> ipAndMask = NetworkUtils.parseIpAndMask(address); 91 consumer.accept(ipAndMask.first, ipAndMask.second); 92 } 93 94 return addresses; 95 } 96 parseIpPrefixListArgument(String packageName, Intent intent, String argName, Consumer<IpPrefix> consumer)97 private String parseIpPrefixListArgument(String packageName, Intent intent, String argName, 98 Consumer<IpPrefix> consumer) { 99 return parseIpAndMaskListArgument(packageName, intent, argName, 100 (inetAddress, prefixLength) -> consumer.accept( 101 new IpPrefix(inetAddress, prefixLength))); 102 } 103 start(String packageName, Intent intent)104 private void start(String packageName, Intent intent) { 105 Builder builder = new Builder(); 106 VpnServiceBuilderShim vpnServiceBuilderShim = VpnServiceBuilderShimImpl.newInstance(); 107 108 final String addresses = parseIpAndMaskListArgument(packageName, intent, "addresses", 109 builder::addAddress); 110 111 String addedRoutes; 112 if (SdkLevel.isAtLeastT() && intent.getBooleanExtra(packageName + ".addRoutesByIpPrefix", 113 false)) { 114 addedRoutes = parseIpPrefixListArgument(packageName, intent, "routes", (prefix) -> { 115 try { 116 vpnServiceBuilderShim.addRoute(builder, prefix); 117 } catch (UnsupportedApiLevelException e) { 118 throw new RuntimeException(e); 119 } 120 }); 121 } else { 122 addedRoutes = parseIpAndMaskListArgument(packageName, intent, "routes", 123 builder::addRoute); 124 } 125 126 String excludedRoutes = null; 127 if (SdkLevel.isAtLeastT()) { 128 excludedRoutes = parseIpPrefixListArgument(packageName, intent, "excludedRoutes", 129 (prefix) -> { 130 try { 131 vpnServiceBuilderShim.excludeRoute(builder, prefix); 132 } catch (UnsupportedApiLevelException e) { 133 throw new RuntimeException(e); 134 } 135 }); 136 } 137 138 String allowed = intent.getStringExtra(packageName + ".allowedapplications"); 139 if (allowed != null) { 140 String[] packageArray = allowed.split(","); 141 for (int i = 0; i < packageArray.length; i++) { 142 String allowedPackage = packageArray[i]; 143 if (!TextUtils.isEmpty(allowedPackage)) { 144 try { 145 builder.addAllowedApplication(allowedPackage); 146 } catch(NameNotFoundException e) { 147 continue; 148 } 149 } 150 } 151 } 152 153 String disallowed = intent.getStringExtra(packageName + ".disallowedapplications"); 154 if (disallowed != null) { 155 String[] packageArray = disallowed.split(","); 156 for (int i = 0; i < packageArray.length; i++) { 157 String disallowedPackage = packageArray[i]; 158 if (!TextUtils.isEmpty(disallowedPackage)) { 159 try { 160 builder.addDisallowedApplication(disallowedPackage); 161 } catch(NameNotFoundException e) { 162 continue; 163 } 164 } 165 } 166 } 167 168 ArrayList<Network> underlyingNetworks = 169 intent.getParcelableArrayListExtra(packageName + ".underlyingNetworks"); 170 if (underlyingNetworks == null) { 171 // VPN tracks default network 172 builder.setUnderlyingNetworks(null); 173 } else { 174 builder.setUnderlyingNetworks(underlyingNetworks.toArray(new Network[0])); 175 } 176 177 boolean isAlwaysMetered = intent.getBooleanExtra(packageName + ".isAlwaysMetered", false); 178 builder.setMetered(isAlwaysMetered); 179 180 ProxyInfo vpnProxy = intent.getParcelableExtra(packageName + ".httpProxy"); 181 builder.setHttpProxy(vpnProxy); 182 builder.setMtu(MTU); 183 builder.setBlocking(true); 184 builder.setSession("MyVpnService"); 185 186 Log.i(TAG, "Establishing VPN," 187 + " addresses=" + addresses 188 + " addedRoutes=" + addedRoutes 189 + " excludedRoutes=" + excludedRoutes 190 + " allowedApplications=" + allowed 191 + " disallowedApplications=" + disallowed); 192 193 mFd = builder.establish(); 194 Log.i(TAG, "Established, fd=" + (mFd == null ? "null" : mFd.getFd())); 195 196 broadcastEstablished(); 197 198 mPacketReflector = new PacketReflector(mFd.getFileDescriptor(), MTU); 199 mPacketReflector.start(); 200 } 201 broadcastEstablished()202 private void broadcastEstablished() { 203 final Intent bcIntent = new Intent(ACTION_ESTABLISHED); 204 bcIntent.putExtra(EXTRA_ALWAYS_ON, isAlwaysOn()); 205 bcIntent.putExtra(EXTRA_LOCKDOWN_ENABLED, isLockdownEnabled()); 206 sendBroadcast(bcIntent); 207 } 208 stop()209 private void stop() { 210 if (mPacketReflector != null) { 211 mPacketReflector.interrupt(); 212 mPacketReflector = null; 213 } 214 try { 215 if (mFd != null) { 216 Log.i(TAG, "Closing filedescriptor"); 217 mFd.close(); 218 } 219 } catch(IOException e) { 220 } finally { 221 mFd = null; 222 } 223 } 224 225 @Override onDestroy()226 public void onDestroy() { 227 stop(); 228 super.onDestroy(); 229 } 230 } 231