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.annotation.IntDef;
20 import android.annotation.NonNull;
21 import android.annotation.Nullable;
22 import android.annotation.SystemApi;
23 import android.annotation.TestApi;
24 import android.compat.annotation.UnsupportedAppUsage;
25 import android.net.util.NetUtils;
26 import android.os.Build;
27 import android.os.Parcel;
28 import android.os.Parcelable;
29 
30 import java.lang.annotation.Retention;
31 import java.lang.annotation.RetentionPolicy;
32 import java.net.Inet4Address;
33 import java.net.Inet6Address;
34 import java.net.InetAddress;
35 import java.net.UnknownHostException;
36 import java.util.Collection;
37 import java.util.Objects;
38 
39 /**
40  * Represents a network route.
41  * <p>
42  * This is used both to describe static network configuration and live network
43  * configuration information.
44  *
45  * A route contains three pieces of information:
46  * <ul>
47  * <li>a destination {@link IpPrefix} specifying the network destinations covered by this route.
48  *     If this is {@code null} it indicates a default route of the address family (IPv4 or IPv6)
49  *     implied by the gateway IP address.
50  * <li>a gateway {@link InetAddress} indicating the next hop to use.  If this is {@code null} it
51  *     indicates a directly-connected route.
52  * <li>an interface (which may be unspecified).
53  * </ul>
54  * Either the destination or the gateway may be {@code null}, but not both.  If the
55  * destination and gateway are both specified, they must be of the same address family
56  * (IPv4 or IPv6).
57  */
58 public final class RouteInfo implements Parcelable {
59     /** @hide */
60     @IntDef(value = {
61             RTN_UNICAST,
62             RTN_UNREACHABLE,
63             RTN_THROW,
64     })
65     @Retention(RetentionPolicy.SOURCE)
66     public @interface RouteType {}
67 
68     /**
69      * The IP destination address for this route.
70      */
71     @NonNull
72     private final IpPrefix mDestination;
73 
74     /**
75      * The gateway address for this route.
76      */
77     @UnsupportedAppUsage
78     @Nullable
79     private final InetAddress mGateway;
80 
81     /**
82      * The interface for this route.
83      */
84     @Nullable
85     private final String mInterface;
86 
87 
88     /** Unicast route. @hide */
89     @SystemApi
90     @TestApi
91     public static final int RTN_UNICAST = 1;
92 
93     /** Unreachable route. @hide */
94     @SystemApi
95     @TestApi
96     public static final int RTN_UNREACHABLE = 7;
97 
98     /** Throw route. @hide */
99     @SystemApi
100     @TestApi
101     public static final int RTN_THROW = 9;
102 
103     /**
104      * The type of this route; one of the RTN_xxx constants above.
105      */
106     private final int mType;
107 
108     /**
109      * The maximum transmission unit size for this route.
110      */
111     private final int mMtu;
112 
113     // Derived data members.
114     // TODO: remove these.
115     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
116     private final boolean mIsHost;
117     private final boolean mHasGateway;
118 
119     /**
120      * Constructs a RouteInfo object.
121      *
122      * If destination is null, then gateway must be specified and the
123      * constructed route is either the IPv4 default route <code>0.0.0.0</code>
124      * if the gateway is an instance of {@link Inet4Address}, or the IPv6 default
125      * route <code>::/0</code> if gateway is an instance of
126      * {@link Inet6Address}.
127      * <p>
128      * destination and gateway may not both be null.
129      *
130      * @param destination the destination prefix
131      * @param gateway the IP address to route packets through
132      * @param iface the interface name to send packets on
133      * @param type the type of this route
134      *
135      * @hide
136      */
137     @SystemApi
138     @TestApi
RouteInfo(@ullable IpPrefix destination, @Nullable InetAddress gateway, @Nullable String iface, @RouteType int type)139     public RouteInfo(@Nullable IpPrefix destination, @Nullable InetAddress gateway,
140             @Nullable String iface, @RouteType int type) {
141         this(destination, gateway, iface, type, 0);
142     }
143 
144     /**
145      * Constructs a RouteInfo object.
146      *
147      * If destination is null, then gateway must be specified and the
148      * constructed route is either the IPv4 default route <code>0.0.0.0</code>
149      * if the gateway is an instance of {@link Inet4Address}, or the IPv6 default
150      * route <code>::/0</code> if gateway is an instance of
151      * {@link Inet6Address}.
152      * <p>
153      * destination and gateway may not both be null.
154      *
155      * @param destination the destination prefix
156      * @param gateway the IP address to route packets through
157      * @param iface the interface name to send packets on
158      * @param type the type of this route
159      * @param mtu the maximum transmission unit size for this route
160      *
161      * @hide
162      */
163     @SystemApi
RouteInfo(@ullable IpPrefix destination, @Nullable InetAddress gateway, @Nullable String iface, @RouteType int type, int mtu)164     public RouteInfo(@Nullable IpPrefix destination, @Nullable InetAddress gateway,
165             @Nullable String iface, @RouteType int type, int mtu) {
166         switch (type) {
167             case RTN_UNICAST:
168             case RTN_UNREACHABLE:
169             case RTN_THROW:
170                 // TODO: It would be nice to ensure that route types that don't have nexthops or
171                 // interfaces, such as unreachable or throw, can't be created if an interface or
172                 // a gateway is specified. This is a bit too complicated to do at the moment
173                 // because:
174                 //
175                 // - LinkProperties sets the interface on routes added to it, and modifies the
176                 //   interfaces of all the routes when its interface name changes.
177                 // - Even when the gateway is null, we store a non-null gateway here.
178                 //
179                 // For now, we just rely on the code that sets routes to do things properly.
180                 break;
181             default:
182                 throw new IllegalArgumentException("Unknown route type " + type);
183         }
184 
185         if (destination == null) {
186             if (gateway != null) {
187                 if (gateway instanceof Inet4Address) {
188                     destination = new IpPrefix(Inet4Address.ANY, 0);
189                 } else {
190                     destination = new IpPrefix(Inet6Address.ANY, 0);
191                 }
192             } else {
193                 // no destination, no gateway. invalid.
194                 throw new IllegalArgumentException("Invalid arguments passed in: " + gateway + "," +
195                         destination);
196             }
197         }
198         // TODO: set mGateway to null if there is no gateway. This is more correct, saves space, and
199         // matches the documented behaviour. Before we can do this we need to fix all callers (e.g.,
200         // ConnectivityService) to stop doing things like r.getGateway().equals(), ... .
201         if (gateway == null) {
202             if (destination.getAddress() instanceof Inet4Address) {
203                 gateway = Inet4Address.ANY;
204             } else {
205                 gateway = Inet6Address.ANY;
206             }
207         }
208         mHasGateway = (!gateway.isAnyLocalAddress());
209 
210         if ((destination.getAddress() instanceof Inet4Address
211                 && !(gateway instanceof Inet4Address))
212                 || (destination.getAddress() instanceof Inet6Address
213                 && !(gateway instanceof Inet6Address))) {
214             throw new IllegalArgumentException("address family mismatch in RouteInfo constructor");
215         }
216         mDestination = destination;  // IpPrefix objects are immutable.
217         mGateway = gateway;          // InetAddress objects are immutable.
218         mInterface = iface;          // Strings are immutable.
219         mType = type;
220         mIsHost = isHost();
221         mMtu = mtu;
222     }
223 
224     /**
225      * Constructs a {@code RouteInfo} object.
226      *
227      * If destination is null, then gateway must be specified and the
228      * constructed route is either the IPv4 default route <code>0.0.0.0</code>
229      * if the gateway is an instance of {@link Inet4Address}, or the IPv6 default
230      * route <code>::/0</code> if gateway is an instance of {@link Inet6Address}.
231      * <p>
232      * Destination and gateway may not both be null.
233      *
234      * @param destination the destination address and prefix in an {@link IpPrefix}
235      * @param gateway the {@link InetAddress} to route packets through
236      * @param iface the interface name to send packets on
237      *
238      * @hide
239      */
240     @UnsupportedAppUsage
RouteInfo(@ullable IpPrefix destination, @Nullable InetAddress gateway, @Nullable String iface)241     public RouteInfo(@Nullable IpPrefix destination, @Nullable InetAddress gateway,
242             @Nullable String iface) {
243         this(destination, gateway, iface, RTN_UNICAST);
244     }
245 
246     /**
247      * @hide
248      */
249     @UnsupportedAppUsage
RouteInfo(@ullable LinkAddress destination, @Nullable InetAddress gateway, @Nullable String iface)250     public RouteInfo(@Nullable LinkAddress destination, @Nullable InetAddress gateway,
251             @Nullable String iface) {
252         this(destination == null ? null :
253                 new IpPrefix(destination.getAddress(), destination.getPrefixLength()),
254                 gateway, iface);
255     }
256 
257     /**
258      * Constructs a {@code RouteInfo} object.
259      *
260      * If destination is null, then gateway must be specified and the
261      * constructed route is either the IPv4 default route <code>0.0.0.0</code>
262      * if the gateway is an instance of {@link Inet4Address}, or the IPv6 default
263      * route <code>::/0</code> if gateway is an instance of {@link Inet6Address}.
264      * <p>
265      * Destination and gateway may not both be null.
266      *
267      * @param destination the destination address and prefix in an {@link IpPrefix}
268      * @param gateway the {@link InetAddress} to route packets through
269      *
270      * @hide
271      */
RouteInfo(@ullable IpPrefix destination, @Nullable InetAddress gateway)272     public RouteInfo(@Nullable IpPrefix destination, @Nullable InetAddress gateway) {
273         this(destination, gateway, null);
274     }
275 
276     /**
277      * @hide
278      *
279      * TODO: Remove this.
280      */
281     @UnsupportedAppUsage
RouteInfo(@ullable LinkAddress destination, @Nullable InetAddress gateway)282     public RouteInfo(@Nullable LinkAddress destination, @Nullable InetAddress gateway) {
283         this(destination, gateway, null);
284     }
285 
286     /**
287      * Constructs a default {@code RouteInfo} object.
288      *
289      * @param gateway the {@link InetAddress} to route packets through
290      *
291      * @hide
292      */
293     @UnsupportedAppUsage
RouteInfo(@onNull InetAddress gateway)294     public RouteInfo(@NonNull InetAddress gateway) {
295         this((IpPrefix) null, gateway, null);
296     }
297 
298     /**
299      * Constructs a {@code RouteInfo} object representing a direct connected subnet.
300      *
301      * @param destination the {@link IpPrefix} describing the address and prefix
302      *                    length of the subnet.
303      *
304      * @hide
305      */
RouteInfo(@onNull IpPrefix destination)306     public RouteInfo(@NonNull IpPrefix destination) {
307         this(destination, null, null);
308     }
309 
310     /**
311      * @hide
312      */
RouteInfo(@onNull LinkAddress destination)313     public RouteInfo(@NonNull LinkAddress destination) {
314         this(destination, null, null);
315     }
316 
317     /**
318      * @hide
319      */
RouteInfo(@onNull IpPrefix destination, @RouteType int type)320     public RouteInfo(@NonNull IpPrefix destination, @RouteType int type) {
321         this(destination, null, null, type);
322     }
323 
324     /**
325      * @hide
326      */
makeHostRoute(@onNull InetAddress host, @Nullable String iface)327     public static RouteInfo makeHostRoute(@NonNull InetAddress host, @Nullable String iface) {
328         return makeHostRoute(host, null, iface);
329     }
330 
331     /**
332      * @hide
333      */
makeHostRoute(@ullable InetAddress host, @Nullable InetAddress gateway, @Nullable String iface)334     public static RouteInfo makeHostRoute(@Nullable InetAddress host, @Nullable InetAddress gateway,
335             @Nullable String iface) {
336         if (host == null) return null;
337 
338         if (host instanceof Inet4Address) {
339             return new RouteInfo(new IpPrefix(host, 32), gateway, iface);
340         } else {
341             return new RouteInfo(new IpPrefix(host, 128), gateway, iface);
342         }
343     }
344 
345     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
isHost()346     private boolean isHost() {
347         return (mDestination.getAddress() instanceof Inet4Address &&
348                 mDestination.getPrefixLength() == 32) ||
349                (mDestination.getAddress() instanceof Inet6Address &&
350                 mDestination.getPrefixLength() == 128);
351     }
352 
353     /**
354      * Retrieves the destination address and prefix length in the form of an {@link IpPrefix}.
355      *
356      * @return {@link IpPrefix} specifying the destination.  This is never {@code null}.
357      */
358     @NonNull
getDestination()359     public IpPrefix getDestination() {
360         return mDestination;
361     }
362 
363     /**
364      * TODO: Convert callers to use IpPrefix and then remove.
365      * @hide
366      */
367     @NonNull
getDestinationLinkAddress()368     public LinkAddress getDestinationLinkAddress() {
369         return new LinkAddress(mDestination.getAddress(), mDestination.getPrefixLength());
370     }
371 
372     /**
373      * Retrieves the gateway or next hop {@link InetAddress} for this route.
374      *
375      * @return {@link InetAddress} specifying the gateway or next hop.  This may be
376      *                             {@code null} for a directly-connected route."
377      */
378     @Nullable
getGateway()379     public InetAddress getGateway() {
380         return mGateway;
381     }
382 
383     /**
384      * Retrieves the interface used for this route if specified, else {@code null}.
385      *
386      * @return The name of the interface used for this route.
387      */
388     @Nullable
getInterface()389     public String getInterface() {
390         return mInterface;
391     }
392 
393     /**
394      * Retrieves the type of this route.
395      *
396      * @return The type of this route; one of the {@code RTN_xxx} constants defined in this class.
397      *
398      * @hide
399      */
400     @TestApi
401     @SystemApi
402     @RouteType
getType()403     public int getType() {
404         return mType;
405     }
406 
407     /**
408      * Retrieves the MTU size for this route.
409      *
410      * @return The MTU size, or 0 if it has not been set.
411      * @hide
412      */
413     @SystemApi
getMtu()414     public int getMtu() {
415         return mMtu;
416     }
417 
418     /**
419      * Indicates if this route is a default route (ie, has no destination specified).
420      *
421      * @return {@code true} if the destination has a prefix length of 0.
422      */
isDefaultRoute()423     public boolean isDefaultRoute() {
424         return mType == RTN_UNICAST && mDestination.getPrefixLength() == 0;
425     }
426 
427     /**
428      * Indicates if this route is an unreachable default route.
429      *
430      * @return {@code true} if it's an unreachable route with prefix length of 0.
431      * @hide
432      */
isUnreachableDefaultRoute()433     private boolean isUnreachableDefaultRoute() {
434         return mType == RTN_UNREACHABLE && mDestination.getPrefixLength() == 0;
435     }
436 
437     /**
438      * Indicates if this route is an IPv4 default route.
439      * @hide
440      */
isIPv4Default()441     public boolean isIPv4Default() {
442         return isDefaultRoute() && mDestination.getAddress() instanceof Inet4Address;
443     }
444 
445     /**
446      * Indicates if this route is an IPv4 unreachable default route.
447      * @hide
448      */
isIPv4UnreachableDefault()449     public boolean isIPv4UnreachableDefault() {
450         return isUnreachableDefaultRoute() && mDestination.getAddress() instanceof Inet4Address;
451     }
452 
453     /**
454      * Indicates if this route is an IPv6 default route.
455      * @hide
456      */
isIPv6Default()457     public boolean isIPv6Default() {
458         return isDefaultRoute() && mDestination.getAddress() instanceof Inet6Address;
459     }
460 
461     /**
462      * Indicates if this route is an IPv6 unreachable default route.
463      * @hide
464      */
isIPv6UnreachableDefault()465     public boolean isIPv6UnreachableDefault() {
466         return isUnreachableDefaultRoute() && mDestination.getAddress() instanceof Inet6Address;
467     }
468 
469     /**
470      * Indicates if this route is a host route (ie, matches only a single host address).
471      *
472      * @return {@code true} if the destination has a prefix length of 32 or 128 for IPv4 or IPv6,
473      * respectively.
474      * @hide
475      */
isHostRoute()476     public boolean isHostRoute() {
477         return mIsHost;
478     }
479 
480     /**
481      * Indicates if this route has a next hop ({@code true}) or is directly-connected
482      * ({@code false}).
483      *
484      * @return {@code true} if a gateway is specified
485      */
hasGateway()486     public boolean hasGateway() {
487         return mHasGateway;
488     }
489 
490     /**
491      * Determines whether the destination and prefix of this route includes the specified
492      * address.
493      *
494      * @param destination A {@link InetAddress} to test to see if it would match this route.
495      * @return {@code true} if the destination and prefix length cover the given address.
496      */
matches(InetAddress destination)497     public boolean matches(InetAddress destination) {
498         return mDestination.contains(destination);
499     }
500 
501     /**
502      * Find the route from a Collection of routes that best matches a given address.
503      * May return null if no routes are applicable.
504      * @param routes a Collection of RouteInfos to chose from
505      * @param dest the InetAddress your trying to get to
506      * @return the RouteInfo from the Collection that best fits the given address
507      *
508      * @hide
509      */
510     @UnsupportedAppUsage
511     @Nullable
selectBestRoute(Collection<RouteInfo> routes, InetAddress dest)512     public static RouteInfo selectBestRoute(Collection<RouteInfo> routes, InetAddress dest) {
513         return NetUtils.selectBestRoute(routes, dest);
514     }
515 
516     /**
517      * Returns a human-readable description of this object.
518      */
toString()519     public String toString() {
520         String val = "";
521         if (mDestination != null) val = mDestination.toString();
522         if (mType == RTN_UNREACHABLE) {
523             val += " unreachable";
524         } else if (mType == RTN_THROW) {
525             val += " throw";
526         } else {
527             val += " ->";
528             if (mGateway != null) val += " " + mGateway.getHostAddress();
529             if (mInterface != null) val += " " + mInterface;
530             if (mType != RTN_UNICAST) {
531                 val += " unknown type " + mType;
532             }
533         }
534         val += " mtu " + mMtu;
535         return val;
536     }
537 
538     /**
539      * Compares this RouteInfo object against the specified object and indicates if they are equal.
540      * @return {@code true} if the objects are equal, {@code false} otherwise.
541      */
equals(Object obj)542     public boolean equals(Object obj) {
543         if (this == obj) return true;
544 
545         if (!(obj instanceof RouteInfo)) return false;
546 
547         RouteInfo target = (RouteInfo) obj;
548 
549         return Objects.equals(mDestination, target.getDestination()) &&
550                 Objects.equals(mGateway, target.getGateway()) &&
551                 Objects.equals(mInterface, target.getInterface()) &&
552                 mType == target.getType() && mMtu == target.getMtu();
553     }
554 
555     /**
556      * A helper class that contains the destination, the gateway and the interface in a
557      * {@code RouteInfo}, used by {@link ConnectivityService#updateRoutes} or
558      * {@link LinkProperties#addRoute} to calculate the list to be updated.
559      * {@code RouteInfo} objects with different interfaces are treated as different routes because
560      * *usually* on Android different interfaces use different routing tables, and moving a route
561      * to a new routing table never constitutes an update, but is always a remove and an add.
562      *
563      * @hide
564      */
565     public static class RouteKey {
566         @NonNull private final IpPrefix mDestination;
567         @Nullable private final InetAddress mGateway;
568         @Nullable private final String mInterface;
569 
RouteKey(@onNull IpPrefix destination, @Nullable InetAddress gateway, @Nullable String iface)570         RouteKey(@NonNull IpPrefix destination, @Nullable InetAddress gateway,
571                 @Nullable String iface) {
572             mDestination = destination;
573             mGateway = gateway;
574             mInterface = iface;
575         }
576 
577         @Override
equals(Object o)578         public boolean equals(Object o) {
579             if (!(o instanceof RouteKey)) {
580                 return false;
581             }
582             RouteKey p = (RouteKey) o;
583             // No need to do anything special for scoped addresses. Inet6Address#equals does not
584             // consider the scope ID, but the netd route IPCs (e.g., INetd#networkAddRouteParcel)
585             // and the kernel ignore scoped addresses both in the prefix and in the nexthop and only
586             // look at RTA_OIF.
587             return Objects.equals(p.mDestination, mDestination)
588                     && Objects.equals(p.mGateway, mGateway)
589                     && Objects.equals(p.mInterface, mInterface);
590         }
591 
592         @Override
hashCode()593         public int hashCode() {
594             return Objects.hash(mDestination, mGateway, mInterface);
595         }
596     }
597 
598     /**
599      * Get {@code RouteKey} of this {@code RouteInfo}.
600      * @return a {@code RouteKey} object.
601      *
602      * @hide
603      */
604     @NonNull
getRouteKey()605     public RouteKey getRouteKey() {
606         return new RouteKey(mDestination, mGateway, mInterface);
607     }
608 
609     /**
610      *  Returns a hashcode for this <code>RouteInfo</code> object.
611      */
hashCode()612     public int hashCode() {
613         return (mDestination.hashCode() * 41)
614                 + (mGateway == null ? 0 :mGateway.hashCode() * 47)
615                 + (mInterface == null ? 0 :mInterface.hashCode() * 67)
616                 + (mType * 71) + (mMtu * 89);
617     }
618 
619     /**
620      * Implement the Parcelable interface
621      */
describeContents()622     public int describeContents() {
623         return 0;
624     }
625 
626     /**
627      * Implement the Parcelable interface
628      */
writeToParcel(Parcel dest, int flags)629     public void writeToParcel(Parcel dest, int flags) {
630         dest.writeParcelable(mDestination, flags);
631         byte[] gatewayBytes = (mGateway == null) ? null : mGateway.getAddress();
632         dest.writeByteArray(gatewayBytes);
633         dest.writeString(mInterface);
634         dest.writeInt(mType);
635         dest.writeInt(mMtu);
636     }
637 
638     /**
639      * Implement the Parcelable interface.
640      */
641     public static final @android.annotation.NonNull Creator<RouteInfo> CREATOR =
642         new Creator<RouteInfo>() {
643         public RouteInfo createFromParcel(Parcel in) {
644             IpPrefix dest = in.readParcelable(null);
645 
646             InetAddress gateway = null;
647             byte[] addr = in.createByteArray();
648             try {
649                 gateway = InetAddress.getByAddress(addr);
650             } catch (UnknownHostException e) {}
651 
652             String iface = in.readString();
653             int type = in.readInt();
654             int mtu = in.readInt();
655 
656             return new RouteInfo(dest, gateway, iface, type, mtu);
657         }
658 
659         public RouteInfo[] newArray(int size) {
660             return new RouteInfo[size];
661         }
662     };
663 }
664