| /* |
| * Copyright (C) 2011 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 android.annotation.IntDef; |
| import android.annotation.NonNull; |
| import android.annotation.Nullable; |
| import android.annotation.SystemApi; |
| import android.compat.annotation.UnsupportedAppUsage; |
| import android.os.Build; |
| import android.os.Parcel; |
| import android.os.Parcelable; |
| |
| import com.android.net.module.util.NetUtils; |
| import com.android.net.module.util.NetworkStackConstants; |
| |
| import java.lang.annotation.Retention; |
| import java.lang.annotation.RetentionPolicy; |
| import java.net.Inet4Address; |
| import java.net.Inet6Address; |
| import java.net.InetAddress; |
| import java.net.UnknownHostException; |
| import java.util.Collection; |
| import java.util.Objects; |
| |
| /** |
| * Represents a network route. |
| * <p> |
| * This is used both to describe static network configuration and live network |
| * configuration information. |
| * |
| * A route contains three pieces of information: |
| * <ul> |
| * <li>a destination {@link IpPrefix} specifying the network destinations covered by this route. |
| * If this is {@code null} it indicates a default route of the address family (IPv4 or IPv6) |
| * implied by the gateway IP address. |
| * <li>a gateway {@link InetAddress} indicating the next hop to use. If this is {@code null} it |
| * indicates a directly-connected route. |
| * <li>an interface (which may be unspecified). |
| * </ul> |
| * Either the destination or the gateway may be {@code null}, but not both. If the |
| * destination and gateway are both specified, they must be of the same address family |
| * (IPv4 or IPv6). |
| */ |
| public final class RouteInfo implements Parcelable { |
| /** @hide */ |
| @IntDef(value = { |
| RTN_UNICAST, |
| RTN_UNREACHABLE, |
| RTN_THROW, |
| }) |
| @Retention(RetentionPolicy.SOURCE) |
| public @interface RouteType {} |
| |
| /** |
| * The IP destination address for this route. |
| */ |
| @NonNull |
| private final IpPrefix mDestination; |
| |
| /** |
| * The gateway address for this route. |
| */ |
| @UnsupportedAppUsage |
| @Nullable |
| private final InetAddress mGateway; |
| |
| /** |
| * The interface for this route. |
| */ |
| @Nullable |
| private final String mInterface; |
| |
| |
| /** |
| * Unicast route. |
| * |
| * Indicates that destination is reachable directly or via gateway. |
| **/ |
| public static final int RTN_UNICAST = 1; |
| |
| /** |
| * Unreachable route. |
| * |
| * Indicates that destination is unreachable. |
| **/ |
| public static final int RTN_UNREACHABLE = 7; |
| |
| /** |
| * Throw route. |
| * |
| * Indicates that routing information about this destination is not in this table. |
| * Routing lookup should continue in another table. |
| **/ |
| public static final int RTN_THROW = 9; |
| |
| /** |
| * The type of this route; one of the RTN_xxx constants above. |
| */ |
| private final int mType; |
| |
| /** |
| * The maximum transmission unit size for this route. |
| */ |
| private final int mMtu; |
| |
| // Derived data members. |
| // TODO: remove these. |
| @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023) |
| private final boolean mIsHost; |
| private final boolean mHasGateway; |
| |
| /** |
| * Constructs a RouteInfo object. |
| * |
| * If destination is null, then gateway must be specified and the |
| * constructed route is either the IPv4 default route <code>0.0.0.0</code> |
| * if the gateway is an instance of {@link Inet4Address}, or the IPv6 default |
| * route <code>::/0</code> if gateway is an instance of |
| * {@link Inet6Address}. |
| * <p> |
| * destination and gateway may not both be null. |
| * |
| * @param destination the destination prefix |
| * @param gateway the IP address to route packets through |
| * @param iface the interface name to send packets on |
| * @param type the type of this route |
| * |
| * @hide |
| */ |
| @SystemApi |
| public RouteInfo(@Nullable IpPrefix destination, @Nullable InetAddress gateway, |
| @Nullable String iface, @RouteType int type) { |
| this(destination, gateway, iface, type, 0); |
| } |
| |
| /** |
| * Constructs a RouteInfo object. |
| * |
| * If destination is null, then gateway must be specified and the |
| * constructed route is either the IPv4 default route <code>0.0.0.0</code> |
| * if the gateway is an instance of {@link Inet4Address}, or the IPv6 default |
| * route <code>::/0</code> if gateway is an instance of |
| * {@link Inet6Address}. |
| * <p> |
| * destination and gateway may not both be null. |
| * |
| * @param destination the destination prefix |
| * @param gateway the IP address to route packets through |
| * @param iface the interface name to send packets on |
| * @param type the type of this route |
| * @param mtu the maximum transmission unit size for this route |
| * |
| * @hide |
| */ |
| @SystemApi |
| public RouteInfo(@Nullable IpPrefix destination, @Nullable InetAddress gateway, |
| @Nullable String iface, @RouteType int type, int mtu) { |
| switch (type) { |
| case RTN_UNICAST: |
| case RTN_UNREACHABLE: |
| case RTN_THROW: |
| // TODO: It would be nice to ensure that route types that don't have nexthops or |
| // interfaces, such as unreachable or throw, can't be created if an interface or |
| // a gateway is specified. This is a bit too complicated to do at the moment |
| // because: |
| // |
| // - LinkProperties sets the interface on routes added to it, and modifies the |
| // interfaces of all the routes when its interface name changes. |
| // - Even when the gateway is null, we store a non-null gateway here. |
| // |
| // For now, we just rely on the code that sets routes to do things properly. |
| break; |
| default: |
| throw new IllegalArgumentException("Unknown route type " + type); |
| } |
| |
| if (destination == null) { |
| if (gateway != null) { |
| if (gateway instanceof Inet4Address) { |
| destination = new IpPrefix(NetworkStackConstants.IPV4_ADDR_ANY, 0); |
| } else { |
| destination = new IpPrefix(NetworkStackConstants.IPV6_ADDR_ANY, 0); |
| } |
| } else { |
| // no destination, no gateway. invalid. |
| throw new IllegalArgumentException("Invalid arguments passed in: " + gateway + "," + |
| destination); |
| } |
| } |
| // TODO: set mGateway to null if there is no gateway. This is more correct, saves space, and |
| // matches the documented behaviour. Before we can do this we need to fix all callers (e.g., |
| // ConnectivityService) to stop doing things like r.getGateway().equals(), ... . |
| if (gateway == null) { |
| if (destination.getAddress() instanceof Inet4Address) { |
| gateway = NetworkStackConstants.IPV4_ADDR_ANY; |
| } else { |
| gateway = NetworkStackConstants.IPV6_ADDR_ANY; |
| } |
| } |
| mHasGateway = (!gateway.isAnyLocalAddress()); |
| |
| if ((destination.getAddress() instanceof Inet4Address |
| && !(gateway instanceof Inet4Address)) |
| || (destination.getAddress() instanceof Inet6Address |
| && !(gateway instanceof Inet6Address))) { |
| throw new IllegalArgumentException("address family mismatch in RouteInfo constructor"); |
| } |
| mDestination = destination; // IpPrefix objects are immutable. |
| mGateway = gateway; // InetAddress objects are immutable. |
| mInterface = iface; // Strings are immutable. |
| mType = type; |
| mIsHost = isHost(); |
| mMtu = mtu; |
| } |
| |
| /** |
| * Constructs a {@code RouteInfo} object. |
| * |
| * If destination is null, then gateway must be specified and the |
| * constructed route is either the IPv4 default route <code>0.0.0.0</code> |
| * if the gateway is an instance of {@link Inet4Address}, or the IPv6 default |
| * route <code>::/0</code> if gateway is an instance of {@link Inet6Address}. |
| * <p> |
| * Destination and gateway may not both be null. |
| * |
| * @param destination the destination address and prefix in an {@link IpPrefix} |
| * @param gateway the {@link InetAddress} to route packets through |
| * @param iface the interface name to send packets on |
| * |
| * @hide |
| */ |
| @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) |
| public RouteInfo(@Nullable IpPrefix destination, @Nullable InetAddress gateway, |
| @Nullable String iface) { |
| this(destination, gateway, iface, RTN_UNICAST); |
| } |
| |
| /** |
| * @hide |
| */ |
| @UnsupportedAppUsage |
| public RouteInfo(@Nullable LinkAddress destination, @Nullable InetAddress gateway, |
| @Nullable String iface) { |
| this(destination == null ? null : |
| new IpPrefix(destination.getAddress(), destination.getPrefixLength()), |
| gateway, iface); |
| } |
| |
| /** |
| * Constructs a {@code RouteInfo} object. |
| * |
| * If destination is null, then gateway must be specified and the |
| * constructed route is either the IPv4 default route <code>0.0.0.0</code> |
| * if the gateway is an instance of {@link Inet4Address}, or the IPv6 default |
| * route <code>::/0</code> if gateway is an instance of {@link Inet6Address}. |
| * <p> |
| * Destination and gateway may not both be null. |
| * |
| * @param destination the destination address and prefix in an {@link IpPrefix} |
| * @param gateway the {@link InetAddress} to route packets through |
| * |
| * @hide |
| */ |
| public RouteInfo(@Nullable IpPrefix destination, @Nullable InetAddress gateway) { |
| this(destination, gateway, null); |
| } |
| |
| /** |
| * @hide |
| * |
| * TODO: Remove this. |
| */ |
| @UnsupportedAppUsage |
| public RouteInfo(@Nullable LinkAddress destination, @Nullable InetAddress gateway) { |
| this(destination, gateway, null); |
| } |
| |
| /** |
| * Constructs a default {@code RouteInfo} object. |
| * |
| * @param gateway the {@link InetAddress} to route packets through |
| * |
| * @hide |
| */ |
| @UnsupportedAppUsage |
| public RouteInfo(@NonNull InetAddress gateway) { |
| this((IpPrefix) null, gateway, null); |
| } |
| |
| /** |
| * Constructs a {@code RouteInfo} object representing a direct connected subnet. |
| * |
| * @param destination the {@link IpPrefix} describing the address and prefix |
| * length of the subnet. |
| * |
| * @hide |
| */ |
| public RouteInfo(@NonNull IpPrefix destination) { |
| this(destination, null, null); |
| } |
| |
| /** |
| * @hide |
| */ |
| public RouteInfo(@NonNull LinkAddress destination) { |
| this(destination, null, null); |
| } |
| |
| /** |
| * @hide |
| */ |
| public RouteInfo(@NonNull IpPrefix destination, @RouteType int type) { |
| this(destination, null, null, type); |
| } |
| |
| /** |
| * @hide |
| */ |
| public static RouteInfo makeHostRoute(@NonNull InetAddress host, @Nullable String iface) { |
| return makeHostRoute(host, null, iface); |
| } |
| |
| /** |
| * @hide |
| */ |
| public static RouteInfo makeHostRoute(@Nullable InetAddress host, @Nullable InetAddress gateway, |
| @Nullable String iface) { |
| if (host == null) return null; |
| |
| if (host instanceof Inet4Address) { |
| return new RouteInfo(new IpPrefix(host, 32), gateway, iface); |
| } else { |
| return new RouteInfo(new IpPrefix(host, 128), gateway, iface); |
| } |
| } |
| |
| @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023) |
| private boolean isHost() { |
| return (mDestination.getAddress() instanceof Inet4Address && |
| mDestination.getPrefixLength() == 32) || |
| (mDestination.getAddress() instanceof Inet6Address && |
| mDestination.getPrefixLength() == 128); |
| } |
| |
| /** |
| * Retrieves the destination address and prefix length in the form of an {@link IpPrefix}. |
| * |
| * @return {@link IpPrefix} specifying the destination. This is never {@code null}. |
| */ |
| @NonNull |
| public IpPrefix getDestination() { |
| return mDestination; |
| } |
| |
| /** |
| * TODO: Convert callers to use IpPrefix and then remove. |
| * @hide |
| */ |
| @NonNull |
| public LinkAddress getDestinationLinkAddress() { |
| return new LinkAddress(mDestination.getAddress(), mDestination.getPrefixLength()); |
| } |
| |
| /** |
| * Retrieves the gateway or next hop {@link InetAddress} for this route. |
| * |
| * @return {@link InetAddress} specifying the gateway or next hop. This may be |
| * {@code null} for a directly-connected route." |
| */ |
| @Nullable |
| public InetAddress getGateway() { |
| return mGateway; |
| } |
| |
| /** |
| * Retrieves the interface used for this route if specified, else {@code null}. |
| * |
| * @return The name of the interface used for this route. |
| */ |
| @Nullable |
| public String getInterface() { |
| return mInterface; |
| } |
| |
| /** |
| * Retrieves the type of this route. |
| * |
| * @return The type of this route; one of the {@code RTN_xxx} constants defined in this class. |
| */ |
| @RouteType |
| public int getType() { |
| return mType; |
| } |
| |
| /** |
| * Retrieves the MTU size for this route. |
| * |
| * @return The MTU size, or 0 if it has not been set. |
| * @hide |
| */ |
| @SystemApi |
| public int getMtu() { |
| return mMtu; |
| } |
| |
| /** |
| * Indicates if this route is a default route (ie, has no destination specified). |
| * |
| * @return {@code true} if the destination has a prefix length of 0. |
| */ |
| public boolean isDefaultRoute() { |
| return mType == RTN_UNICAST && mDestination.getPrefixLength() == 0; |
| } |
| |
| /** |
| * Indicates if this route is an unreachable default route. |
| * |
| * @return {@code true} if it's an unreachable route with prefix length of 0. |
| * @hide |
| */ |
| private boolean isUnreachableDefaultRoute() { |
| return mType == RTN_UNREACHABLE && mDestination.getPrefixLength() == 0; |
| } |
| |
| /** |
| * Indicates if this route is an IPv4 default route. |
| * @hide |
| */ |
| public boolean isIPv4Default() { |
| return isDefaultRoute() && mDestination.getAddress() instanceof Inet4Address; |
| } |
| |
| /** |
| * Indicates if this route is an IPv4 unreachable default route. |
| * @hide |
| */ |
| public boolean isIPv4UnreachableDefault() { |
| return isUnreachableDefaultRoute() && mDestination.getAddress() instanceof Inet4Address; |
| } |
| |
| /** |
| * Indicates if this route is an IPv6 default route. |
| * @hide |
| */ |
| public boolean isIPv6Default() { |
| return isDefaultRoute() && mDestination.getAddress() instanceof Inet6Address; |
| } |
| |
| /** |
| * Indicates if this route is an IPv6 unreachable default route. |
| * @hide |
| */ |
| public boolean isIPv6UnreachableDefault() { |
| return isUnreachableDefaultRoute() && mDestination.getAddress() instanceof Inet6Address; |
| } |
| |
| /** |
| * Indicates if this route is a host route (ie, matches only a single host address). |
| * |
| * @return {@code true} if the destination has a prefix length of 32 or 128 for IPv4 or IPv6, |
| * respectively. |
| * @hide |
| */ |
| public boolean isHostRoute() { |
| return mIsHost; |
| } |
| |
| /** |
| * Indicates if this route has a next hop ({@code true}) or is directly-connected |
| * ({@code false}). |
| * |
| * @return {@code true} if a gateway is specified |
| */ |
| public boolean hasGateway() { |
| return mHasGateway; |
| } |
| |
| /** |
| * Determines whether the destination and prefix of this route includes the specified |
| * address. |
| * |
| * @param destination A {@link InetAddress} to test to see if it would match this route. |
| * @return {@code true} if the destination and prefix length cover the given address. |
| */ |
| public boolean matches(InetAddress destination) { |
| return mDestination.contains(destination); |
| } |
| |
| /** |
| * Find the route from a Collection of routes that best matches a given address. |
| * May return null if no routes are applicable. |
| * @param routes a Collection of RouteInfos to chose from |
| * @param dest the InetAddress your trying to get to |
| * @return the RouteInfo from the Collection that best fits the given address |
| * |
| * @hide |
| */ |
| @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) |
| @Nullable |
| public static RouteInfo selectBestRoute(Collection<RouteInfo> routes, InetAddress dest) { |
| return NetUtils.selectBestRoute(routes, dest); |
| } |
| |
| /** |
| * Returns a human-readable description of this object. |
| */ |
| public String toString() { |
| String val = ""; |
| if (mDestination != null) val = mDestination.toString(); |
| if (mType == RTN_UNREACHABLE) { |
| val += " unreachable"; |
| } else if (mType == RTN_THROW) { |
| val += " throw"; |
| } else { |
| val += " ->"; |
| if (mGateway != null) val += " " + mGateway.getHostAddress(); |
| if (mInterface != null) val += " " + mInterface; |
| if (mType != RTN_UNICAST) { |
| val += " unknown type " + mType; |
| } |
| } |
| val += " mtu " + mMtu; |
| return val; |
| } |
| |
| /** |
| * Compares this RouteInfo object against the specified object and indicates if they are equal. |
| * @return {@code true} if the objects are equal, {@code false} otherwise. |
| */ |
| public boolean equals(@Nullable Object obj) { |
| if (this == obj) return true; |
| |
| if (!(obj instanceof RouteInfo)) return false; |
| |
| RouteInfo target = (RouteInfo) obj; |
| |
| return Objects.equals(mDestination, target.getDestination()) && |
| Objects.equals(mGateway, target.getGateway()) && |
| Objects.equals(mInterface, target.getInterface()) && |
| mType == target.getType() && mMtu == target.getMtu(); |
| } |
| |
| /** |
| * A helper class that contains the destination, the gateway and the interface in a |
| * {@code RouteInfo}, used by {@link ConnectivityService#updateRoutes} or |
| * {@link LinkProperties#addRoute} to calculate the list to be updated. |
| * {@code RouteInfo} objects with different interfaces are treated as different routes because |
| * *usually* on Android different interfaces use different routing tables, and moving a route |
| * to a new routing table never constitutes an update, but is always a remove and an add. |
| * |
| * @hide |
| */ |
| public static class RouteKey { |
| @NonNull private final IpPrefix mDestination; |
| @Nullable private final InetAddress mGateway; |
| @Nullable private final String mInterface; |
| |
| RouteKey(@NonNull IpPrefix destination, @Nullable InetAddress gateway, |
| @Nullable String iface) { |
| mDestination = destination; |
| mGateway = gateway; |
| mInterface = iface; |
| } |
| |
| @Override |
| public boolean equals(@Nullable Object o) { |
| if (!(o instanceof RouteKey)) { |
| return false; |
| } |
| RouteKey p = (RouteKey) o; |
| // No need to do anything special for scoped addresses. Inet6Address#equals does not |
| // consider the scope ID, but the route IPCs (e.g., RoutingCoordinatorManager#addRoute) |
| // and the kernel ignore scoped addresses both in the prefix and in the nexthop and only |
| // look at RTA_OIF. |
| return Objects.equals(p.mDestination, mDestination) |
| && Objects.equals(p.mGateway, mGateway) |
| && Objects.equals(p.mInterface, mInterface); |
| } |
| |
| @Override |
| public int hashCode() { |
| return Objects.hash(mDestination, mGateway, mInterface); |
| } |
| } |
| |
| /** |
| * Get {@code RouteKey} of this {@code RouteInfo}. |
| * @return a {@code RouteKey} object. |
| * |
| * @hide |
| */ |
| @NonNull |
| public RouteKey getRouteKey() { |
| return new RouteKey(mDestination, mGateway, mInterface); |
| } |
| |
| /** |
| * Returns a hashcode for this <code>RouteInfo</code> object. |
| */ |
| public int hashCode() { |
| return (mDestination.hashCode() * 41) |
| + (mGateway == null ? 0 :mGateway.hashCode() * 47) |
| + (mInterface == null ? 0 :mInterface.hashCode() * 67) |
| + (mType * 71) + (mMtu * 89); |
| } |
| |
| /** |
| * Implement the Parcelable interface |
| */ |
| public int describeContents() { |
| return 0; |
| } |
| |
| /** |
| * Implement the Parcelable interface |
| */ |
| public void writeToParcel(Parcel dest, int flags) { |
| dest.writeParcelable(mDestination, flags); |
| byte[] gatewayBytes = (mGateway == null) ? null : mGateway.getAddress(); |
| dest.writeByteArray(gatewayBytes); |
| dest.writeString(mInterface); |
| dest.writeInt(mType); |
| dest.writeInt(mMtu); |
| } |
| |
| /** |
| * Implement the Parcelable interface. |
| */ |
| public static final @android.annotation.NonNull Creator<RouteInfo> CREATOR = |
| new Creator<RouteInfo>() { |
| public RouteInfo createFromParcel(Parcel in) { |
| IpPrefix dest = in.readParcelable(null); |
| |
| InetAddress gateway = null; |
| byte[] addr = in.createByteArray(); |
| try { |
| gateway = InetAddress.getByAddress(addr); |
| } catch (UnknownHostException e) {} |
| |
| String iface = in.readString(); |
| int type = in.readInt(); |
| int mtu = in.readInt(); |
| |
| return new RouteInfo(dest, gateway, iface, type, mtu); |
| } |
| |
| public RouteInfo[] newArray(int size) { |
| return new RouteInfo[size]; |
| } |
| }; |
| } |