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