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