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 class RouteInfo implements Parcelable {
50     /**
51      * The IP destination address for this route.
52      * TODO: Make this an IpPrefix.
53      */
54     private final LinkAddress mDestination;
55 
56     /**
57      * The gateway address for this route.
58      */
59     private final InetAddress mGateway;
60 
61     /**
62      * The interface for this route.
63      */
64     private final String mInterface;
65 
66     private final boolean mIsDefault;
67     private final boolean mIsHost;
68     private final boolean mHasGateway;
69 
70     /**
71      * Constructs a RouteInfo object.
72      *
73      * If destination is null, then gateway must be specified and the
74      * constructed route is either the IPv4 default route <code>0.0.0.0</code>
75      * if the gateway is an instance of {@link Inet4Address}, or the IPv6 default
76      * route <code>::/0</code> if gateway is an instance of
77      * {@link Inet6Address}.
78      * <p>
79      * destination and gateway may not both be null.
80      *
81      * @param destination the destination prefix
82      * @param gateway the IP address to route packets through
83      * @param iface the interface name to send packets on
84      *
85      * TODO: Convert to use IpPrefix.
86      *
87      * @hide
88      */
RouteInfo(IpPrefix destination, InetAddress gateway, String iface)89     public RouteInfo(IpPrefix destination, InetAddress gateway, String iface) {
90         this(destination == null ? null :
91                 new LinkAddress(destination.getAddress(), destination.getPrefixLength()),
92                 gateway, iface);
93     }
94 
95     /**
96      * @hide
97      */
RouteInfo(LinkAddress destination, InetAddress gateway, String iface)98     public RouteInfo(LinkAddress destination, InetAddress gateway, String iface) {
99         if (destination == null) {
100             if (gateway != null) {
101                 if (gateway instanceof Inet4Address) {
102                     destination = new LinkAddress(Inet4Address.ANY, 0);
103                 } else {
104                     destination = new LinkAddress(Inet6Address.ANY, 0);
105                 }
106             } else {
107                 // no destination, no gateway. invalid.
108                 throw new IllegalArgumentException("Invalid arguments passed in: " + gateway + "," +
109                                                    destination);
110             }
111         }
112         if (gateway == null) {
113             if (destination.getAddress() instanceof Inet4Address) {
114                 gateway = Inet4Address.ANY;
115             } else {
116                 gateway = Inet6Address.ANY;
117             }
118         }
119         mHasGateway = (!gateway.isAnyLocalAddress());
120 
121         mDestination = new LinkAddress(NetworkUtils.getNetworkPart(destination.getAddress(),
122                 destination.getPrefixLength()), destination.getPrefixLength());
123         if ((destination.getAddress() instanceof Inet4Address &&
124                  (gateway instanceof Inet4Address == false)) ||
125                 (destination.getAddress() instanceof Inet6Address &&
126                  (gateway instanceof Inet6Address == false))) {
127             throw new IllegalArgumentException("address family mismatch in RouteInfo constructor");
128         }
129         mGateway = gateway;
130         mInterface = iface;
131         mIsDefault = isDefault();
132         mIsHost = isHost();
133     }
134 
135     /**
136      * Constructs a {@code RouteInfo} object.
137      *
138      * If destination is null, then gateway must be specified and the
139      * constructed route is either the IPv4 default route <code>0.0.0.0</code>
140      * if the gateway is an instance of {@link Inet4Address}, or the IPv6 default
141      * route <code>::/0</code> if gateway is an instance of {@link Inet6Address}.
142      * <p>
143      * Destination and gateway may not both be null.
144      *
145      * @param destination the destination address and prefix in an {@link IpPrefix}
146      * @param gateway the {@link InetAddress} to route packets through
147      *
148      * @hide
149      */
RouteInfo(IpPrefix destination, InetAddress gateway)150     public RouteInfo(IpPrefix destination, InetAddress gateway) {
151         this(destination, gateway, null);
152     }
153 
154     /**
155      * @hide
156      */
RouteInfo(LinkAddress destination, InetAddress gateway)157     public RouteInfo(LinkAddress destination, InetAddress gateway) {
158         this(destination, gateway, null);
159     }
160 
161     /**
162      * Constructs a default {@code RouteInfo} object.
163      *
164      * @param gateway the {@link InetAddress} to route packets through
165      *
166      * @hide
167      */
RouteInfo(InetAddress gateway)168     public RouteInfo(InetAddress gateway) {
169         this((LinkAddress) null, gateway, null);
170     }
171 
172     /**
173      * Constructs a {@code RouteInfo} object representing a direct connected subnet.
174      *
175      * @param destination the {@link IpPrefix} describing the address and prefix
176      *                    length of the subnet.
177      *
178      * @hide
179      */
RouteInfo(IpPrefix destination)180     public RouteInfo(IpPrefix destination) {
181         this(destination, null, null);
182     }
183 
184     /**
185      * @hide
186      */
RouteInfo(LinkAddress destination)187     public RouteInfo(LinkAddress destination) {
188         this(destination, null, null);
189     }
190 
191     /**
192      * @hide
193      */
makeHostRoute(InetAddress host, String iface)194     public static RouteInfo makeHostRoute(InetAddress host, String iface) {
195         return makeHostRoute(host, null, iface);
196     }
197 
198     /**
199      * @hide
200      */
makeHostRoute(InetAddress host, InetAddress gateway, String iface)201     public static RouteInfo makeHostRoute(InetAddress host, InetAddress gateway, String iface) {
202         if (host == null) return null;
203 
204         if (host instanceof Inet4Address) {
205             return new RouteInfo(new LinkAddress(host, 32), gateway, iface);
206         } else {
207             return new RouteInfo(new LinkAddress(host, 128), gateway, iface);
208         }
209     }
210 
isHost()211     private boolean isHost() {
212         return (mDestination.getAddress() instanceof Inet4Address &&
213                 mDestination.getPrefixLength() == 32) ||
214                (mDestination.getAddress() instanceof Inet6Address &&
215                 mDestination.getPrefixLength() == 128);
216     }
217 
isDefault()218     private boolean isDefault() {
219         boolean val = false;
220         if (mGateway != null) {
221             if (mGateway instanceof Inet4Address) {
222                 val = (mDestination == null || mDestination.getPrefixLength() == 0);
223             } else {
224                 val = (mDestination == null || mDestination.getPrefixLength() == 0);
225             }
226         }
227         return val;
228     }
229 
230     /**
231      * Retrieves the destination address and prefix length in the form of an {@link IpPrefix}.
232      *
233      * @return {@link IpPrefix} specifying the destination.  This is never {@code null}.
234      */
getDestination()235     public IpPrefix getDestination() {
236         return new IpPrefix(mDestination.getAddress(), mDestination.getPrefixLength());
237     }
238 
239     /**
240      * TODO: Convert callers to use IpPrefix and then remove.
241      * @hide
242      */
getDestinationLinkAddress()243     public LinkAddress getDestinationLinkAddress() {
244         return mDestination;
245     }
246 
247     /**
248      * Retrieves the gateway or next hop {@link InetAddress} for this route.
249      *
250      * @return {@link InetAddress} specifying the gateway or next hop.  This may be
251      &                             {@code null} for a directly-connected route."
252      */
getGateway()253     public InetAddress getGateway() {
254         return mGateway;
255     }
256 
257     /**
258      * Retrieves the interface used for this route if specified, else {@code null}.
259      *
260      * @return The name of the interface used for this route.
261      */
getInterface()262     public String getInterface() {
263         return mInterface;
264     }
265 
266     /**
267      * Indicates if this route is a default route (ie, has no destination specified).
268      *
269      * @return {@code true} if the destination has a prefix length of 0.
270      */
isDefaultRoute()271     public boolean isDefaultRoute() {
272         return mIsDefault;
273     }
274 
275     /**
276      * Indicates if this route is a host route (ie, matches only a single host address).
277      *
278      * @return {@code true} if the destination has a prefix length of 32 or 128 for IPv4 or IPv6,
279      * respectively.
280      * @hide
281      */
isHostRoute()282     public boolean isHostRoute() {
283         return mIsHost;
284     }
285 
286     /**
287      * Indicates if this route has a next hop ({@code true}) or is directly-connected
288      * ({@code false}).
289      *
290      * @return {@code true} if a gateway is specified
291      * @hide
292      */
hasGateway()293     public boolean hasGateway() {
294         return mHasGateway;
295     }
296 
297     /**
298      * Determines whether the destination and prefix of this route includes the specified
299      * address.
300      *
301      * @param destination A {@link InetAddress} to test to see if it would match this route.
302      * @return {@code true} if the destination and prefix length cover the given address.
303      */
matches(InetAddress destination)304     public boolean matches(InetAddress destination) {
305         if (destination == null) return false;
306 
307         // match the route destination and destination with prefix length
308         InetAddress dstNet = NetworkUtils.getNetworkPart(destination,
309                 mDestination.getPrefixLength());
310 
311         return mDestination.getAddress().equals(dstNet);
312     }
313 
314     /**
315      * Find the route from a Collection of routes that best matches a given address.
316      * May return null if no routes are applicable.
317      * @param routes a Collection of RouteInfos to chose from
318      * @param dest the InetAddress your trying to get to
319      * @return the RouteInfo from the Collection that best fits the given address
320      *
321      * @hide
322      */
selectBestRoute(Collection<RouteInfo> routes, InetAddress dest)323     public static RouteInfo selectBestRoute(Collection<RouteInfo> routes, InetAddress dest) {
324         if ((routes == null) || (dest == null)) return null;
325 
326         RouteInfo bestRoute = null;
327         // pick a longest prefix match under same address type
328         for (RouteInfo route : routes) {
329             if (NetworkUtils.addressTypeMatches(route.mDestination.getAddress(), dest)) {
330                 if ((bestRoute != null) &&
331                         (bestRoute.mDestination.getPrefixLength() >=
332                         route.mDestination.getPrefixLength())) {
333                     continue;
334                 }
335                 if (route.matches(dest)) bestRoute = route;
336             }
337         }
338         return bestRoute;
339     }
340 
341     /**
342      * Returns a human-readable description of this object.
343      */
toString()344     public String toString() {
345         String val = "";
346         if (mDestination != null) val = mDestination.toString();
347         val += " ->";
348         if (mGateway != null) val += " " + mGateway.getHostAddress();
349         if (mInterface != null) val += " " + mInterface;
350         return val;
351     }
352 
353     /**
354      * Compares this RouteInfo object against the specified object and indicates if they are equal.
355      * @return {@code true} if the objects are equal, {@code false} otherwise.
356      */
equals(Object obj)357     public boolean equals(Object obj) {
358         if (this == obj) return true;
359 
360         if (!(obj instanceof RouteInfo)) return false;
361 
362         RouteInfo target = (RouteInfo) obj;
363 
364         return Objects.equals(mDestination, target.getDestinationLinkAddress()) &&
365                 Objects.equals(mGateway, target.getGateway()) &&
366                 Objects.equals(mInterface, target.getInterface());
367     }
368 
369     /**
370      *  Returns a hashcode for this <code>RouteInfo</code> object.
371      */
hashCode()372     public int hashCode() {
373         return (mDestination == null ? 0 : mDestination.hashCode() * 41)
374                 + (mGateway == null ? 0 :mGateway.hashCode() * 47)
375                 + (mInterface == null ? 0 :mInterface.hashCode() * 67)
376                 + (mIsDefault ? 3 : 7);
377     }
378 
379     /**
380      * Implement the Parcelable interface
381      * @hide
382      */
describeContents()383     public int describeContents() {
384         return 0;
385     }
386 
387     /**
388      * Implement the Parcelable interface
389      * @hide
390      */
writeToParcel(Parcel dest, int flags)391     public void writeToParcel(Parcel dest, int flags) {
392         if (mDestination == null) {
393             dest.writeByte((byte) 0);
394         } else {
395             dest.writeByte((byte) 1);
396             dest.writeByteArray(mDestination.getAddress().getAddress());
397             dest.writeInt(mDestination.getPrefixLength());
398         }
399 
400         if (mGateway == null) {
401             dest.writeByte((byte) 0);
402         } else {
403             dest.writeByte((byte) 1);
404             dest.writeByteArray(mGateway.getAddress());
405         }
406 
407         dest.writeString(mInterface);
408     }
409 
410     /**
411      * Implement the Parcelable interface.
412      * @hide
413      */
414     public static final Creator<RouteInfo> CREATOR =
415         new Creator<RouteInfo>() {
416         public RouteInfo createFromParcel(Parcel in) {
417             InetAddress destAddr = null;
418             int prefix = 0;
419             InetAddress gateway = null;
420 
421             if (in.readByte() == 1) {
422                 byte[] addr = in.createByteArray();
423                 prefix = in.readInt();
424 
425                 try {
426                     destAddr = InetAddress.getByAddress(addr);
427                 } catch (UnknownHostException e) {}
428             }
429 
430             if (in.readByte() == 1) {
431                 byte[] addr = in.createByteArray();
432 
433                 try {
434                     gateway = InetAddress.getByAddress(addr);
435                 } catch (UnknownHostException e) {}
436             }
437 
438             String iface = in.readString();
439 
440             LinkAddress dest = null;
441 
442             if (destAddr != null) {
443                 dest = new LinkAddress(destAddr, prefix);
444             }
445 
446             return new RouteInfo(dest, gateway, iface);
447         }
448 
449         public RouteInfo[] newArray(int size) {
450             return new RouteInfo[size];
451         }
452     };
453 }
454