RouteInfo.java revision 4095bd5a8811d4d8c2bb0efc28d9610629e27c89
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;
28
29/**
30 * A simple container for route information.
31 * <p>
32 * This is used both to describe static network configuration and live network
33 * configuration information.  In the static case the interface name (retrieved
34 * via {@link #getInterface}) should be {@code null} as that information will not
35 * yet be known.
36 *
37 * A route may be configured with:
38 * <ul>
39 * <li>a destination {@link LinkAddress} for directly-connected subnets,
40 * <li>a gateway {@link InetAddress} for default routes,
41 * <li>or both for a subnet.
42 * </ul>
43 */
44public class RouteInfo implements Parcelable {
45    /**
46     * The IP destination address for this route.
47     */
48    private final LinkAddress mDestination;
49
50    /**
51     * The gateway address for this route.
52     */
53    private final InetAddress mGateway;
54
55    /**
56     * The interface for this route.
57     */
58    private final String mInterface;
59
60    private final boolean mIsDefault;
61    private final boolean mIsHost;
62    private final boolean mHasGateway;
63
64    /**
65     * Constructs a RouteInfo object.
66     *
67     * If destination is null, then gateway must be specified and the
68     * constructed route is either the IPv4 default route <code>0.0.0.0</code>
69     * if the gateway is an instance of {@link Inet4Address}, or the IPv6 default
70     * route <code>::/0</code> if gateway is an instance of
71     * {@link Inet6Address}.
72     *
73     * destination and gateway may not both be null.
74     *
75     * @param destination the destination prefix
76     * @param gateway the IP address to route packets through
77     * @param iface the interface name to send packets on
78     *
79     * @hide
80     */
81    public RouteInfo(LinkAddress destination, InetAddress gateway, String iface) {
82        if (destination == null) {
83            if (gateway != null) {
84                if (gateway instanceof Inet4Address) {
85                    destination = new LinkAddress(Inet4Address.ANY, 0);
86                } else {
87                    destination = new LinkAddress(Inet6Address.ANY, 0);
88                }
89            } else {
90                // no destination, no gateway. invalid.
91                throw new IllegalArgumentException("Invalid arguments passed in: " + gateway + "," +
92                                                   destination);
93            }
94        }
95        if (gateway == null) {
96            if (destination.getAddress() instanceof Inet4Address) {
97                gateway = Inet4Address.ANY;
98            } else {
99                gateway = Inet6Address.ANY;
100            }
101        }
102        mHasGateway = (!gateway.isAnyLocalAddress());
103
104        mDestination = new LinkAddress(NetworkUtils.getNetworkPart(destination.getAddress(),
105                destination.getNetworkPrefixLength()), destination.getNetworkPrefixLength());
106        mGateway = gateway;
107        mInterface = iface;
108        mIsDefault = isDefault();
109        mIsHost = isHost();
110    }
111
112    /**
113     * Constructs a {@code RouteInfo} object.
114     *
115     * If destination is null, then gateway must be specified and the
116     * constructed route is either the IPv4 default route <code>0.0.0.0</code>
117     * if the gateway is an instance of {@link Inet4Address}, or the IPv6 default
118     * route <code>::/0</code> if gateway is an instance of {@link Inet6Address}.
119     * <p>
120     * Destination and gateway may not both be null.
121     *
122     * @param destination the destination address and prefix in a {@link LinkAddress}
123     * @param gateway the {@link InetAddress} to route packets through
124     */
125    public RouteInfo(LinkAddress destination, InetAddress gateway) {
126        this(destination, gateway, null);
127    }
128
129    /**
130     * Constructs a default {@code RouteInfo} object.
131     *
132     * @param gateway the {@link InetAddress} to route packets through
133     */
134    public RouteInfo(InetAddress gateway) {
135        this(null, gateway, null);
136    }
137
138    /**
139     * Constructs a {@code RouteInfo} object representing a direct connected subnet.
140     *
141     * @param host the {@link LinkAddress} describing the address and prefix length of the subnet.
142     */
143    public RouteInfo(LinkAddress host) {
144        this(host, null, null);
145    }
146
147    /**
148     * @hide
149     */
150    public static RouteInfo makeHostRoute(InetAddress host, String iface) {
151        return makeHostRoute(host, null, iface);
152    }
153
154    /**
155     * @hide
156     */
157    public static RouteInfo makeHostRoute(InetAddress host, InetAddress gateway, String iface) {
158        if (host == null) return null;
159
160        if (host instanceof Inet4Address) {
161            return new RouteInfo(new LinkAddress(host, 32), gateway, iface);
162        } else {
163            return new RouteInfo(new LinkAddress(host, 128), gateway, iface);
164        }
165    }
166
167    private boolean isHost() {
168        return (mDestination.getAddress() instanceof Inet4Address &&
169                mDestination.getNetworkPrefixLength() == 32) ||
170               (mDestination.getAddress() instanceof Inet6Address &&
171                mDestination.getNetworkPrefixLength() == 128);
172    }
173
174    private boolean isDefault() {
175        boolean val = false;
176        if (mGateway != null) {
177            if (mGateway instanceof Inet4Address) {
178                val = (mDestination == null || mDestination.getNetworkPrefixLength() == 0);
179            } else {
180                val = (mDestination == null || mDestination.getNetworkPrefixLength() == 0);
181            }
182        }
183        return val;
184    }
185
186    /**
187     * Retrieves the destination address and prefix length in the form of a {@link LinkAddress}.
188     *
189     * @return {@link LinkAddress} specifying the destination.
190     */
191    public LinkAddress getDestination() {
192        return mDestination;
193    }
194
195    /**
196     * Retrieves the gateway or next hop {@link InetAddress} for this route.
197     *
198     * @return {@link InetAddress} specifying the gateway or next hop.
199     */
200    public InetAddress getGateway() {
201        return mGateway;
202    }
203
204    /**
205     * Retrieves the interface used for this route, if known.  Note that for static
206     * network configurations, this won't be set.
207     *
208     * @return The name of the interface used for this route.
209     */
210    public String getInterface() {
211        return mInterface;
212    }
213
214    /**
215     * Indicates if this route is a default route (ie, has no destination specified).
216     *
217     * @return {@code true} if the destination is null or has a prefix length of 0.
218     */
219    public boolean isDefaultRoute() {
220        return mIsDefault;
221    }
222
223    /**
224     * Indicates if this route is a host route (ie, matches only a single host address).
225     *
226     * @return {@code true} if the destination has a prefix length of 32/128 for v4/v6.
227     */
228    public boolean isHostRoute() {
229        return mIsHost;
230    }
231
232    /**
233     * Indicates if this route has a next hop ({@code true}) or is directly-connected
234     * ({@code false}).
235     *
236     * @return {@code true} if a gateway is specified
237     */
238    public boolean hasGateway() {
239        return mHasGateway;
240    }
241
242    /**
243     * @hide
244     */
245    protected boolean matches(InetAddress destination) {
246        if (destination == null) return false;
247
248        // match the route destination and destination with prefix length
249        InetAddress dstNet = NetworkUtils.getNetworkPart(destination,
250                mDestination.getNetworkPrefixLength());
251
252        return mDestination.getAddress().equals(dstNet);
253    }
254
255    /**
256     * Find the route from a Collection of routes that best matches a given address.
257     * May return null if no routes are applicable.
258     * @param routes a Collection of RouteInfos to chose from
259     * @param dest the InetAddress your trying to get to
260     * @return the RouteInfo from the Collection that best fits the given address
261     *
262     * @hide
263     */
264    public static RouteInfo selectBestRoute(Collection<RouteInfo> routes, InetAddress dest) {
265        if ((routes == null) || (dest == null)) return null;
266
267        RouteInfo bestRoute = null;
268        // pick a longest prefix match under same address type
269        for (RouteInfo route : routes) {
270            if (NetworkUtils.addressTypeMatches(route.mDestination.getAddress(), dest)) {
271                if ((bestRoute != null) &&
272                        (bestRoute.mDestination.getNetworkPrefixLength() >=
273                        route.mDestination.getNetworkPrefixLength())) {
274                    continue;
275                }
276                if (route.matches(dest)) bestRoute = route;
277            }
278        }
279        return bestRoute;
280    }
281
282    public String toString() {
283        String val = "";
284        if (mDestination != null) val = mDestination.toString();
285        if (mGateway != null) val += " -> " + mGateway.getHostAddress();
286        return val;
287    }
288
289    public boolean equals(Object obj) {
290        if (this == obj) return true;
291
292        if (!(obj instanceof RouteInfo)) return false;
293
294        RouteInfo target = (RouteInfo) obj;
295
296        boolean sameDestination = ( mDestination == null) ?
297                target.getDestination() == null
298                : mDestination.equals(target.getDestination());
299
300        boolean sameAddress = (mGateway == null) ?
301                target.getGateway() == null
302                : mGateway.equals(target.getGateway());
303
304        boolean sameInterface = (mInterface == null) ?
305                target.getInterface() == null
306                : mInterface.equals(target.getInterface());
307
308        return sameDestination && sameAddress && sameInterface
309                && mIsDefault == target.mIsDefault;
310    }
311
312    public int hashCode() {
313        return (mDestination == null ? 0 : mDestination.hashCode() * 41)
314                + (mGateway == null ? 0 :mGateway.hashCode() * 47)
315                + (mInterface == null ? 0 :mInterface.hashCode() * 67)
316                + (mIsDefault ? 3 : 7);
317    }
318
319    /**
320     * Implement the Parcelable interface
321     * @hide
322     */
323    public int describeContents() {
324        return 0;
325    }
326
327    /**
328     * Implement the Parcelable interface
329     * @hide
330     */
331    public void writeToParcel(Parcel dest, int flags) {
332        if (mDestination == null) {
333            dest.writeByte((byte) 0);
334        } else {
335            dest.writeByte((byte) 1);
336            dest.writeByteArray(mDestination.getAddress().getAddress());
337            dest.writeInt(mDestination.getNetworkPrefixLength());
338        }
339
340        if (mGateway == null) {
341            dest.writeByte((byte) 0);
342        } else {
343            dest.writeByte((byte) 1);
344            dest.writeByteArray(mGateway.getAddress());
345        }
346
347        dest.writeString(mInterface);
348    }
349
350    /**
351     * Implement the Parcelable interface.
352     * @hide
353     */
354    public static final Creator<RouteInfo> CREATOR =
355        new Creator<RouteInfo>() {
356        public RouteInfo createFromParcel(Parcel in) {
357            InetAddress destAddr = null;
358            int prefix = 0;
359            InetAddress gateway = null;
360
361            if (in.readByte() == 1) {
362                byte[] addr = in.createByteArray();
363                prefix = in.readInt();
364
365                try {
366                    destAddr = InetAddress.getByAddress(addr);
367                } catch (UnknownHostException e) {}
368            }
369
370            if (in.readByte() == 1) {
371                byte[] addr = in.createByteArray();
372
373                try {
374                    gateway = InetAddress.getByAddress(addr);
375                } catch (UnknownHostException e) {}
376            }
377
378            String iface = in.readString();
379
380            LinkAddress dest = null;
381
382            if (destAddr != null) {
383                dest = new LinkAddress(destAddr, prefix);
384            }
385
386            return new RouteInfo(dest, gateway, iface);
387        }
388
389        public RouteInfo[] newArray(int size) {
390            return new RouteInfo[size];
391        }
392    };
393}
394