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 29 /** 30 * A simple container for route information. 31 * 32 * In order to be used, a route must have a destination prefix and: 33 * 34 * - A gateway address (next-hop, for gatewayed routes), or 35 * - An interface (for directly-connected routes), or 36 * - Both a gateway and an interface. 37 * 38 * This class does not enforce these constraints because there is code that 39 * uses RouteInfo objects to store directly-connected routes without interfaces. 40 * Such objects cannot be used directly, but can be put into a LinkProperties 41 * object which then specifies the interface. 42 * 43 * @hide 44 */ 45 public class RouteInfo implements Parcelable { 46 /** 47 * The IP destination address for this route. 48 */ 49 private final LinkAddress mDestination; 50 51 /** 52 * The gateway address for this route. 53 */ 54 private final InetAddress mGateway; 55 56 /** 57 * The interface for this route. 58 */ 59 private final String mInterface; 60 61 private final boolean mIsDefault; 62 private final boolean mIsHost; 63 private final boolean mHasGateway; 64 65 /** 66 * Constructs a RouteInfo object. 67 * 68 * If destination is null, then gateway must be specified and the 69 * constructed route is either the IPv4 default route <code>0.0.0.0</code> 70 * if @gateway is an instance of {@link Inet4Address}, or the IPv6 default 71 * route <code>::/0</code> if gateway is an instance of 72 * {@link Inet6Address}. 73 * 74 * destination and gateway may not both be null. 75 * 76 * @param destination the destination prefix 77 * @param gateway the IP address to route packets through 78 * @param iface the interface name to send packets on 79 */ 80 public RouteInfo(LinkAddress destination, InetAddress gateway, String iface) { 81 if (destination == null) { 82 if (gateway != null) { 83 if (gateway instanceof Inet4Address) { 84 destination = new LinkAddress(Inet4Address.ANY, 0); 85 } else { 86 destination = new LinkAddress(Inet6Address.ANY, 0); 87 } 88 } else { 89 // no destination, no gateway. invalid. 90 throw new IllegalArgumentException("Invalid arguments passed in: " + gateway + "," + 91 destination); 92 } 93 } 94 if (gateway == null) { 95 if (destination.getAddress() instanceof Inet4Address) { 96 gateway = Inet4Address.ANY; 97 } else { 98 gateway = Inet6Address.ANY; 99 } 100 } 101 mHasGateway = (!gateway.isAnyLocalAddress()); 102 103 mDestination = new LinkAddress(NetworkUtils.getNetworkPart(destination.getAddress(), 104 destination.getNetworkPrefixLength()), destination.getNetworkPrefixLength()); 105 mGateway = gateway; 106 mInterface = iface; 107 mIsDefault = isDefault(); 108 mIsHost = isHost(); 109 } 110 111 public RouteInfo(LinkAddress destination, InetAddress gateway) { 112 this(destination, gateway, null); 113 } 114 115 public RouteInfo(InetAddress gateway) { 116 this(null, gateway, null); 117 } 118 119 public RouteInfo(LinkAddress host) { 120 this(host, null, null); 121 } 122 123 public static RouteInfo makeHostRoute(InetAddress host, String iface) { 124 return makeHostRoute(host, null, iface); 125 } 126 127 public static RouteInfo makeHostRoute(InetAddress host, InetAddress gateway, String iface) { 128 if (host == null) return null; 129 130 if (host instanceof Inet4Address) { 131 return new RouteInfo(new LinkAddress(host, 32), gateway, iface); 132 } else { 133 return new RouteInfo(new LinkAddress(host, 128), gateway, iface); 134 } 135 } 136 137 private boolean isHost() { 138 return (mDestination.getAddress() instanceof Inet4Address && 139 mDestination.getNetworkPrefixLength() == 32) || 140 (mDestination.getAddress() instanceof Inet6Address && 141 mDestination.getNetworkPrefixLength() == 128); 142 } 143 144 private boolean isDefault() { 145 boolean val = false; 146 if (mGateway != null) { 147 if (mGateway instanceof Inet4Address) { 148 val = (mDestination == null || mDestination.getNetworkPrefixLength() == 0); 149 } else { 150 val = (mDestination == null || mDestination.getNetworkPrefixLength() == 0); 151 } 152 } 153 return val; 154 } 155 156 157 public LinkAddress getDestination() { 158 return mDestination; 159 } 160 161 public InetAddress getGateway() { 162 return mGateway; 163 } 164 165 public String getInterface() { 166 return mInterface; 167 } 168 169 public boolean isDefaultRoute() { 170 return mIsDefault; 171 } 172 173 public boolean isHostRoute() { 174 return mIsHost; 175 } 176 177 public boolean hasGateway() { 178 return mHasGateway; 179 } 180 181 public String toString() { 182 String val = ""; 183 if (mDestination != null) val = mDestination.toString(); 184 if (mGateway != null) val += " -> " + mGateway.getHostAddress(); 185 return val; 186 } 187 188 public int describeContents() { 189 return 0; 190 } 191 192 public void writeToParcel(Parcel dest, int flags) { 193 if (mDestination == null) { 194 dest.writeByte((byte) 0); 195 } else { 196 dest.writeByte((byte) 1); 197 dest.writeByteArray(mDestination.getAddress().getAddress()); 198 dest.writeInt(mDestination.getNetworkPrefixLength()); 199 } 200 201 if (mGateway == null) { 202 dest.writeByte((byte) 0); 203 } else { 204 dest.writeByte((byte) 1); 205 dest.writeByteArray(mGateway.getAddress()); 206 } 207 208 dest.writeString(mInterface); 209 } 210 211 @Override 212 public boolean equals(Object obj) { 213 if (this == obj) return true; 214 215 if (!(obj instanceof RouteInfo)) return false; 216 217 RouteInfo target = (RouteInfo) obj; 218 219 boolean sameDestination = ( mDestination == null) ? 220 target.getDestination() == null 221 : mDestination.equals(target.getDestination()); 222 223 boolean sameAddress = (mGateway == null) ? 224 target.getGateway() == null 225 : mGateway.equals(target.getGateway()); 226 227 boolean sameInterface = (mInterface == null) ? 228 target.getInterface() == null 229 : mInterface.equals(target.getInterface()); 230 231 return sameDestination && sameAddress && sameInterface 232 && mIsDefault == target.mIsDefault; 233 } 234 235 @Override 236 public int hashCode() { 237 return (mDestination == null ? 0 : mDestination.hashCode() * 41) 238 + (mGateway == null ? 0 :mGateway.hashCode() * 47) 239 + (mInterface == null ? 0 :mInterface.hashCode() * 67) 240 + (mIsDefault ? 3 : 7); 241 } 242 243 public static final Creator<RouteInfo> CREATOR = 244 new Creator<RouteInfo>() { 245 public RouteInfo createFromParcel(Parcel in) { 246 InetAddress destAddr = null; 247 int prefix = 0; 248 InetAddress gateway = null; 249 250 if (in.readByte() == 1) { 251 byte[] addr = in.createByteArray(); 252 prefix = in.readInt(); 253 254 try { 255 destAddr = InetAddress.getByAddress(addr); 256 } catch (UnknownHostException e) {} 257 } 258 259 if (in.readByte() == 1) { 260 byte[] addr = in.createByteArray(); 261 262 try { 263 gateway = InetAddress.getByAddress(addr); 264 } catch (UnknownHostException e) {} 265 } 266 267 String iface = in.readString(); 268 269 LinkAddress dest = null; 270 271 if (destAddr != null) { 272 dest = new LinkAddress(destAddr, prefix); 273 } 274 275 return new RouteInfo(dest, gateway, iface); 276 } 277 278 public RouteInfo[] newArray(int size) { 279 return new RouteInfo[size]; 280 } 281 }; 282 283 protected boolean matches(InetAddress destination) { 284 if (destination == null) return false; 285 286 // match the route destination and destination with prefix length 287 InetAddress dstNet = NetworkUtils.getNetworkPart(destination, 288 mDestination.getNetworkPrefixLength()); 289 290 return mDestination.getAddress().equals(dstNet); 291 } 292 293 /** 294 * Find the route from a Collection of routes that best matches a given address. 295 * May return null if no routes are applicable. 296 * @param routes a Collection of RouteInfos to chose from 297 * @param dest the InetAddress your trying to get to 298 * @return the RouteInfo from the Collection that best fits the given address 299 */ 300 public static RouteInfo selectBestRoute(Collection<RouteInfo> routes, InetAddress dest) { 301 if ((routes == null) || (dest == null)) return null; 302 303 RouteInfo bestRoute = null; 304 // pick a longest prefix match under same address type 305 for (RouteInfo route : routes) { 306 if (NetworkUtils.addressTypeMatches(route.mDestination.getAddress(), dest)) { 307 if ((bestRoute != null) && 308 (bestRoute.mDestination.getNetworkPrefixLength() >= 309 route.mDestination.getNetworkPrefixLength())) { 310 continue; 311 } 312 if (route.matches(dest)) bestRoute = route; 313 } 314 } 315 return bestRoute; 316 } 317 } 318