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