Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 9fae0a82 authored by Chalard Jean's avatar Chalard Jean Committed by android-build-merger
Browse files

Merge "Give VPNs the INTERNET capability when they route most of the IP space"...

Merge "Give VPNs the INTERNET capability when they route most of the IP space" am: 20013384 am: 18e51821
am: 207b8e2b

Change-Id: Ifd77149ca2c45249b8776c878c6fac46ae81c2a4
parents f1070034 207b8e2b
Loading
Loading
Loading
Loading
+47 −0
Original line number Diff line number Diff line
@@ -25,6 +25,7 @@ import java.net.Inet6Address;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.Arrays;
import java.util.Comparator;

/**
 * This class represents an IP prefix, i.e., a contiguous block of IP addresses aligned on a
@@ -186,6 +187,20 @@ public final class IpPrefix implements Parcelable {
        return Arrays.equals(this.address, addrBytes);
    }

    /**
     * Returns whether the specified prefix is entirely contained in this prefix.
     *
     * Note this is mathematical inclusion, so a prefix is always contained within itself.
     * @param otherPrefix the prefix to test
     * @hide
     */
    public boolean containsPrefix(IpPrefix otherPrefix) {
        if (otherPrefix.getPrefixLength() < prefixLength) return false;
        final byte[] otherAddress = otherPrefix.getRawAddress();
        NetworkUtils.maskRawAddress(otherAddress, prefixLength);
        return Arrays.equals(otherAddress, address);
    }

    /**
     * @hide
     */
@@ -229,6 +244,38 @@ public final class IpPrefix implements Parcelable {
        dest.writeInt(prefixLength);
    }

    /**
     * Returns a comparator ordering IpPrefixes by length, shorter to longer.
     * Contents of the address will break ties.
     * @hide
     */
    public static Comparator<IpPrefix> lengthComparator() {
        return new Comparator<IpPrefix>() {
            @Override
            public int compare(IpPrefix prefix1, IpPrefix prefix2) {
                if (prefix1.isIPv4()) {
                    if (prefix2.isIPv6()) return -1;
                } else {
                    if (prefix2.isIPv4()) return 1;
                }
                final int p1len = prefix1.getPrefixLength();
                final int p2len = prefix2.getPrefixLength();
                if (p1len < p2len) return -1;
                if (p2len < p1len) return 1;
                final byte[] a1 = prefix1.address;
                final byte[] a2 = prefix2.address;
                final int len = a1.length < a2.length ? a1.length : a2.length;
                for (int i = 0; i < len; ++i) {
                    if (a1[i] < a2[i]) return -1;
                    if (a1[i] > a2[i]) return 1;
                }
                if (a2.length < len) return 1;
                if (a1.length < len) return -1;
                return 0;
            }
        };
    }

    /**
     * Implement the Parcelable interface.
     */
+75 −6
Original line number Diff line number Diff line
@@ -16,19 +16,20 @@

package android.net;

import android.os.Parcel;
import android.util.Log;
import android.util.Pair;

import java.io.FileDescriptor;
import java.net.InetAddress;
import java.math.BigInteger;
import java.net.Inet4Address;
import java.net.Inet6Address;
import java.net.InetAddress;
import java.net.SocketException;
import java.net.UnknownHostException;
import java.util.Collection;
import java.util.Locale;

import android.os.Parcel;
import android.util.Log;
import android.util.Pair;

import java.util.TreeSet;

