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 
     29 /**
     30  * A simple container for route information.
     31  *
     32  * In order to be used, a route must have a destination prefix and:
     33  *
     34  * - A gateway address (next-hop, for gatewayed routes), or
     35  * - An interface (for directly-connected routes), or
     36  * - Both a gateway and an interface.
     37  *
     38  * This class does not enforce these constraints because there is code that
     39  * uses RouteInfo objects to store directly-connected routes without interfaces.
     40  * Such objects cannot be used directly, but can be put into a LinkProperties
     41  * object which then specifies the interface.
     42  *
     43  * @hide
     44  */
     45 public class RouteInfo implements Parcelable {
     46     /**
     47      * The IP destination address for this route.
     48      */
     49     private final LinkAddress mDestination;
     50 
     51     /**
     52      * The gateway address for this route.
     53      */
     54     private final InetAddress mGateway;
     55 
     56     /**
     57      * The interface for this route.
     58      */
     59     private final String mInterface;
     60 
     61     private final boolean mIsDefault;
     62     private final boolean mIsHost;
     63     private final boolean mHasGateway;
     64 
     65     /**
     66      * Constructs a RouteInfo object.
     67      *
     68      * If destination is null, then gateway must be specified and the
     69      * constructed route is either the IPv4 default route <code>0.0.0.0</code>
     70      * if @gateway is an instance of {@link Inet4Address}, or the IPv6 default
     71      * route <code>::/0</code> if gateway is an instance of
     72      * {@link Inet6Address}.
     73      *
     74      * destination and gateway may not both be null.
     75      *
     76      * @param destination the destination prefix
     77      * @param gateway the IP address to route packets through
     78      * @param iface the interface name to send packets on
     79      */
     80     public RouteInfo(LinkAddress destination, InetAddress gateway, String iface) {
     81         if (destination == null) {
     82             if (gateway != null) {
     83                 if (gateway instanceof Inet4Address) {
     84                     destination = new LinkAddress(Inet4Address.ANY, 0);
     85                 } else {
     86                     destination = new LinkAddress(Inet6Address.ANY, 0);
     87                 }
     88             } else {
     89                 // no destination, no gateway. invalid.
     90                 throw new IllegalArgumentException("Invalid arguments passed in: " + gateway + "," +
     91                                                    destination);
     92             }
     93         }
     94         if (gateway == null) {
     95             if (destination.getAddress() instanceof Inet4Address) {
     96                 gateway = Inet4Address.ANY;
     97             } else {
     98                 gateway = Inet6Address.ANY;
     99             }
    100         }
    101         mHasGateway = (!gateway.isAnyLocalAddress());
    102 
    103         mDestination = new LinkAddress(NetworkUtils.getNetworkPart(destination.getAddress(),
    104                 destination.getNetworkPrefixLength()), destination.getNetworkPrefixLength());
    105         mGateway = gateway;
    106         mInterface = iface;
    107         mIsDefault = isDefault();
    108         mIsHost = isHost();
    109     }
    110 
    111     public RouteInfo(LinkAddress destination, InetAddress gateway) {
    112         this(destination, gateway, null);
    113     }
    114 
    115     public RouteInfo(InetAddress gateway) {
    116         this(null, gateway, null);
    117     }
    118 
    119     public RouteInfo(LinkAddress host) {
    120         this(host, null, null);
    121     }
    122 
    123     public static RouteInfo makeHostRoute(InetAddress host, String iface) {
    124         return makeHostRoute(host, null, iface);
    125     }
    126 
    127     public static RouteInfo makeHostRoute(InetAddress host, InetAddress gateway, String iface) {
    128         if (host == null) return null;
    129 
    130         if (host instanceof Inet4Address) {
    131             return new RouteInfo(new LinkAddress(host, 32), gateway, iface);
    132         } else {
    133             return new RouteInfo(new LinkAddress(host, 128), gateway, iface);
    134         }
    135     }
    136 
    137     private boolean isHost() {
    138         return (mDestination.getAddress() instanceof Inet4Address &&
    139                 mDestination.getNetworkPrefixLength() == 32) ||
    140                (mDestination.getAddress() instanceof Inet6Address &&
    141                 mDestination.getNetworkPrefixLength() == 128);
    142     }
    143 
    144     private boolean isDefault() {
    145         boolean val = false;
    146         if (mGateway != null) {
    147             if (mGateway instanceof Inet4Address) {
    148                 val = (mDestination == null || mDestination.getNetworkPrefixLength() == 0);
    149             } else {
    150                 val = (mDestination == null || mDestination.getNetworkPrefixLength() == 0);
    151             }
    152         }
    153         return val;
    154     }
    155 
    156 
    157     public LinkAddress getDestination() {
    158         return mDestination;
    159     }
    160 
    161     public InetAddress getGateway() {
    162         return mGateway;
    163     }
    164 
    165     public String getInterface() {
    166         return mInterface;
    167     }
    168 
    169     public boolean isDefaultRoute() {
    170         return mIsDefault;
    171     }
    172 
    173     public boolean isHostRoute() {
    174         return mIsHost;
    175     }
    176 
    177     public boolean hasGateway() {
    178         return mHasGateway;
    179     }
    180 
    181     public String toString() {
    182         String val = "";
    183         if (mDestination != null) val = mDestination.toString();
    184         if (mGateway != null) val += " -> " + mGateway.getHostAddress();
    185         return val;
    186     }
    187 
    188     public int describeContents() {
    189         return 0;
    190     }
    191 
    192     public void writeToParcel(Parcel dest, int flags) {
    193         if (mDestination == null) {
    194             dest.writeByte((byte) 0);
    195         } else {
    196             dest.writeByte((byte) 1);
    197             dest.writeByteArray(mDestination.getAddress().getAddress());
    198             dest.writeInt(mDestination.getNetworkPrefixLength());
    199         }
    200 
    201         if (mGateway == null) {
    202             dest.writeByte((byte) 0);
    203         } else {
    204             dest.writeByte((byte) 1);
    205             dest.writeByteArray(mGateway.getAddress());
    206         }
    207 
    208         dest.writeString(mInterface);
    209     }
    210 
    211     @Override
    212     public boolean equals(Object obj) {
    213         if (this == obj) return true;
    214 
    215         if (!(obj instanceof RouteInfo)) return false;
    216 
    217         RouteInfo target = (RouteInfo) obj;
    218 
    219         boolean sameDestination = ( mDestination == null) ?
    220                 target.getDestination() == null
    221                 : mDestination.equals(target.getDestination());
    222 
    223         boolean sameAddress = (mGateway == null) ?
    224                 target.getGateway() == null
    225                 : mGateway.equals(target.getGateway());
    226 
    227         boolean sameInterface = (mInterface == null) ?
    228                 target.getInterface() == null
    229                 : mInterface.equals(target.getInterface());
    230 
    231         return sameDestination && sameAddress && sameInterface
    232             && mIsDefault == target.mIsDefault;
    233     }
    234 
    235     @Override
    236     public int hashCode() {
    237         return (mDestination == null ? 0 : mDestination.hashCode() * 41)
    238             + (mGateway == null ? 0 :mGateway.hashCode() * 47)
    239             + (mInterface == null ? 0 :mInterface.hashCode() * 67)
    240             + (mIsDefault ? 3 : 7);
    241     }
    242 
    243     public static final Creator<RouteInfo> CREATOR =
    244         new Creator<RouteInfo>() {
    245         public RouteInfo createFromParcel(Parcel in) {
    246             InetAddress destAddr = null;
    247             int prefix = 0;
    248             InetAddress gateway = null;
    249 
    250             if (in.readByte() == 1) {
    251                 byte[] addr = in.createByteArray();
    252                 prefix = in.readInt();
    253 
    254                 try {
    255                     destAddr = InetAddress.getByAddress(addr);
    256                 } catch (UnknownHostException e) {}
    257             }
    258 
    259             if (in.readByte() == 1) {
    260                 byte[] addr = in.createByteArray();
    261 
    262                 try {
    263                     gateway = InetAddress.getByAddress(addr);
    264                 } catch (UnknownHostException e) {}
    265             }
    266 
    267             String iface = in.readString();
    268 
    269             LinkAddress dest = null;
    270 
    271             if (destAddr != null) {
    272                 dest = new LinkAddress(destAddr, prefix);
    273             }
    274 
    275             return new RouteInfo(dest, gateway, iface);
    276         }
    277 
    278         public RouteInfo[] newArray(int size) {
    279             return new RouteInfo[size];
    280         }
    281     };
    282 
    283     protected boolean matches(InetAddress destination) {
    284         if (destination == null) return false;
    285 
    286         // match the route destination and destination with prefix length
    287         InetAddress dstNet = NetworkUtils.getNetworkPart(destination,
    288                 mDestination.getNetworkPrefixLength());
    289 
    290         return mDestination.getAddress().equals(dstNet);
    291     }
    292 
    293     /**
    294      * Find the route from a Collection of routes that best matches a given address.
    295      * May return null if no routes are applicable.
    296      * @param routes a Collection of RouteInfos to chose from
    297      * @param dest the InetAddress your trying to get to
    298      * @return the RouteInfo from the Collection that best fits the given address
    299      */
    300     public static RouteInfo selectBestRoute(Collection<RouteInfo> routes, InetAddress dest) {
    301         if ((routes == null) || (dest == null)) return null;
    302 
    303         RouteInfo bestRoute = null;
    304         // pick a longest prefix match under same address type
    305         for (RouteInfo route : routes) {
    306             if (NetworkUtils.addressTypeMatches(route.mDestination.getAddress(), dest)) {
    307                 if ((bestRoute != null) &&
    308                         (bestRoute.mDestination.getNetworkPrefixLength() >=
    309                         route.mDestination.getNetworkPrefixLength())) {
    310                     continue;
    311                 }
    312                 if (route.matches(dest)) bestRoute = route;
    313             }
    314         }
    315         return bestRoute;
    316     }
    317 }
    318