diff options
| author | 2021-12-22 15:12:35 +0000 | |
|---|---|---|
| committer | 2022-01-06 14:49:02 +0000 | |
| commit | 5b1d7c2eadf44d47a41468ec7c78b37d464b373f (patch) | |
| tree | f1f47c1b163d2c0059d7df61700720b26d6c4d58 | |
| parent | 0a9079431b2c636e305d7397365c04fa15e10717 (diff) | |
Revert "Revert "Add APIs that allow to exclude routes from VPN""
This reverts commit 487b2e645a4d159c48033b335ec6f929214ad27e.
Reason for revert: failing build target configuration is fixed
Change-Id: I7c69b7244b4262336e427e4c671aaebb70ddc6ed
| -rw-r--r-- | core/api/current.txt | 2 | ||||
| -rw-r--r-- | core/java/android/net/VpnService.java | 123 | ||||
| -rw-r--r-- | core/java/com/android/internal/net/VpnConfig.java | 40 | ||||
| -rw-r--r-- | services/core/java/com/android/server/connectivity/Vpn.java | 7 |
4 files changed, 141 insertions, 31 deletions
diff --git a/core/api/current.txt b/core/api/current.txt index b42d6aaf9386..97328e846e33 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -26895,11 +26895,13 @@ package android.net { method @NonNull public android.net.VpnService.Builder addDnsServer(@NonNull java.net.InetAddress); method @NonNull public android.net.VpnService.Builder addDnsServer(@NonNull String); method @NonNull public android.net.VpnService.Builder addRoute(@NonNull java.net.InetAddress, int); + method @NonNull public android.net.VpnService.Builder addRoute(@NonNull android.net.IpPrefix); method @NonNull public android.net.VpnService.Builder addRoute(@NonNull String, int); method @NonNull public android.net.VpnService.Builder addSearchDomain(@NonNull String); method @NonNull public android.net.VpnService.Builder allowBypass(); method @NonNull public android.net.VpnService.Builder allowFamily(int); method @Nullable public android.os.ParcelFileDescriptor establish(); + method @NonNull public android.net.VpnService.Builder excludeRoute(@NonNull android.net.IpPrefix); method @NonNull public android.net.VpnService.Builder setBlocking(boolean); method @NonNull public android.net.VpnService.Builder setConfigureIntent(@NonNull android.app.PendingIntent); method @NonNull public android.net.VpnService.Builder setHttpProxy(@NonNull android.net.ProxyInfo); diff --git a/core/java/android/net/VpnService.java b/core/java/android/net/VpnService.java index 2ced05693755..1ae1b050d32f 100644 --- a/core/java/android/net/VpnService.java +++ b/core/java/android/net/VpnService.java @@ -41,6 +41,7 @@ import android.os.RemoteException; import android.os.ServiceManager; import android.os.UserHandle; +import com.android.internal.annotations.VisibleForTesting; import com.android.internal.net.NetworkUtilsInternal; import com.android.internal.net.VpnConfig; @@ -50,6 +51,7 @@ import java.net.Inet6Address; import java.net.InetAddress; import java.net.Socket; import java.util.ArrayList; +import java.util.Collections; import java.util.List; import java.util.Set; @@ -471,6 +473,13 @@ public class VpnService extends Service { } } + private static void checkNonPrefixBytes(@NonNull InetAddress address, int prefixLength) { + final IpPrefix prefix = new IpPrefix(address, prefixLength); + if (!prefix.getAddress().equals(address)) { + throw new IllegalArgumentException("Bad address"); + } + } + /** * Helper class to create a VPN interface. This class should be always * used within the scope of the outer {@link VpnService}. @@ -481,9 +490,9 @@ public class VpnService extends Service { private final VpnConfig mConfig = new VpnConfig(); @UnsupportedAppUsage - private final List<LinkAddress> mAddresses = new ArrayList<LinkAddress>(); + private final List<LinkAddress> mAddresses = new ArrayList<>(); @UnsupportedAppUsage - private final List<RouteInfo> mRoutes = new ArrayList<RouteInfo>(); + private final List<RouteInfo> mRoutes = new ArrayList<>(); public Builder() { mConfig.user = VpnService.this.getClass().getName(); @@ -555,7 +564,6 @@ public class VpnService extends Service { throw new IllegalArgumentException("Bad address"); } mAddresses.add(new LinkAddress(address, prefixLength)); - mConfig.updateAllowedFamilies(address); return this; } @@ -579,28 +587,68 @@ public class VpnService extends Service { * Add a network route to the VPN interface. Both IPv4 and IPv6 * routes are supported. * + * If a route with the same destination is already present, its type will be updated. + * + * @throws IllegalArgumentException if the route is invalid. + */ + @NonNull + private Builder addRoute(@NonNull IpPrefix prefix, int type) { + check(prefix.getAddress(), prefix.getPrefixLength()); + + final RouteInfo newRoute = new RouteInfo(prefix, /* gateway */ + null, /* interface */ null, type); + + final int index = findRouteIndexByDestination(newRoute); + + if (index == -1) { + mRoutes.add(newRoute); + } else { + mRoutes.set(index, newRoute); + } + + return this; + } + + /** + * Add a network route to the VPN interface. Both IPv4 and IPv6 + * routes are supported. + * * Adding a route implicitly allows traffic from that address family * (i.e., IPv4 or IPv6) to be routed over the VPN. @see #allowFamily * + * Calling this method overrides previous calls to {@link #excludeRoute} for the same + * destination. + * + * If multiple routes match the packet destination, route with the longest prefix takes + * precedence. + * * @throws IllegalArgumentException if the route is invalid. */ @NonNull public Builder addRoute(@NonNull InetAddress address, int prefixLength) { - check(address, prefixLength); + checkNonPrefixBytes(address, prefixLength); - int offset = prefixLength / 8; - byte[] bytes = address.getAddress(); - if (offset < bytes.length) { - for (bytes[offset] <<= prefixLength % 8; offset < bytes.length; ++offset) { - if (bytes[offset] != 0) { - throw new IllegalArgumentException("Bad address"); - } - } - } - mRoutes.add(new RouteInfo(new IpPrefix(address, prefixLength), null, null, - RouteInfo.RTN_UNICAST)); - mConfig.updateAllowedFamilies(address); - return this; + return addRoute(new IpPrefix(address, prefixLength), RouteInfo.RTN_UNICAST); + } + + /** + * Add a network route to the VPN interface. Both IPv4 and IPv6 + * routes are supported. + * + * Adding a route implicitly allows traffic from that address family + * (i.e., IPv4 or IPv6) to be routed over the VPN. @see #allowFamily + * + * Calling this method overrides previous calls to {@link #excludeRoute} for the same + * destination. + * + * If multiple routes match the packet destination, route with the longest prefix takes + * precedence. + * + * @throws IllegalArgumentException if the route is invalid. + */ + @NonNull + public Builder addRoute(@NonNull IpPrefix prefix) { + return addRoute(prefix, RouteInfo.RTN_UNICAST); } /** @@ -611,6 +659,12 @@ public class VpnService extends Service { * Adding a route implicitly allows traffic from that address family * (i.e., IPv4 or IPv6) to be routed over the VPN. @see #allowFamily * + * Calling this method overrides previous calls to {@link #excludeRoute} for the same + * destination. + * + * If multiple routes match the packet destination, route with the longest prefix takes + * precedence. + * * @throws IllegalArgumentException if the route is invalid. * @see #addRoute(InetAddress, int) */ @@ -620,6 +674,23 @@ public class VpnService extends Service { } /** + * Exclude a network route from the VPN interface. Both IPv4 and IPv6 + * routes are supported. + * + * Calling this method overrides previous calls to {@link #addRoute} for the same + * destination. + * + * If multiple routes match the packet destination, route with the longest prefix takes + * precedence. + * + * @throws IllegalArgumentException if the route is invalid. + */ + @NonNull + public Builder excludeRoute(@NonNull IpPrefix prefix) { + return addRoute(prefix, RouteInfo.RTN_THROW); + } + + /** * Add a DNS server to the VPN connection. Both IPv4 and IPv6 * addresses are supported. If none is set, the DNS servers of * the default network will be used. @@ -900,5 +971,23 @@ public class VpnService extends Service { throw new IllegalStateException(e); } } + + private int findRouteIndexByDestination(RouteInfo route) { + for (int i = 0; i < mRoutes.size(); i++) { + if (mRoutes.get(i).getDestination().equals(route.getDestination())) { + return i; + } + } + return -1; + } + + /** + * Method for testing, to observe mRoutes while builder is being used. + * @hide + */ + @VisibleForTesting + public List<RouteInfo> routes() { + return Collections.unmodifiableList(mRoutes); + } } } diff --git a/core/java/com/android/internal/net/VpnConfig.java b/core/java/com/android/internal/net/VpnConfig.java index 2e7629a76dee..2ae56f808972 100644 --- a/core/java/com/android/internal/net/VpnConfig.java +++ b/core/java/com/android/internal/net/VpnConfig.java @@ -34,8 +34,6 @@ import android.os.Parcel; import android.os.Parcelable; import android.os.UserHandle; -import java.net.Inet4Address; -import java.net.InetAddress; import java.util.ArrayList; import java.util.Arrays; import java.util.List; @@ -93,8 +91,8 @@ public class VpnConfig implements Parcelable { public String interfaze; public String session; public int mtu = -1; - public List<LinkAddress> addresses = new ArrayList<LinkAddress>(); - public List<RouteInfo> routes = new ArrayList<RouteInfo>(); + public List<LinkAddress> addresses = new ArrayList<>(); + public List<RouteInfo> routes = new ArrayList<>(); public List<String> dnsServers; public List<String> searchDomains; public List<String> allowedApplications; @@ -114,12 +112,32 @@ public class VpnConfig implements Parcelable { public VpnConfig() { } - public void updateAllowedFamilies(InetAddress address) { - if (address instanceof Inet4Address) { - allowIPv4 = true; - } else { - allowIPv6 = true; - } + public VpnConfig(VpnConfig other) { + user = other.user; + interfaze = other.interfaze; + session = other.session; + mtu = other.mtu; + addresses = copyOf(other.addresses); + routes = copyOf(other.routes); + dnsServers = copyOf(other.dnsServers); + searchDomains = copyOf(other.searchDomains); + allowedApplications = copyOf(other.allowedApplications); + disallowedApplications = copyOf(other.disallowedApplications); + configureIntent = other.configureIntent; + startTime = other.startTime; + legacy = other.legacy; + blocking = other.blocking; + allowBypass = other.allowBypass; + allowIPv4 = other.allowIPv4; + allowIPv6 = other.allowIPv6; + isMetered = other.isMetered; + underlyingNetworks = other.underlyingNetworks != null ? Arrays.copyOf( + other.underlyingNetworks, other.underlyingNetworks.length) : null; + proxyInfo = other.proxyInfo; + } + + private static <T> List<T> copyOf(List<T> list) { + return list != null ? new ArrayList<>(list) : null; } public void addLegacyRoutes(String routesStr) { @@ -131,7 +149,6 @@ public class VpnConfig implements Parcelable { //each route is ip/prefix RouteInfo info = new RouteInfo(new IpPrefix(route), null, null, RouteInfo.RTN_UNICAST); this.routes.add(info); - updateAllowedFamilies(info.getDestination().getAddress()); } } @@ -144,7 +161,6 @@ public class VpnConfig implements Parcelable { //each address is ip/prefix LinkAddress addr = new LinkAddress(address); this.addresses.add(addr); - updateAllowedFamilies(addr.getAddress()); } } diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java index 2c666b5bb7c7..9a9c3ea05d19 100644 --- a/services/core/java/com/android/server/connectivity/Vpn.java +++ b/services/core/java/com/android/server/connectivity/Vpn.java @@ -1208,8 +1208,11 @@ public class Vpn { for (RouteInfo route : mConfig.routes) { lp.addRoute(route); InetAddress address = route.getDestination().getAddress(); - allowIPv4 |= address instanceof Inet4Address; - allowIPv6 |= address instanceof Inet6Address; + + if (route.getType() == RouteInfo.RTN_UNICAST) { + allowIPv4 |= address instanceof Inet4Address; + allowIPv6 |= address instanceof Inet6Address; + } } } |