/**
 * Native methods for managing network interfaces.
@@ -385,4 +386,72 @@ public class NetworkUtils {
        result = builder.toString();
        return result;
    }

    /**
     * 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.
     *
     * Obviously this returns an integral value between 0 and 2**32.
     * 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
     */
    public static long routedIPv4AddressCount(final TreeSet<IpPrefix> prefixes) {
        long routedIPCount = 0;
        for (final IpPrefix prefix : deduplicatePrefixSet(prefixes)) {
            if (!prefix.isIPv4()) {
                Log.wtf(TAG, "Non-IPv4 prefix in routedIPv4AddressCount");
            }
            int rank = 32 - prefix.getPrefixLength();
            routedIPCount += 1L << rank;
        }
        return routedIPCount;
    }

    /**
     * Returns how many IPv6 addresses match any of the prefixes in the passed ordered set.
     *
     * 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;
    }
}
+51 −1
Original line number Diff line number Diff line
@@ -57,6 +57,7 @@ import android.net.NetworkCapabilities;
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.File;
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.ArrayList;
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 @@ public class Vpn {
    // 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 @@ public class Vpn {
        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);
+49 −2
Original line number Diff line number Diff line
@@ -223,14 +223,14 @@ public class IpPrefixTest {
    }

    @Test
    public void testContains() {
    public void testContainsInetAddress() {
        IpPrefix p = new IpPrefix("2001:db8:f00::ace:d00d/127");
        assertTrue(p.contains(Address("2001:db8:f00::ace:d00c")));
        assertTrue(p.contains(Address("2001:db8:f00::ace:d00d")));
        assertFalse(p.contains(Address("2001:db8:f00::ace:d00e")));
        assertFalse(p.contains(Address("2001:db8:f00::bad:d00d")));
        assertFalse(p.contains(Address("2001:4868:4860::8888")));
        assertFalse(p.contains(null));
        assertFalse(p.contains((InetAddress)null));
        assertFalse(p.contains(Address("8.8.8.8")));

        p = new IpPrefix("192.0.2.0/23");
@@ -250,6 +250,53 @@ public class IpPrefixTest {
        assertFalse(ipv4Default.contains(Address("2001:db8::f00")));
    }

    @Test
    public void testContainsIpPrefix() {
        assertTrue(new IpPrefix("0.0.0.0/0").containsPrefix(new IpPrefix("0.0.0.0/0")));
        assertTrue(new IpPrefix("0.0.0.0/0").containsPrefix(new IpPrefix("1.2.3.4/0")));
        assertTrue(new IpPrefix("0.0.0.0/0").containsPrefix(new IpPrefix("1.2.3.4/8")));
        assertTrue(new IpPrefix("0.0.0.0/0").containsPrefix(new IpPrefix("1.2.3.4/24")));
        assertTrue(new IpPrefix("0.0.0.0/0").containsPrefix(new IpPrefix("1.2.3.4/23")));

        assertTrue(new IpPrefix("1.2.3.4/8").containsPrefix(new IpPrefix("1.2.3.4/8")));
        assertTrue(new IpPrefix("1.2.3.4/8").containsPrefix(new IpPrefix("1.254.12.9/8")));
        assertTrue(new IpPrefix("1.2.3.4/21").containsPrefix(new IpPrefix("1.2.3.4/21")));
        assertTrue(new IpPrefix("1.2.3.4/32").containsPrefix(new IpPrefix("1.2.3.4/32")));

        assertTrue(new IpPrefix("1.2.3.4/20").containsPrefix(new IpPrefix("1.2.3.0/24")));

        assertFalse(new IpPrefix("1.2.3.4/32").containsPrefix(new IpPrefix("1.2.3.5/32")));
        assertFalse(new IpPrefix("1.2.3.4/8").containsPrefix(new IpPrefix("2.2.3.4/8")));
        assertFalse(new IpPrefix("0.0.0.0/16").containsPrefix(new IpPrefix("0.0.0.0/15")));
        assertFalse(new IpPrefix("100.0.0.0/8").containsPrefix(new IpPrefix("99.0.0.0/8")));

        assertTrue(new IpPrefix("::/0").containsPrefix(new IpPrefix("::/0")));
        assertTrue(new IpPrefix("::/0").containsPrefix(new IpPrefix("2001:db8::f00/1")));
        assertTrue(new IpPrefix("::/0").containsPrefix(new IpPrefix("3d8a:661:a0::770/8")));
        assertTrue(new IpPrefix("::/0").containsPrefix(new IpPrefix("2001:db8::f00/8")));
        assertTrue(new IpPrefix("::/0").containsPrefix(new IpPrefix("2001:db8::f00/64")));
        assertTrue(new IpPrefix("::/0").containsPrefix(new IpPrefix("2001:db8::f00/113")));
        assertTrue(new IpPrefix("::/0").containsPrefix(new IpPrefix("2001:db8::f00/128")));

        assertTrue(new IpPrefix("2001:db8:f00::ace:d00d/64").containsPrefix(
                new IpPrefix("2001:db8:f00::ace:d00d/64")));
        assertTrue(new IpPrefix("2001:db8:f00::ace:d00d/64").containsPrefix(
                new IpPrefix("2001:db8:f00::ace:d00d/120")));
        assertFalse(new IpPrefix("2001:db8:f00::ace:d00d/64").containsPrefix(
                new IpPrefix("2001:db8:f00::ace:d00d/32")));
        assertFalse(new IpPrefix("2001:db8:f00::ace:d00d/64").containsPrefix(
                new IpPrefix("2006:db8:f00::ace:d00d/96")));

        assertTrue(new IpPrefix("2001:db8:f00::ace:d00d/128").containsPrefix(
                new IpPrefix("2001:db8:f00::ace:d00d/128")));
        assertTrue(new IpPrefix("2001:db8:f00::ace:d00d/100").containsPrefix(
                new IpPrefix("2001:db8:f00::ace:ccaf/110")));

        assertFalse(new IpPrefix("2001:db8:f00::ace:d00d/128").containsPrefix(
                new IpPrefix("2001:db8:f00::ace:d00e/128")));
        assertFalse(new IpPrefix("::/30").containsPrefix(new IpPrefix("::/29")));
    }

    @Test
    public void testHashCode() {
        IpPrefix p = new IpPrefix(new byte[4], 0);
+99 −0
Original line number Diff line number Diff line
@@ -19,8 +19,10 @@ package android.net;
import android.net.NetworkUtils;
import android.test.suitebuilder.annotation.SmallTest;

import java.math.BigInteger;
import java.net.Inet4Address;
import java.net.InetAddress;
import java.util.TreeSet;

import junit.framework.TestCase;

@@ -67,4 +69,101 @@ public class NetworkUtilsTest extends TestCase {
        assertInvalidNetworkMask(IPv4Address("255.255.255.253"));
        assertInvalidNetworkMask(IPv4Address("255.255.0.255"));
    }

    @SmallTest
    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));
    }

    @SmallTest
    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