Home | History | Annotate | Download | only in net
      1 /*
      2  * Copyright (C) 2011 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package android.net;
     18 
     19 import android.os.Parcel;
     20 import android.os.Parcelable;
     21 
     22 import java.net.UnknownHostException;
     23 import java.net.InetAddress;
     24 import java.net.Inet4Address;
     25 import java.net.Inet6Address;
     26 
     27 import java.util.Collection;
     28 import java.util.Objects;
     29 
     30 /**
     31  * Represents a network route.
     32  * <p>
     33  * This is used both to describe static network configuration and live network
     34  * configuration information.
     35  *
     36  * A route contains three pieces of information:
     37  * <ul>
     38  * <li>a destination {@link IpPrefix} specifying the network destinations covered by this route.
     39  *     If this is {@code null} it indicates a default route of the address family (IPv4 or IPv6)
     40  *     implied by the gateway IP address.
     41  * <li>a gateway {@link InetAddress} indicating the next hop to use.  If this is {@code null} it
     42  *     indicates a directly-connected route.
     43  * <li>an interface (which may be unspecified).
     44  * </ul>
     45  * Either the destination or the gateway may be {@code null}, but not both.  If the
     46  * destination and gateway are both specified, they must be of the same address family
     47  * (IPv4 or IPv6).
     48  */
     49 public final class RouteInfo implements Parcelable {
     50     /**
     51      * The IP destination address for this route.
     52      */
     53     private final IpPrefix mDestination;
     54 
     55     /**
     56      * The gateway address for this route.
     57      */
     58     private final InetAddress mGateway;
     59 
     60     /**
     61      * The interface for this route.
     62      */
     63     private final String mInterface;
     64 
     65 
     66     /** Unicast route. @hide */
     67     public static final int RTN_UNICAST = 1;
     68 
     69     /** Unreachable route. @hide */
     70     public static final int RTN_UNREACHABLE = 7;
     71 
     72     /** Throw route. @hide */
     73     public static final int RTN_THROW = 9;
     74 
     75     /**
     76      * The type of this route; one of the RTN_xxx constants above.
     77      */
     78     private final int mType;
     79 
     80     // Derived data members.
     81     // TODO: remove these.
     82     private final boolean mIsHost;
     83     private final boolean mHasGateway;
     84 
     85     /**
     86      * Constructs a RouteInfo object.
     87      *
     88      * If destination is null, then gateway must be specified and the
     89      * constructed route is either the IPv4 default route <code>0.0.0.0</code>
     90      * if the gateway is an instance of {@link Inet4Address}, or the IPv6 default
     91      * route <code>::/0</code> if gateway is an instance of
     92      * {@link Inet6Address}.
     93      * <p>
     94      * destination and gateway may not both be null.
     95      *
     96      * @param destination the destination prefix
     97      * @param gateway the IP address to route packets through
     98      * @param iface the interface name to send packets on
     99      *
    100      * @hide
    101      */
    102     public RouteInfo(IpPrefix destination, InetAddress gateway, String iface, int type) {
    103         switch (type) {
    104             case RTN_UNICAST:
    105             case RTN_UNREACHABLE:
    106             case RTN_THROW:
    107                 // TODO: It would be nice to ensure that route types that don't have nexthops or
    108                 // interfaces, such as unreachable or throw, can't be created if an interface or
    109                 // a gateway is specified. This is a bit too complicated to do at the moment
    110                 // because:
    111                 //
    112                 // - LinkProperties sets the interface on routes added to it, and modifies the
    113                 //   interfaces of all the routes when its interface name changes.
    114                 // - Even when the gateway is null, we store a non-null gateway here.
    115                 //
    116                 // For now, we just rely on the code that sets routes to do things properly.
    117                 break;
    118             default:
    119                 throw new IllegalArgumentException("Unknown route type " + type);
    120         }
    121 
    122         if (destination == null) {
    123             if (gateway != null) {
    124                 if (gateway instanceof Inet4Address) {
    125                     destination = new IpPrefix(Inet4Address.ANY, 0);
    126                 } else {
    127                     destination = new IpPrefix(Inet6Address.ANY, 0);
    128                 }
    129             } else {
    130                 // no destination, no gateway. invalid.
    131                 throw new IllegalArgumentException("Invalid arguments passed in: " + gateway + "," +
    132                                                    destination);
    133             }
    134         }
    135         // TODO: set mGateway to null if there is no gateway. This is more correct, saves space, and
    136         // matches the documented behaviour. Before we can do this we need to fix all callers (e.g.,
    137         // ConnectivityService) to stop doing things like r.getGateway().equals(), ... .
    138         if (gateway == null) {
    139             if (destination.getAddress() instanceof Inet4Address) {
    140                 gateway = Inet4Address.ANY;
    141             } else {
    142                 gateway = Inet6Address.ANY;
    143             }
    144         }
    145         mHasGateway = (!gateway.isAnyLocalAddress());
    146 
    147         if ((destination.getAddress() instanceof Inet4Address &&
    148                  (gateway instanceof Inet4Address == false)) ||
    149                 (destination.getAddress() instanceof Inet6Address &&
    150                  (gateway instanceof Inet6Address == false))) {
    151             throw new IllegalArgumentException("address family mismatch in RouteInfo constructor");
    152         }
    153         mDestination = destination;  // IpPrefix objects are immutable.
    154         mGateway = gateway;          // InetAddress objects are immutable.
    155         mInterface = iface;          // Strings are immutable.
    156         mType = type;
    157         mIsHost = isHost();
    158     }
    159 
    160     /**
    161      *  @hide
    162      */
    163     public RouteInfo(IpPrefix destination, InetAddress gateway, String iface) {
    164         this(destination, gateway, iface, RTN_UNICAST);
    165     }
    166 
    167     /**
    168      * @hide
    169      */
    170     public RouteInfo(LinkAddress destination, InetAddress gateway, String iface) {
    171         this(destination == null ? null :
    172                 new IpPrefix(destination.getAddress(), destination.getPrefixLength()),
    173                 gateway, iface);
    174     }
    175 
    176     /**
    177      * Constructs a {@code RouteInfo} object.
    178      *
    179      * If destination is null, then gateway must be specified and the
    180      * constructed route is either the IPv4 default route <code>0.0.0.0</code>
    181      * if the gateway is an instance of {@link Inet4Address}, or the IPv6 default
    182      * route <code>::/0</code> if gateway is an instance of {@link Inet6Address}.
    183      * <p>
    184      * Destination and gateway may not both be null.
    185      *
    186      * @param destination the destination address and prefix in an {@link IpPrefix}
    187      * @param gateway the {@link InetAddress} to route packets through
    188      *
    189      * @hide
    190      */
    191     public RouteInfo(IpPrefix destination, InetAddress gateway) {
    192         this(destination, gateway, null);
    193     }
    194 
    195     /**
    196      * @hide
    197      *
    198      * TODO: Remove this.
    199      */
    200     public RouteInfo(LinkAddress destination, InetAddress gateway) {
    201         this(destination, gateway, null);
    202     }
    203 
    204     /**
    205      * Constructs a default {@code RouteInfo} object.
    206      *
    207      * @param gateway the {@link InetAddress} to route packets through
    208      *
    209      * @hide
    210      */
    211     public RouteInfo(InetAddress gateway) {
    212         this((IpPrefix) null, gateway, null);
    213     }
    214 
    215     /**
    216      * Constructs a {@code RouteInfo} object representing a direct connected subnet.
    217      *
    218      * @param destination the {@link IpPrefix} describing the address and prefix
    219      *                    length of the subnet.
    220      *
    221      * @hide
    222      */
    223     public RouteInfo(IpPrefix destination) {
    224         this(destination, null, null);
    225     }
    226 
    227     /**
    228      * @hide
    229      */
    230     public RouteInfo(LinkAddress destination) {
    231         this(destination, null, null);
    232     }
    233 
    234     /**
    235      * @hide
    236      */
    237     public RouteInfo(IpPrefix destination, int type) {
    238         this(destination, null, null, type);
    239     }
    240 
    241     /**
    242      * @hide
    243      */
    244     public static RouteInfo makeHostRoute(InetAddress host, String iface) {
    245         return makeHostRoute(host, null, iface);
    246     }
    247 
    248     /**
    249      * @hide
    250      */
    251     public static RouteInfo makeHostRoute(InetAddress host, InetAddress gateway, String iface) {
    252         if (host == null) return null;
    253 
    254         if (host instanceof Inet4Address) {
    255             return new RouteInfo(new IpPrefix(host, 32), gateway, iface);
    256         } else {
    257             return new RouteInfo(new IpPrefix(host, 128), gateway, iface);
    258         }
    259     }
    260 
    261     private boolean isHost() {
    262         return (mDestination.getAddress() instanceof Inet4Address &&
    263                 mDestination.getPrefixLength() == 32) ||
    264                (mDestination.getAddress() instanceof Inet6Address &&
    265                 mDestination.getPrefixLength() == 128);
    266     }
    267 
    268     /**
    269      * Retrieves the destination address and prefix length in the form of an {@link IpPrefix}.
    270      *
    271      * @return {@link IpPrefix} specifying the destination.  This is never {@code null}.
    272      */
    273     public IpPrefix getDestination() {
    274         return mDestination;
    275     }
    276 
    277     /**
    278      * TODO: Convert callers to use IpPrefix and then remove.
    279      * @hide
    280      */
    281     public LinkAddress getDestinationLinkAddress() {
    282         return new LinkAddress(mDestination.getAddress(), mDestination.getPrefixLength());
    283     }
    284 
    285     /**
    286      * Retrieves the gateway or next hop {@link InetAddress} for this route.
    287      *
    288      * @return {@link InetAddress} specifying the gateway or next hop.  This may be
    289      *                             {@code null} for a directly-connected route."
    290      */
    291     public InetAddress getGateway() {
    292         return mGateway;
    293     }
    294 
    295     /**
    296      * Retrieves the interface used for this route if specified, else {@code null}.
    297      *
    298      * @return The name of the interface used for this route.
    299      */
    300     public String getInterface() {
    301         return mInterface;
    302     }
    303 
    304     /**
    305      * Retrieves the type of this route.
    306      *
    307      * @return The type of this route; one of the {@code RTN_xxx} constants defined in this class.
    308      *
    309      * @hide
    310      */
    311     public int getType() {
    312         return mType;
    313     }
    314 
    315     /**
    316      * Indicates if this route is a default route (ie, has no destination specified).
    317      *
    318      * @return {@code true} if the destination has a prefix length of 0.
    319      */
    320     public boolean isDefaultRoute() {
    321         return mType == RTN_UNICAST && mDestination.getPrefixLength() == 0;
    322     }
    323 
    324     /**
    325      * Indicates if this route is an IPv4 default route.
    326      * @hide
    327      */
    328     public boolean isIPv4Default() {
    329         return isDefaultRoute() && mDestination.getAddress() instanceof Inet4Address;
    330     }
    331 
    332     /**
    333      * Indicates if this route is an IPv6 default route.
    334      * @hide
    335      */
    336     public boolean isIPv6Default() {
    337         return isDefaultRoute() && mDestination.getAddress() instanceof Inet6Address;
    338     }
    339 
    340     /**
    341      * Indicates if this route is a host route (ie, matches only a single host address).
    342      *
    343      * @return {@code true} if the destination has a prefix length of 32 or 128 for IPv4 or IPv6,
    344      * respectively.
    345      * @hide
    346      */
    347     public boolean isHostRoute() {
    348         return mIsHost;
    349     }
    350 
    351     /**
    352      * Indicates if this route has a next hop ({@code true}) or is directly-connected
    353      * ({@code false}).
    354      *
    355      * @return {@code true} if a gateway is specified
    356      * @hide
    357      */
    358     public boolean hasGateway() {
    359         return mHasGateway;
    360     }
    361 
    362     /**
    363      * Determines whether the destination and prefix of this route includes the specified
    364      * address.
    365      *
    366      * @param destination A {@link InetAddress} to test to see if it would match this route.
    367      * @return {@code true} if the destination and prefix length cover the given address.
    368      */
    369     public boolean matches(InetAddress destination) {
    370         return mDestination.contains(destination);
    371     }
    372 
    373     /**
    374      * Find the route from a Collection of routes that best matches a given address.
    375      * May return null if no routes are applicable.
    376      * @param routes a Collection of RouteInfos to chose from
    377      * @param dest the InetAddress your trying to get to
    378      * @return the RouteInfo from the Collection that best fits the given address
    379      *
    380      * @hide
    381      */
    382     public static RouteInfo selectBestRoute(Collection<RouteInfo> routes, InetAddress dest) {
    383         if ((routes == null) || (dest == null)) return null;
    384 
    385         RouteInfo bestRoute = null;
    386         // pick a longest prefix match under same address type
    387         for (RouteInfo route : routes) {
    388             if (NetworkUtils.addressTypeMatches(route.mDestination.getAddress(), dest)) {
    389                 if ((bestRoute != null) &&
    390                         (bestRoute.mDestination.getPrefixLength() >=
    391                         route.mDestination.getPrefixLength())) {
    392                     continue;
    393                 }
    394                 if (route.matches(dest)) bestRoute = route;
    395             }
    396         }
    397         return bestRoute;
    398     }
    399 
    400     /**
    401      * Returns a human-readable description of this object.
    402      */
    403     public String toString() {
    404         String val = "";
    405         if (mDestination != null) val = mDestination.toString();
    406         if (mType == RTN_UNREACHABLE) {
    407             val += " unreachable";
    408         } else if (mType == RTN_THROW) {
    409             val += " throw";
    410         } else {
    411             val += " ->";
    412             if (mGateway != null) val += " " + mGateway.getHostAddress();
    413             if (mInterface != null) val += " " + mInterface;
    414             if (mType != RTN_UNICAST) {
    415                 val += " unknown type " + mType;
    416             }
    417         }
    418         return val;
    419     }
    420 
    421     /**
    422      * Compares this RouteInfo object against the specified object and indicates if they are equal.
    423      * @return {@code true} if the objects are equal, {@code false} otherwise.
    424      */
    425     public boolean equals(Object obj) {
    426         if (this == obj) return true;
    427 
    428         if (!(obj instanceof RouteInfo)) return false;
    429 
    430         RouteInfo target = (RouteInfo) obj;
    431 
    432         return Objects.equals(mDestination, target.getDestination()) &&
    433                 Objects.equals(mGateway, target.getGateway()) &&
    434                 Objects.equals(mInterface, target.getInterface()) &&
    435                 mType == target.getType();
    436     }
    437 
    438     /**
    439      *  Returns a hashcode for this <code>RouteInfo</code> object.
    440      */
    441     public int hashCode() {
    442         return (mDestination.hashCode() * 41)
    443                 + (mGateway == null ? 0 :mGateway.hashCode() * 47)
    444                 + (mInterface == null ? 0 :mInterface.hashCode() * 67)
    445                 + (mType * 71);
    446     }
    447 
    448     /**
    449      * Implement the Parcelable interface
    450      */
    451     public int describeContents() {
    452         return 0;
    453     }
    454 
    455     /**
    456      * Implement the Parcelable interface
    457      */
    458     public void writeToParcel(Parcel dest, int flags) {
    459         dest.writeParcelable(mDestination, flags);
    460         byte[] gatewayBytes = (mGateway == null) ? null : mGateway.getAddress();
    461         dest.writeByteArray(gatewayBytes);
    462         dest.writeString(mInterface);
    463         dest.writeInt(mType);
    464     }
    465 
    466     /**
    467      * Implement the Parcelable interface.
    468      */
    469     public static final Creator<RouteInfo> CREATOR =
    470         new Creator<RouteInfo>() {
    471         public RouteInfo createFromParcel(Parcel in) {
    472             IpPrefix dest = in.readParcelable(null);
    473 
    474             InetAddress gateway = null;
    475             byte[] addr = in.createByteArray();
    476             try {
    477                 gateway = InetAddress.getByAddress(addr);
    478             } catch (UnknownHostException e) {}
    479 
    480             String iface = in.readString();
    481             int type = in.readInt();
    482 
    483             return new RouteInfo(dest, gateway, iface, type);
    484         }
    485 
    486         public RouteInfo[] newArray(int size) {
    487             return new RouteInfo[size];
    488         }
    489     };
    490 }
    491