• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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         if (destination == null) return false;
371 
372         // match the route destination and destination with prefix length
373         InetAddress dstNet = NetworkUtils.getNetworkPart(destination,
374                 mDestination.getPrefixLength());
375 
376         return mDestination.getAddress().equals(dstNet);
377     }
378 
379     /**
380      * Find the route from a Collection of routes that best matches a given address.
381      * May return null if no routes are applicable.
382      * @param routes a Collection of RouteInfos to chose from
383      * @param dest the InetAddress your trying to get to
384      * @return the RouteInfo from the Collection that best fits the given address
385      *
386      * @hide
387      */
selectBestRoute(Collection<RouteInfo> routes, InetAddress dest)388     public static RouteInfo selectBestRoute(Collection<RouteInfo> routes, InetAddress dest) {
389         if ((routes == null) || (dest == null)) return null;
390 
391         RouteInfo bestRoute = null;
392         // pick a longest prefix match under same address type
393         for (RouteInfo route : routes) {
394             if (NetworkUtils.addressTypeMatches(route.mDestination.getAddress(), dest)) {
395                 if ((bestRoute != null) &&
396                         (bestRoute.mDestination.getPrefixLength() >=
397                         route.mDestination.getPrefixLength())) {
398                     continue;
399                 }
400                 if (route.matches(dest)) bestRoute = route;
401             }
402         }
403         return bestRoute;
404     }
405 
406     /**
407      * Returns a human-readable description of this object.
408      */
toString()409     public String toString() {
410         String val = "";
411         if (mDestination != null) val = mDestination.toString();
412         if (mType == RTN_UNREACHABLE) {
413             val += " unreachable";
414         } else if (mType == RTN_THROW) {
415             val += " throw";
416         } else {
417             val += " ->";
418             if (mGateway != null) val += " " + mGateway.getHostAddress();
419             if (mInterface != null) val += " " + mInterface;
420             if (mType != RTN_UNICAST) {
421                 val += " unknown type " + mType;
422             }
423         }
424         return val;
425     }
426 
427     /**
428      * Compares this RouteInfo object against the specified object and indicates if they are equal.
429      * @return {@code true} if the objects are equal, {@code false} otherwise.
430      */
equals(Object obj)431     public boolean equals(Object obj) {
432         if (this == obj) return true;
433 
434         if (!(obj instanceof RouteInfo)) return false;
435 
436         RouteInfo target = (RouteInfo) obj;
437 
438         return Objects.equals(mDestination, target.getDestination()) &&
439                 Objects.equals(mGateway, target.getGateway()) &&
440                 Objects.equals(mInterface, target.getInterface()) &&
441                 mType == target.getType();
442     }
443 
444     /**
445      *  Returns a hashcode for this <code>RouteInfo</code> object.
446      */
hashCode()447     public int hashCode() {
448         return (mDestination.hashCode() * 41)
449                 + (mGateway == null ? 0 :mGateway.hashCode() * 47)
450                 + (mInterface == null ? 0 :mInterface.hashCode() * 67)
451                 + (mType * 71);
452     }
453 
454     /**
455      * Implement the Parcelable interface
456      */
describeContents()457     public int describeContents() {
458         return 0;
459     }
460 
461     /**
462      * Implement the Parcelable interface
463      */
writeToParcel(Parcel dest, int flags)464     public void writeToParcel(Parcel dest, int flags) {
465         dest.writeParcelable(mDestination, flags);
466         byte[] gatewayBytes = (mGateway == null) ? null : mGateway.getAddress();
467         dest.writeByteArray(gatewayBytes);
468         dest.writeString(mInterface);
469         dest.writeInt(mType);
470     }
471 
472     /**
473      * Implement the Parcelable interface.
474      */
475     public static final Creator<RouteInfo> CREATOR =
476         new Creator<RouteInfo>() {
477         public RouteInfo createFromParcel(Parcel in) {
478             IpPrefix dest = in.readParcelable(null);
479 
480             InetAddress gateway = null;
481             byte[] addr = in.createByteArray();
482             try {
483                 gateway = InetAddress.getByAddress(addr);
484             } catch (UnknownHostException e) {}
485 
486             String iface = in.readString();
487             int type = in.readInt();
488 
489             return new RouteInfo(dest, gateway, iface, type);
490         }
491 
492         public RouteInfo[] newArray(int size) {
493             return new RouteInfo[size];
494         }
495     };
496 }
497