Loading core/java/android/net/NetworkUtils.java +16 −60 Original line number Original line Diff line number Diff line Loading @@ -29,7 +29,6 @@ import android.util.Log; import android.util.Pair; import android.util.Pair; import java.io.FileDescriptor; import java.io.FileDescriptor; import java.math.BigInteger; import java.net.Inet4Address; import java.net.Inet4Address; import java.net.Inet6Address; import java.net.Inet6Address; import java.net.InetAddress; import java.net.InetAddress; Loading @@ -37,7 +36,6 @@ import java.net.SocketException; import java.net.UnknownHostException; import java.net.UnknownHostException; import java.util.Collection; import java.util.Collection; import java.util.Locale; import java.util.Locale; import java.util.TreeSet; /** /** * Native methods for managing network interfaces. * Native methods for managing network interfaces. Loading Loading @@ -392,72 +390,30 @@ public class NetworkUtils { return result; return result; } } /** private static final int[] ADDRESS_FAMILIES = new int[] {AF_INET, AF_INET6}; * Returns a prefix set without overlaps. * * This expects the src set to be sorted from shorter to longer. Results are undefined * failing this condition. The returned prefix set is sorted in the same order as the * passed set, with the same comparator. */ private static TreeSet<IpPrefix> deduplicatePrefixSet(final TreeSet<IpPrefix> src) { final TreeSet<IpPrefix> dst = new TreeSet<>(src.comparator()); // Prefixes match addresses that share their upper part up to their length, therefore // the only kind of possible overlap in two prefixes is strict inclusion of the longer // (more restrictive) in the shorter (including equivalence if they have the same // length). // Because prefixes in the src set are sorted from shorter to longer, deduplicating // is done by simply iterating in order, and not adding any longer prefix that is // already covered by a shorter one. newPrefixes: for (IpPrefix newPrefix : src) { for (IpPrefix existingPrefix : dst) { if (existingPrefix.containsPrefix(newPrefix)) { continue newPrefixes; } } dst.add(newPrefix); } return dst; } /** /** * Returns how many IPv4 addresses match any of the prefixes in the passed ordered set. * Returns true if the hostname is weakly validated. * * @param hostname Name of host to validate. * Obviously this returns an integral value between 0 and 2**32. * @return True if it's a valid-ish hostname. * The behavior is undefined if any of the prefixes is not an IPv4 prefix or if the * set is not ordered smallest prefix to longer prefix. * * * @param prefixes the set of prefixes, ordered by length * @hide */ */ public static long routedIPv4AddressCount(final TreeSet<IpPrefix> prefixes) { public static boolean isWeaklyValidatedHostname(@NonNull String hostname) { long routedIPCount = 0; // TODO(b/34953048): Use a validation method that permits more accurate, for (final IpPrefix prefix : deduplicatePrefixSet(prefixes)) { // but still inexpensive, checking of likely valid DNS hostnames. if (!prefix.isIPv4()) { final String weakHostnameRegex = "^[a-zA-Z0-9_.-]+$"; Log.wtf(TAG, "Non-IPv4 prefix in routedIPv4AddressCount"); if (!hostname.matches(weakHostnameRegex)) { } return false; int rank = 32 - prefix.getPrefixLength(); routedIPCount += 1L << rank; } return routedIPCount; } } /** for (int address_family : ADDRESS_FAMILIES) { * Returns how many IPv6 addresses match any of the prefixes in the passed ordered set. if (Os.inet_pton(address_family, hostname) != null) { * return false; * This returns a BigInteger between 0 and 2**128. * The behavior is undefined if any of the prefixes is not an IPv6 prefix or if the * set is not ordered smallest prefix to longer prefix. */ public static BigInteger routedIPv6AddressCount(final TreeSet<IpPrefix> prefixes) { BigInteger routedIPCount = BigInteger.ZERO; for (final IpPrefix prefix : deduplicatePrefixSet(prefixes)) { if (!prefix.isIPv6()) { Log.wtf(TAG, "Non-IPv6 prefix in routedIPv6AddressCount"); } } int rank = 128 - prefix.getPrefixLength(); routedIPCount = routedIPCount.add(BigInteger.ONE.shiftLeft(rank)); } } return routedIPCount; return true; } } private static final int[] ADDRESS_FAMILIES = new int[] {AF_INET, AF_INET6}; private static final int[] ADDRESS_FAMILIES = new int[] {AF_INET, AF_INET6}; Loading services/core/java/com/android/server/connectivity/Vpn.java +9 −71 Original line number Original line Diff line number Diff line Loading @@ -58,7 +58,6 @@ import android.net.NetworkFactory; import android.net.NetworkInfo; import android.net.NetworkInfo; import android.net.NetworkInfo.DetailedState; import android.net.NetworkInfo.DetailedState; import android.net.NetworkMisc; import android.net.NetworkMisc; import android.net.NetworkUtils; import android.net.RouteInfo; import android.net.RouteInfo; import android.net.UidRange; import android.net.UidRange; import android.net.VpnService; import android.net.VpnService; Loading Loading @@ -105,7 +104,6 @@ import java.io.File; import java.io.IOException; import java.io.IOException; import java.io.InputStream; import java.io.InputStream; import java.io.OutputStream; import java.io.OutputStream; import java.math.BigInteger; import java.net.Inet4Address; import java.net.Inet4Address; import java.net.Inet6Address; import java.net.Inet6Address; import java.net.InetAddress; import java.net.InetAddress; Loading @@ -114,7 +112,6 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Arrays; import java.util.Collection; import java.util.Collection; import java.util.Collections; import java.util.Collections; import java.util.Comparator; import java.util.List; import java.util.List; import java.util.Objects; import java.util.Objects; import java.util.Set; import java.util.Set; Loading @@ -134,31 +131,6 @@ public class Vpn { // the device idle whitelist during service launch and VPN bootstrap. // the device idle whitelist during service launch and VPN bootstrap. private static final long VPN_LAUNCH_IDLE_WHITELIST_DURATION_MS = 60 * 1000; 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)); } // How many routes to evaluate before bailing and declaring this Vpn should provide // the INTERNET capability. This is necessary because computing the address space is // O(n²) and this is running in the system service, so a limit is needed to alleviate // the risk of attack. // This is taken as a total of IPv4 + IPV6 routes for simplicity, but the algorithm // is actually O(n²)+O(n²). private static final int MAX_ROUTES_TO_EVALUATE = 150; // TODO: create separate trackers for each unique VPN to support // TODO: create separate trackers for each unique VPN to support // automated reconnection // automated reconnection Loading Loading @@ -256,7 +228,7 @@ public class Vpn { } } /** /** * Update current state, dispaching event to listeners. * Update current state, dispatching event to listeners. */ */ @VisibleForTesting @VisibleForTesting protected void updateState(DetailedState detailedState, String reason) { protected void updateState(DetailedState detailedState, String reason) { Loading Loading @@ -301,7 +273,7 @@ public class Vpn { } } @VisibleForTesting @VisibleForTesting public static void applyUnderlyingCapabilities( static void applyUnderlyingCapabilities( ConnectivityManager cm, ConnectivityManager cm, Network[] underlyingNetworks, Network[] underlyingNetworks, NetworkCapabilities caps, NetworkCapabilities caps, Loading Loading @@ -901,38 +873,6 @@ public class Vpn { return lp; 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 List<RouteInfo> routes = lp.getAllRoutes(); if (routes.size() > MAX_ROUTES_TO_EVALUATE) return true; final Comparator<IpPrefix> prefixLengthComparator = IpPrefix.lengthComparator(); TreeSet<IpPrefix> ipv4Prefixes = new TreeSet<>(prefixLengthComparator); TreeSet<IpPrefix> ipv6Prefixes = new TreeSet<>(prefixLengthComparator); for (final RouteInfo route : routes) { if (route.getType() == RouteInfo.RTN_UNREACHABLE) continue; 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; } /** /** * Attempt to perform a seamless handover of VPNs by only updating LinkProperties without * Attempt to perform a seamless handover of VPNs by only updating LinkProperties without * registering a new NetworkAgent. This is not always possible if the new VPN configuration * registering a new NetworkAgent. This is not always possible if the new VPN configuration Loading Loading @@ -1765,7 +1705,7 @@ public class Vpn { byte[] value = keyStore.get(Credentials.USER_CERTIFICATE + profile.ipsecServerCert); byte[] value = keyStore.get(Credentials.USER_CERTIFICATE + profile.ipsecServerCert); serverCert = (value == null) ? null : new String(value, StandardCharsets.UTF_8); serverCert = (value == null) ? null : new String(value, StandardCharsets.UTF_8); } } if (privateKey == null || userCert == null || caCert == null || serverCert == null) { if (userCert == null || caCert == null || serverCert == null) { throw new IllegalStateException("Cannot load credentials"); throw new IllegalStateException("Cannot load credentials"); } } Loading Loading @@ -1884,7 +1824,7 @@ public class Vpn { * Return the information of the current ongoing legacy VPN. * Return the information of the current ongoing legacy VPN. * Callers are responsible for checking permissions if needed. * Callers are responsible for checking permissions if needed. */ */ public synchronized LegacyVpnInfo getLegacyVpnInfoPrivileged() { private synchronized LegacyVpnInfo getLegacyVpnInfoPrivileged() { if (mLegacyVpnRunner == null) return null; if (mLegacyVpnRunner == null) return null; final LegacyVpnInfo info = new LegacyVpnInfo(); final LegacyVpnInfo info = new LegacyVpnInfo(); Loading Loading @@ -2038,7 +1978,6 @@ public class Vpn { private void bringup() { private void bringup() { // Catch all exceptions so we can clean up a few things. // Catch all exceptions so we can clean up a few things. boolean initFinished = false; try { try { // Initialize the timer. // Initialize the timer. mBringupStartTime = SystemClock.elapsedRealtime(); mBringupStartTime = SystemClock.elapsedRealtime(); Loading @@ -2057,7 +1996,6 @@ public class Vpn { throw new IllegalStateException("Cannot delete the state"); throw new IllegalStateException("Cannot delete the state"); } } new File("/data/misc/vpn/abort").delete(); new File("/data/misc/vpn/abort").delete(); initFinished = true; // Check if we need to restart any of the daemons. // Check if we need to restart any of the daemons. boolean restart = false; boolean restart = false; Loading tests/net/java/android/net/NetworkUtilsTest.javadeleted 100644 → 0 +0 −128 Original line number Original line Diff line number Diff line /* * Copyright (C) 2015 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.net; import static junit.framework.Assert.assertEquals; import androidx.test.runner.AndroidJUnit4; import org.junit.Test; import org.junit.runner.RunWith; import java.math.BigInteger; import java.util.TreeSet; @RunWith(AndroidJUnit4.class) @androidx.test.filters.SmallTest public class NetworkUtilsTest { @Test public void testRoutedIPv4AddressCount() { final TreeSet<IpPrefix> set = new TreeSet<>(IpPrefix.lengthComparator()); // No routes routes to no addresses. assertEquals(0, NetworkUtils.routedIPv4AddressCount(set)); set.add(new IpPrefix("0.0.0.0/0")); assertEquals(1l << 32, NetworkUtils.routedIPv4AddressCount(set)); set.add(new IpPrefix("20.18.0.0/16")); set.add(new IpPrefix("20.18.0.0/24")); set.add(new IpPrefix("20.18.0.0/8")); // There is a default route, still covers everything assertEquals(1l << 32, NetworkUtils.routedIPv4AddressCount(set)); set.clear(); set.add(new IpPrefix("20.18.0.0/24")); set.add(new IpPrefix("20.18.0.0/8")); // The 8-length includes the 24-length prefix assertEquals(1l << 24, NetworkUtils.routedIPv4AddressCount(set)); set.add(new IpPrefix("10.10.10.126/25")); // The 8-length does not include this 25-length prefix assertEquals((1l << 24) + (1 << 7), NetworkUtils.routedIPv4AddressCount(set)); set.clear(); set.add(new IpPrefix("1.2.3.4/32")); set.add(new IpPrefix("1.2.3.4/32")); set.add(new IpPrefix("1.2.3.4/32")); set.add(new IpPrefix("1.2.3.4/32")); assertEquals(1l, NetworkUtils.routedIPv4AddressCount(set)); set.add(new IpPrefix("1.2.3.5/32")); set.add(new IpPrefix("1.2.3.6/32")); set.add(new IpPrefix("1.2.3.7/32")); set.add(new IpPrefix("1.2.3.8/32")); set.add(new IpPrefix("1.2.3.9/32")); set.add(new IpPrefix("1.2.3.0/32")); assertEquals(7l, NetworkUtils.routedIPv4AddressCount(set)); // 1.2.3.4/30 eats 1.2.3.{4-7}/32 set.add(new IpPrefix("1.2.3.4/30")); set.add(new IpPrefix("6.2.3.4/28")); set.add(new IpPrefix("120.2.3.4/16")); assertEquals(7l - 4 + 4 + 16 + 65536, NetworkUtils.routedIPv4AddressCount(set)); } @Test public void testRoutedIPv6AddressCount() { final TreeSet<IpPrefix> set = new TreeSet<>(IpPrefix.lengthComparator()); // No routes routes to no addresses. assertEquals(BigInteger.ZERO, NetworkUtils.routedIPv6AddressCount(set)); set.add(new IpPrefix("::/0")); assertEquals(BigInteger.ONE.shiftLeft(128), NetworkUtils.routedIPv6AddressCount(set)); set.add(new IpPrefix("1234:622a::18/64")); set.add(new IpPrefix("add4:f00:80:f7:1111::6adb/96")); set.add(new IpPrefix("add4:f00:80:f7:1111::6adb/8")); // There is a default route, still covers everything assertEquals(BigInteger.ONE.shiftLeft(128), NetworkUtils.routedIPv6AddressCount(set)); set.clear(); set.add(new IpPrefix("add4:f00:80:f7:1111::6adb/96")); set.add(new IpPrefix("add4:f00:80:f7:1111::6adb/8")); // The 8-length includes the 96-length prefix assertEquals(BigInteger.ONE.shiftLeft(120), NetworkUtils.routedIPv6AddressCount(set)); set.add(new IpPrefix("10::26/64")); // The 8-length does not include this 64-length prefix assertEquals(BigInteger.ONE.shiftLeft(120).add(BigInteger.ONE.shiftLeft(64)), NetworkUtils.routedIPv6AddressCount(set)); set.clear(); set.add(new IpPrefix("add4:f00:80:f7:1111::6ad4/128")); set.add(new IpPrefix("add4:f00:80:f7:1111::6ad4/128")); set.add(new IpPrefix("add4:f00:80:f7:1111::6ad4/128")); set.add(new IpPrefix("add4:f00:80:f7:1111::6ad4/128")); assertEquals(BigInteger.ONE, NetworkUtils.routedIPv6AddressCount(set)); set.add(new IpPrefix("add4:f00:80:f7:1111::6ad5/128")); set.add(new IpPrefix("add4:f00:80:f7:1111::6ad6/128")); set.add(new IpPrefix("add4:f00:80:f7:1111::6ad7/128")); set.add(new IpPrefix("add4:f00:80:f7:1111::6ad8/128")); set.add(new IpPrefix("add4:f00:80:f7:1111::6ad9/128")); set.add(new IpPrefix("add4:f00:80:f7:1111::6ad0/128")); assertEquals(BigInteger.valueOf(7), NetworkUtils.routedIPv6AddressCount(set)); // add4:f00:80:f7:1111::6ad4/126 eats add4:f00:8[:f7:1111::6ad{4-7}/128 set.add(new IpPrefix("add4:f00:80:f7:1111::6ad4/126")); set.add(new IpPrefix("d00d:f00:80:f7:1111::6ade/124")); set.add(new IpPrefix("f00b:a33::/112")); assertEquals(BigInteger.valueOf(7l - 4 + 4 + 16 + 65536), NetworkUtils.routedIPv6AddressCount(set)); } } Loading
core/java/android/net/NetworkUtils.java +16 −60 Original line number Original line Diff line number Diff line Loading @@ -29,7 +29,6 @@ import android.util.Log; import android.util.Pair; import android.util.Pair; import java.io.FileDescriptor; import java.io.FileDescriptor; import java.math.BigInteger; import java.net.Inet4Address; import java.net.Inet4Address; import java.net.Inet6Address; import java.net.Inet6Address; import java.net.InetAddress; import java.net.InetAddress; Loading @@ -37,7 +36,6 @@ import java.net.SocketException; import java.net.UnknownHostException; import java.net.UnknownHostException; import java.util.Collection; import java.util.Collection; import java.util.Locale; import java.util.Locale; import java.util.TreeSet; /** /** * Native methods for managing network interfaces. * Native methods for managing network interfaces. Loading Loading @@ -392,72 +390,30 @@ public class NetworkUtils { return result; return result; } } /** private static final int[] ADDRESS_FAMILIES = new int[] {AF_INET, AF_INET6}; * Returns a prefix set without overlaps. * * This expects the src set to be sorted from shorter to longer. Results are undefined * failing this condition. The returned prefix set is sorted in the same order as the * passed set, with the same comparator. */ private static TreeSet<IpPrefix> deduplicatePrefixSet(final TreeSet<IpPrefix> src) { final TreeSet<IpPrefix> dst = new TreeSet<>(src.comparator()); // Prefixes match addresses that share their upper part up to their length, therefore // the only kind of possible overlap in two prefixes is strict inclusion of the longer // (more restrictive) in the shorter (including equivalence if they have the same // length). // Because prefixes in the src set are sorted from shorter to longer, deduplicating // is done by simply iterating in order, and not adding any longer prefix that is // already covered by a shorter one. newPrefixes: for (IpPrefix newPrefix : src) { for (IpPrefix existingPrefix : dst) { if (existingPrefix.containsPrefix(newPrefix)) { continue newPrefixes; } } dst.add(newPrefix); } return dst; } /** /** * Returns how many IPv4 addresses match any of the prefixes in the passed ordered set. * Returns true if the hostname is weakly validated. * * @param hostname Name of host to validate. * Obviously this returns an integral value between 0 and 2**32. * @return True if it's a valid-ish hostname. * The behavior is undefined if any of the prefixes is not an IPv4 prefix or if the * set is not ordered smallest prefix to longer prefix. * * * @param prefixes the set of prefixes, ordered by length * @hide */ */ public static long routedIPv4AddressCount(final TreeSet<IpPrefix> prefixes) { public static boolean isWeaklyValidatedHostname(@NonNull String hostname) { long routedIPCount = 0; // TODO(b/34953048): Use a validation method that permits more accurate, for (final IpPrefix prefix : deduplicatePrefixSet(prefixes)) { // but still inexpensive, checking of likely valid DNS hostnames. if (!prefix.isIPv4()) { final String weakHostnameRegex = "^[a-zA-Z0-9_.-]+$"; Log.wtf(TAG, "Non-IPv4 prefix in routedIPv4AddressCount"); if (!hostname.matches(weakHostnameRegex)) { } return false; int rank = 32 - prefix.getPrefixLength(); routedIPCount += 1L << rank; } return routedIPCount; } } /** for (int address_family : ADDRESS_FAMILIES) { * Returns how many IPv6 addresses match any of the prefixes in the passed ordered set. if (Os.inet_pton(address_family, hostname) != null) { * return false; * This returns a BigInteger between 0 and 2**128. * The behavior is undefined if any of the prefixes is not an IPv6 prefix or if the * set is not ordered smallest prefix to longer prefix. */ public static BigInteger routedIPv6AddressCount(final TreeSet<IpPrefix> prefixes) { BigInteger routedIPCount = BigInteger.ZERO; for (final IpPrefix prefix : deduplicatePrefixSet(prefixes)) { if (!prefix.isIPv6()) { Log.wtf(TAG, "Non-IPv6 prefix in routedIPv6AddressCount"); } } int rank = 128 - prefix.getPrefixLength(); routedIPCount = routedIPCount.add(BigInteger.ONE.shiftLeft(rank)); } } return routedIPCount; return true; } } private static final int[] ADDRESS_FAMILIES = new int[] {AF_INET, AF_INET6}; private static final int[] ADDRESS_FAMILIES = new int[] {AF_INET, AF_INET6}; Loading
services/core/java/com/android/server/connectivity/Vpn.java +9 −71 Original line number Original line Diff line number Diff line Loading @@ -58,7 +58,6 @@ import android.net.NetworkFactory; import android.net.NetworkInfo; import android.net.NetworkInfo; import android.net.NetworkInfo.DetailedState; import android.net.NetworkInfo.DetailedState; import android.net.NetworkMisc; import android.net.NetworkMisc; import android.net.NetworkUtils; import android.net.RouteInfo; import android.net.RouteInfo; import android.net.UidRange; import android.net.UidRange; import android.net.VpnService; import android.net.VpnService; Loading Loading @@ -105,7 +104,6 @@ import java.io.File; import java.io.IOException; import java.io.IOException; import java.io.InputStream; import java.io.InputStream; import java.io.OutputStream; import java.io.OutputStream; import java.math.BigInteger; import java.net.Inet4Address; import java.net.Inet4Address; import java.net.Inet6Address; import java.net.Inet6Address; import java.net.InetAddress; import java.net.InetAddress; Loading @@ -114,7 +112,6 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Arrays; import java.util.Collection; import java.util.Collection; import java.util.Collections; import java.util.Collections; import java.util.Comparator; import java.util.List; import java.util.List; import java.util.Objects; import java.util.Objects; import java.util.Set; import java.util.Set; Loading @@ -134,31 +131,6 @@ public class Vpn { // the device idle whitelist during service launch and VPN bootstrap. // the device idle whitelist during service launch and VPN bootstrap. private static final long VPN_LAUNCH_IDLE_WHITELIST_DURATION_MS = 60 * 1000; 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)); } // How many routes to evaluate before bailing and declaring this Vpn should provide // the INTERNET capability. This is necessary because computing the address space is // O(n²) and this is running in the system service, so a limit is needed to alleviate // the risk of attack. // This is taken as a total of IPv4 + IPV6 routes for simplicity, but the algorithm // is actually O(n²)+O(n²). private static final int MAX_ROUTES_TO_EVALUATE = 150; // TODO: create separate trackers for each unique VPN to support // TODO: create separate trackers for each unique VPN to support // automated reconnection // automated reconnection Loading Loading @@ -256,7 +228,7 @@ public class Vpn { } } /** /** * Update current state, dispaching event to listeners. * Update current state, dispatching event to listeners. */ */ @VisibleForTesting @VisibleForTesting protected void updateState(DetailedState detailedState, String reason) { protected void updateState(DetailedState detailedState, String reason) { Loading Loading @@ -301,7 +273,7 @@ public class Vpn { } } @VisibleForTesting @VisibleForTesting public static void applyUnderlyingCapabilities( static void applyUnderlyingCapabilities( ConnectivityManager cm, ConnectivityManager cm, Network[] underlyingNetworks, Network[] underlyingNetworks, NetworkCapabilities caps, NetworkCapabilities caps, Loading Loading @@ -901,38 +873,6 @@ public class Vpn { return lp; 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 List<RouteInfo> routes = lp.getAllRoutes(); if (routes.size() > MAX_ROUTES_TO_EVALUATE) return true; final Comparator<IpPrefix> prefixLengthComparator = IpPrefix.lengthComparator(); TreeSet<IpPrefix> ipv4Prefixes = new TreeSet<>(prefixLengthComparator); TreeSet<IpPrefix> ipv6Prefixes = new TreeSet<>(prefixLengthComparator); for (final RouteInfo route : routes) { if (route.getType() == RouteInfo.RTN_UNREACHABLE) continue; 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; } /** /** * Attempt to perform a seamless handover of VPNs by only updating LinkProperties without * Attempt to perform a seamless handover of VPNs by only updating LinkProperties without * registering a new NetworkAgent. This is not always possible if the new VPN configuration * registering a new NetworkAgent. This is not always possible if the new VPN configuration Loading Loading @@ -1765,7 +1705,7 @@ public class Vpn { byte[] value = keyStore.get(Credentials.USER_CERTIFICATE + profile.ipsecServerCert); byte[] value = keyStore.get(Credentials.USER_CERTIFICATE + profile.ipsecServerCert); serverCert = (value == null) ? null : new String(value, StandardCharsets.UTF_8); serverCert = (value == null) ? null : new String(value, StandardCharsets.UTF_8); } } if (privateKey == null || userCert == null || caCert == null || serverCert == null) { if (userCert == null || caCert == null || serverCert == null) { throw new IllegalStateException("Cannot load credentials"); throw new IllegalStateException("Cannot load credentials"); } } Loading Loading @@ -1884,7 +1824,7 @@ public class Vpn { * Return the information of the current ongoing legacy VPN. * Return the information of the current ongoing legacy VPN. * Callers are responsible for checking permissions if needed. * Callers are responsible for checking permissions if needed. */ */ public synchronized LegacyVpnInfo getLegacyVpnInfoPrivileged() { private synchronized LegacyVpnInfo getLegacyVpnInfoPrivileged() { if (mLegacyVpnRunner == null) return null; if (mLegacyVpnRunner == null) return null; final LegacyVpnInfo info = new LegacyVpnInfo(); final LegacyVpnInfo info = new LegacyVpnInfo(); Loading Loading @@ -2038,7 +1978,6 @@ public class Vpn { private void bringup() { private void bringup() { // Catch all exceptions so we can clean up a few things. // Catch all exceptions so we can clean up a few things. boolean initFinished = false; try { try { // Initialize the timer. // Initialize the timer. mBringupStartTime = SystemClock.elapsedRealtime(); mBringupStartTime = SystemClock.elapsedRealtime(); Loading @@ -2057,7 +1996,6 @@ public class Vpn { throw new IllegalStateException("Cannot delete the state"); throw new IllegalStateException("Cannot delete the state"); } } new File("/data/misc/vpn/abort").delete(); new File("/data/misc/vpn/abort").delete(); initFinished = true; // Check if we need to restart any of the daemons. // Check if we need to restart any of the daemons. boolean restart = false; boolean restart = false; Loading
tests/net/java/android/net/NetworkUtilsTest.javadeleted 100644 → 0 +0 −128 Original line number Original line Diff line number Diff line /* * Copyright (C) 2015 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.net; import static junit.framework.Assert.assertEquals; import androidx.test.runner.AndroidJUnit4; import org.junit.Test; import org.junit.runner.RunWith; import java.math.BigInteger; import java.util.TreeSet; @RunWith(AndroidJUnit4.class) @androidx.test.filters.SmallTest public class NetworkUtilsTest { @Test public void testRoutedIPv4AddressCount() { final TreeSet<IpPrefix> set = new TreeSet<>(IpPrefix.lengthComparator()); // No routes routes to no addresses. assertEquals(0, NetworkUtils.routedIPv4AddressCount(set)); set.add(new IpPrefix("0.0.0.0/0")); assertEquals(1l << 32, NetworkUtils.routedIPv4AddressCount(set)); set.add(new IpPrefix("20.18.0.0/16")); set.add(new IpPrefix("20.18.0.0/24")); set.add(new IpPrefix("20.18.0.0/8")); // There is a default route, still covers everything assertEquals(1l << 32, NetworkUtils.routedIPv4AddressCount(set)); set.clear(); set.add(new IpPrefix("20.18.0.0/24")); set.add(new IpPrefix("20.18.0.0/8")); // The 8-length includes the 24-length prefix assertEquals(1l << 24, NetworkUtils.routedIPv4AddressCount(set)); set.add(new IpPrefix("10.10.10.126/25")); // The 8-length does not include this 25-length prefix assertEquals((1l << 24) + (1 << 7), NetworkUtils.routedIPv4AddressCount(set)); set.clear(); set.add(new IpPrefix("1.2.3.4/32")); set.add(new IpPrefix("1.2.3.4/32")); set.add(new IpPrefix("1.2.3.4/32")); set.add(new IpPrefix("1.2.3.4/32")); assertEquals(1l, NetworkUtils.routedIPv4AddressCount(set)); set.add(new IpPrefix("1.2.3.5/32")); set.add(new IpPrefix("1.2.3.6/32")); set.add(new IpPrefix("1.2.3.7/32")); set.add(new IpPrefix("1.2.3.8/32")); set.add(new IpPrefix("1.2.3.9/32")); set.add(new IpPrefix("1.2.3.0/32")); assertEquals(7l, NetworkUtils.routedIPv4AddressCount(set)); // 1.2.3.4/30 eats 1.2.3.{4-7}/32 set.add(new IpPrefix("1.2.3.4/30")); set.add(new IpPrefix("6.2.3.4/28")); set.add(new IpPrefix("120.2.3.4/16")); assertEquals(7l - 4 + 4 + 16 + 65536, NetworkUtils.routedIPv4AddressCount(set)); } @Test public void testRoutedIPv6AddressCount() { final TreeSet<IpPrefix> set = new TreeSet<>(IpPrefix.lengthComparator()); // No routes routes to no addresses. assertEquals(BigInteger.ZERO, NetworkUtils.routedIPv6AddressCount(set)); set.add(new IpPrefix("::/0")); assertEquals(BigInteger.ONE.shiftLeft(128), NetworkUtils.routedIPv6AddressCount(set)); set.add(new IpPrefix("1234:622a::18/64")); set.add(new IpPrefix("add4:f00:80:f7:1111::6adb/96")); set.add(new IpPrefix("add4:f00:80:f7:1111::6adb/8")); // There is a default route, still covers everything assertEquals(BigInteger.ONE.shiftLeft(128), NetworkUtils.routedIPv6AddressCount(set)); set.clear(); set.add(new IpPrefix("add4:f00:80:f7:1111::6adb/96")); set.add(new IpPrefix("add4:f00:80:f7:1111::6adb/8")); // The 8-length includes the 96-length prefix assertEquals(BigInteger.ONE.shiftLeft(120), NetworkUtils.routedIPv6AddressCount(set)); set.add(new IpPrefix("10::26/64")); // The 8-length does not include this 64-length prefix assertEquals(BigInteger.ONE.shiftLeft(120).add(BigInteger.ONE.shiftLeft(64)), NetworkUtils.routedIPv6AddressCount(set)); set.clear(); set.add(new IpPrefix("add4:f00:80:f7:1111::6ad4/128")); set.add(new IpPrefix("add4:f00:80:f7:1111::6ad4/128")); set.add(new IpPrefix("add4:f00:80:f7:1111::6ad4/128")); set.add(new IpPrefix("add4:f00:80:f7:1111::6ad4/128")); assertEquals(BigInteger.ONE, NetworkUtils.routedIPv6AddressCount(set)); set.add(new IpPrefix("add4:f00:80:f7:1111::6ad5/128")); set.add(new IpPrefix("add4:f00:80:f7:1111::6ad6/128")); set.add(new IpPrefix("add4:f00:80:f7:1111::6ad7/128")); set.add(new IpPrefix("add4:f00:80:f7:1111::6ad8/128")); set.add(new IpPrefix("add4:f00:80:f7:1111::6ad9/128")); set.add(new IpPrefix("add4:f00:80:f7:1111::6ad0/128")); assertEquals(BigInteger.valueOf(7), NetworkUtils.routedIPv6AddressCount(set)); // add4:f00:80:f7:1111::6ad4/126 eats add4:f00:8[:f7:1111::6ad{4-7}/128 set.add(new IpPrefix("add4:f00:80:f7:1111::6ad4/126")); set.add(new IpPrefix("d00d:f00:80:f7:1111::6ade/124")); set.add(new IpPrefix("f00b:a33::/112")); assertEquals(BigInteger.valueOf(7l - 4 + 4 + 16 + 65536), NetworkUtils.routedIPv6AddressCount(set)); } }