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