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