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