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      */
RouteInfo(IpPrefix destination, InetAddress gateway, String iface, int type)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      */
RouteInfo(IpPrefix destination, InetAddress gateway, String iface)163     public RouteInfo(IpPrefix destination, InetAddress gateway, String iface) {
164         this(destination, gateway, iface, RTN_UNICAST);
165     }
166 
167     /**
168      * @hide
169      */
RouteInfo(LinkAddress destination, InetAddress gateway, String iface)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      */
RouteInfo(IpPrefix destination, InetAddress gateway)191     public RouteInfo(IpPrefix destination, InetAddress gateway) {
192         this(destination, gateway, null);
193     }
194 
195     /**
196      * @hide
197      *
198      * TODO: Remove this.
199      */
RouteInfo(LinkAddress destination, InetAddress gateway)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      */
RouteInfo(InetAddress gateway)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      */
RouteInfo(IpPrefix destination)223     public RouteInfo(IpPrefix destination) {
224         this(destination, null, null);
225     }
226 
227     /**
228      * @hide
229      */
RouteInfo(LinkAddress destination)230     public RouteInfo(LinkAddress destination) {
231         this(destination, null, null);
232     }
233 
234     /**
235      * @hide
236      */
RouteInfo(IpPrefix destination, int type)237     public RouteInfo(IpPrefix destination, int type) {
238         this(destination, null, null, type);
239     }
240 
241     /**
242      * @hide
243      */
makeHostRoute(InetAddress host, String iface)244     public static RouteInfo makeHostRoute(InetAddress host, String iface) {
245         return makeHostRoute(host, null, iface);
246     }
247 
248     /**
249      * @hide
250      */
makeHostRoute(InetAddress host, InetAddress gateway, String iface)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 
isHost()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      */
getDestination()273     public IpPrefix getDestination() {
274         return mDestination;
275     }
276 
277     /**
278      * TODO: Convert callers to use IpPrefix and then remove.
279      * @hide
280      */
getDestinationLinkAddress()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      */
getGateway()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      */
getInterface()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      */
getType()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      */
isDefaultRoute()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      */
isIPv4Default()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      */
isIPv6Default()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      */
isHostRoute()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      */
hasGateway()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      */
matches(InetAddress destination)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      */
selectBestRoute(Collection<RouteInfo> routes, InetAddress dest)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      */
toString()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      */
equals(Object obj)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      */
hashCode()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      */
describeContents()451     public int describeContents() {
452         return 0;
453     }
454 
455     /**
456      * Implement the Parcelable interface
457      */
writeToParcel(Parcel dest, int flags)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