Give VPNs the INTERNET capability when they route most of the IP space
Test: manual, plus wrote some new tests for this
Bug: 72765718
Change-Id: I9759da72b752fd8eeb1d0647db9ab341f04c0528
diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java
index bb46d5e..c9bdcf1 100644
--- a/services/core/java/com/android/server/connectivity/Vpn.java
+++ b/services/core/java/com/android/server/connectivity/Vpn.java
@@ -57,6 +57,7 @@
import android.net.NetworkInfo;
import android.net.NetworkInfo.DetailedState;
import android.net.NetworkMisc;
+import android.net.NetworkUtils;
import android.net.RouteInfo;
import android.net.UidRange;
import android.net.Uri;
@@ -105,6 +106,7 @@
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
+import java.math.BigInteger;
import java.net.Inet4Address;
import java.net.Inet6Address;
import java.net.InetAddress;
@@ -113,6 +115,7 @@
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
+import java.util.Comparator;
import java.util.List;
import java.util.Set;
import java.util.SortedSet;
@@ -131,6 +134,24 @@
// the device idle whitelist during service launch and VPN bootstrap.
private static final long VPN_LAUNCH_IDLE_WHITELIST_DURATION_MS = 60 * 1000;
+ // Settings for how much of the address space should be routed so that Vpn considers
+ // "most" of the address space is routed. This is used to determine whether this Vpn
+ // should be marked with the INTERNET capability.
+ private static final long MOST_IPV4_ADDRESSES_COUNT;
+ private static final BigInteger MOST_IPV6_ADDRESSES_COUNT;
+ static {
+ // 85% of the address space must be routed for Vpn to consider this VPN to provide
+ // INTERNET access.
+ final int howManyPercentIsMost = 85;
+
+ final long twoPower32 = 1L << 32;
+ MOST_IPV4_ADDRESSES_COUNT = twoPower32 * howManyPercentIsMost / 100;
+ final BigInteger twoPower128 = BigInteger.ONE.shiftLeft(128);
+ MOST_IPV6_ADDRESSES_COUNT = twoPower128
+ .multiply(BigInteger.valueOf(howManyPercentIsMost))
+ .divide(BigInteger.valueOf(100));
+ }
+
// TODO: create separate trackers for each unique VPN to support
// automated reconnection
@@ -830,10 +851,39 @@
return lp;
}
+ /**
+ * Analyzes the passed LinkedProperties to figure out whether it routes to most of the IP space.
+ *
+ * This returns true if the passed LinkedProperties contains routes to either most of the IPv4
+ * space or to most of the IPv6 address space, where "most" is defined by the value of the
+ * MOST_IPV{4,6}_ADDRESSES_COUNT constants : if more than this number of addresses are matched
+ * by any of the routes, then it's decided that most of the space is routed.
+ * @hide
+ */
+ @VisibleForTesting
+ static boolean providesRoutesToMostDestinations(LinkProperties lp) {
+ final Comparator<IpPrefix> prefixLengthComparator = IpPrefix.lengthComparator();
+ TreeSet<IpPrefix> ipv4Prefixes = new TreeSet<>(prefixLengthComparator);
+ TreeSet<IpPrefix> ipv6Prefixes = new TreeSet<>(prefixLengthComparator);
+ for (final RouteInfo route : lp.getAllRoutes()) {
+ IpPrefix destination = route.getDestination();
+ if (destination.isIPv4()) {
+ ipv4Prefixes.add(destination);
+ } else {
+ ipv6Prefixes.add(destination);
+ }
+ }
+ if (NetworkUtils.routedIPv4AddressCount(ipv4Prefixes) > MOST_IPV4_ADDRESSES_COUNT) {
+ return true;
+ }
+ return NetworkUtils.routedIPv6AddressCount(ipv6Prefixes)
+ .compareTo(MOST_IPV6_ADDRESSES_COUNT) >= 0;
+ }
+
private void agentConnect() {
LinkProperties lp = makeLinkProperties();
- if (lp.hasIPv4DefaultRoute() || lp.hasIPv6DefaultRoute()) {
+ if (providesRoutesToMostDestinations(lp)) {
mNetworkCapabilities.addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET);
} else {
mNetworkCapabilities.removeCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET);