1 /* 2 * Copyright (C) 2019 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.ipsec.ike; 18 19 import android.annotation.IntDef; 20 import android.annotation.NonNull; 21 import android.net.InetAddresses; 22 import android.net.ipsec.ike.exceptions.InvalidSyntaxException; 23 import android.os.PersistableBundle; 24 import android.util.ArraySet; 25 26 import com.android.internal.annotations.VisibleForTesting; 27 28 import java.lang.annotation.Retention; 29 import java.lang.annotation.RetentionPolicy; 30 import java.net.Inet4Address; 31 import java.net.Inet6Address; 32 import java.net.InetAddress; 33 import java.net.UnknownHostException; 34 import java.nio.BufferOverflowException; 35 import java.nio.ByteBuffer; 36 import java.util.Objects; 37 38 /** 39 * IkeTrafficSelector represents a Traffic Selector of a Child Session. 40 * 41 * <p>Traffic Selectors specify addresses that are acceptable within the IPsec SA. 42 * 43 * <p>Callers can propose {@link IkeTrafficSelector}s when building a {@link ChildSessionParams} and 44 * receive the negotiated {@link IkeTrafficSelector}s via a {@link ChildSessionConfiguration}. 45 * 46 * @see <a href="https://tools.ietf.org/html/rfc7296#section-3.13">RFC 7296, Internet Key Exchange 47 * Protocol Version 2 (IKEv2)</a> 48 */ 49 public final class IkeTrafficSelector { 50 51 // IpProtocolId consists of standard IP Protocol IDs. 52 /** @hide */ 53 @Retention(RetentionPolicy.SOURCE) 54 @IntDef({IP_PROTOCOL_ID_UNSPEC, IP_PROTOCOL_ID_ICMP, IP_PROTOCOL_ID_TCP, IP_PROTOCOL_ID_UDP}) 55 public @interface IpProtocolId {} 56 57 // Zero value is re-defined by IKE to indicate that all IP protocols are acceptable. 58 /** @hide */ 59 @VisibleForTesting static final int IP_PROTOCOL_ID_UNSPEC = 0; 60 /** @hide */ 61 @VisibleForTesting static final int IP_PROTOCOL_ID_ICMP = 1; 62 /** @hide */ 63 @VisibleForTesting static final int IP_PROTOCOL_ID_TCP = 6; 64 /** @hide */ 65 @VisibleForTesting static final int IP_PROTOCOL_ID_UDP = 17; 66 67 private static final ArraySet<Integer> IP_PROTOCOL_ID_SET = new ArraySet<>(); 68 69 static { 70 IP_PROTOCOL_ID_SET.add(IP_PROTOCOL_ID_UNSPEC); 71 IP_PROTOCOL_ID_SET.add(IP_PROTOCOL_ID_ICMP); 72 IP_PROTOCOL_ID_SET.add(IP_PROTOCOL_ID_TCP); 73 IP_PROTOCOL_ID_SET.add(IP_PROTOCOL_ID_UDP); 74 } 75 76 /** 77 * TrafficSelectorType consists of IKE standard Traffic Selector Types. 78 * 79 * @see <a 80 * href="https://www.iana.org/assignments/ikev2-parameters/ikev2-parameters.xhtml">Internet 81 * Key Exchange Version 2 (IKEv2) Parameters</a> 82 * @hide 83 */ 84 @Retention(RetentionPolicy.SOURCE) 85 @IntDef({TRAFFIC_SELECTOR_TYPE_IPV4_ADDR_RANGE, TRAFFIC_SELECTOR_TYPE_IPV6_ADDR_RANGE}) 86 public @interface TrafficSelectorType {} 87 88 /** @hide */ 89 public static final int TRAFFIC_SELECTOR_TYPE_IPV4_ADDR_RANGE = 7; 90 /** @hide */ 91 public static final int TRAFFIC_SELECTOR_TYPE_IPV6_ADDR_RANGE = 8; 92 93 /** @hide */ 94 public static final int PORT_NUMBER_MIN = 0; 95 /** @hide */ 96 public static final int PORT_NUMBER_MAX = 65535; 97 98 // TODO: Consider defining these constants in a central place in Connectivity. 99 private static final int IPV4_ADDR_LEN = 4; 100 private static final int IPV6_ADDR_LEN = 16; 101 102 @VisibleForTesting static final int TRAFFIC_SELECTOR_IPV4_LEN = 16; 103 @VisibleForTesting static final int TRAFFIC_SELECTOR_IPV6_LEN = 40; 104 105 private static final String START_PORT_KEY = "startPort"; 106 private static final String END_PORT_KEY = "endPort"; 107 private static final String START_ADDRESS_KEY = "startingAddress"; 108 private static final String END_ADDRESS_KEY = "endingAddress"; 109 110 /** @hide */ 111 public final int tsType; 112 /** @hide */ 113 public final int ipProtocolId; 114 /** @hide */ 115 public final int selectorLength; 116 /** The smallest port number allowed by this Traffic Selector. Informational only. */ 117 public final int startPort; 118 /** The largest port number allowed by this Traffic Selector. Informational only. */ 119 public final int endPort; 120 /** The smallest address included in this Traffic Selector. */ 121 @NonNull public final InetAddress startingAddress; 122 /** The largest address included in this Traffic Selector. */ 123 @NonNull public final InetAddress endingAddress; 124 IkeTrafficSelector( int tsType, int ipProtocolId, int selectorLength, int startPort, int endPort, InetAddress startingAddress, InetAddress endingAddress)125 private IkeTrafficSelector( 126 int tsType, 127 int ipProtocolId, 128 int selectorLength, 129 int startPort, 130 int endPort, 131 InetAddress startingAddress, 132 InetAddress endingAddress) { 133 this.tsType = tsType; 134 this.ipProtocolId = ipProtocolId; 135 this.selectorLength = selectorLength; 136 137 // Ports & Addresses previously validated in decodeIkeTrafficSelectors() 138 this.startPort = startPort; 139 this.endPort = endPort; 140 this.startingAddress = startingAddress; 141 this.endingAddress = endingAddress; 142 } 143 144 /** 145 * Construct an instance of {@link IkeTrafficSelector} for negotiating a Child Session. 146 * 147 * <p>Android platform does not support port-based routing. The port range negotiation is only 148 * informational. 149 * 150 * @param startPort the smallest port number allowed by this Traffic Selector. 151 * @param endPort the largest port number allowed by this Traffic Selector. 152 * @param startingAddress the smallest address included in this Traffic Selector. 153 * @param endingAddress the largest address included in this Traffic Selector. 154 */ IkeTrafficSelector( int startPort, int endPort, @NonNull InetAddress startingAddress, @NonNull InetAddress endingAddress)155 public IkeTrafficSelector( 156 int startPort, 157 int endPort, 158 @NonNull InetAddress startingAddress, 159 @NonNull InetAddress endingAddress) { 160 this(getTsType(startingAddress), startPort, endPort, startingAddress, endingAddress); 161 } 162 getTsType(InetAddress address)163 private static int getTsType(InetAddress address) { 164 if (address instanceof Inet4Address) { 165 return TRAFFIC_SELECTOR_TYPE_IPV4_ADDR_RANGE; 166 } 167 return TRAFFIC_SELECTOR_TYPE_IPV6_ADDR_RANGE; 168 } 169 170 /** 171 * Construct an instance of IkeTrafficSelector for building an outbound IKE message. 172 * 173 * @param tsType the Traffic Selector type. 174 * @param startPort the smallest port number allowed by this Traffic Selector. 175 * @param endPort the largest port number allowed by this Traffic Selector. 176 * @param startingAddress the smallest address included in this Traffic Selector. 177 * @param endingAddress the largest address included in this Traffic Selector. 178 * @hide 179 */ IkeTrafficSelector( @rafficSelectorType int tsType, int startPort, int endPort, InetAddress startingAddress, InetAddress endingAddress)180 public IkeTrafficSelector( 181 @TrafficSelectorType int tsType, 182 int startPort, 183 int endPort, 184 InetAddress startingAddress, 185 InetAddress endingAddress) { 186 187 this.tsType = tsType; 188 this.ipProtocolId = IP_PROTOCOL_ID_UNSPEC; 189 190 switch (tsType) { 191 case TRAFFIC_SELECTOR_TYPE_IPV4_ADDR_RANGE: 192 this.selectorLength = TRAFFIC_SELECTOR_IPV4_LEN; 193 194 if (!(startingAddress instanceof Inet4Address) 195 || !(endingAddress instanceof Inet4Address)) { 196 throw new IllegalArgumentException( 197 "Invalid address range: TS_IPV4_ADDR_RANGE requires IPv4 addresses."); 198 } 199 200 break; 201 case TRAFFIC_SELECTOR_TYPE_IPV6_ADDR_RANGE: 202 this.selectorLength = TRAFFIC_SELECTOR_IPV6_LEN; 203 204 if (!(startingAddress instanceof Inet6Address) 205 || !(endingAddress instanceof Inet6Address)) { 206 throw new IllegalArgumentException( 207 "Invalid address range: TS_IPV6_ADDR_RANGE requires IPv6 addresses."); 208 } 209 break; 210 default: 211 throw new IllegalArgumentException("Unrecognized Traffic Selector type."); 212 } 213 214 if (compareInetAddressTo(startingAddress, endingAddress) > 0) { 215 throw new IllegalArgumentException("Received invalid address range."); 216 } 217 218 if (!isPortRangeValid(startPort, endPort)) { 219 throw new IllegalArgumentException( 220 "Invalid port range. startPort: " + startPort + " endPort: " + endPort); 221 } 222 223 this.startPort = startPort; 224 this.endPort = endPort; 225 this.startingAddress = startingAddress; 226 this.endingAddress = endingAddress; 227 } 228 229 /** 230 * Constructs this object by deserializing a PersistableBundle 231 * 232 * @hide 233 */ 234 @NonNull fromPersistableBundle(@onNull PersistableBundle in)235 public static IkeTrafficSelector fromPersistableBundle(@NonNull PersistableBundle in) { 236 Objects.requireNonNull(in, "PersistableBundle not provided"); 237 238 int startPort = in.getInt(START_PORT_KEY); 239 int endPort = in.getInt(END_PORT_KEY); 240 241 InetAddress startingAddress = 242 InetAddresses.parseNumericAddress(in.getString(START_ADDRESS_KEY)); 243 Objects.requireNonNull(in, "startAddress not provided"); 244 InetAddress endingAddress = 245 InetAddresses.parseNumericAddress(in.getString(END_ADDRESS_KEY)); 246 Objects.requireNonNull(in, "endAddress not provided"); 247 248 return new IkeTrafficSelector(startPort, endPort, startingAddress, endingAddress); 249 } 250 251 /** 252 * Serializes this object to a PersistableBundle 253 * 254 * @hide 255 */ 256 @NonNull toPersistableBundle()257 public PersistableBundle toPersistableBundle() { 258 final PersistableBundle result = new PersistableBundle(); 259 260 result.putInt(START_PORT_KEY, startPort); 261 result.putInt(END_PORT_KEY, endPort); 262 result.putString(START_ADDRESS_KEY, startingAddress.getHostAddress()); 263 result.putString(END_ADDRESS_KEY, endingAddress.getHostAddress()); 264 265 return result; 266 } 267 268 /** 269 * Decode IkeTrafficSelectors from inbound Traffic Selector Payload. 270 * 271 * <p>This method is only called by IkeTsPayload when decoding inbound IKE message. 272 * 273 * @param numTs number or Traffic Selectors 274 * @param tsBytes encoded byte array of Traffic Selectors 275 * @return an array of decoded IkeTrafficSelectors 276 * @throws InvalidSyntaxException if received bytes are malformed. 277 * @hide 278 */ decodeIkeTrafficSelectors(int numTs, byte[] tsBytes)279 public static IkeTrafficSelector[] decodeIkeTrafficSelectors(int numTs, byte[] tsBytes) 280 throws InvalidSyntaxException { 281 IkeTrafficSelector[] tsArray = new IkeTrafficSelector[numTs]; 282 ByteBuffer inputBuffer = ByteBuffer.wrap(tsBytes); 283 284 try { 285 for (int i = 0; i < numTs; i++) { 286 int tsType = Byte.toUnsignedInt(inputBuffer.get()); 287 switch (tsType) { 288 case TRAFFIC_SELECTOR_TYPE_IPV4_ADDR_RANGE: 289 tsArray[i] = decodeTrafficSelector(inputBuffer, 290 TRAFFIC_SELECTOR_TYPE_IPV4_ADDR_RANGE); 291 break; 292 case TRAFFIC_SELECTOR_TYPE_IPV6_ADDR_RANGE: 293 tsArray[i] = decodeTrafficSelector(inputBuffer, 294 TRAFFIC_SELECTOR_TYPE_IPV6_ADDR_RANGE); 295 break; 296 default: 297 throw new InvalidSyntaxException( 298 "Invalid Traffic Selector type: " + tsType); 299 } 300 } 301 } catch (BufferOverflowException e) { 302 // Throw exception if any Traffic Selector has invalid length. 303 throw new InvalidSyntaxException(e); 304 } 305 306 if (inputBuffer.remaining() != 0) { 307 throw new InvalidSyntaxException( 308 "Unexpected trailing characters of Traffic Selectors."); 309 } 310 311 return tsArray; 312 } 313 314 // Decode Traffic Selector from a ByteBuffer. A BufferOverflowException will be thrown and 315 // caught by method caller if operation reaches the input ByteBuffer's limit. decodeTrafficSelector(ByteBuffer inputBuffer, int tsType)316 private static IkeTrafficSelector decodeTrafficSelector(ByteBuffer inputBuffer, int tsType) 317 throws InvalidSyntaxException { 318 // Decode and validate IP Protocol ID 319 int ipProtocolId = Byte.toUnsignedInt(inputBuffer.get()); 320 if (!IP_PROTOCOL_ID_SET.contains(ipProtocolId)) { 321 throw new InvalidSyntaxException("Invalid IP Protocol ID."); 322 } 323 324 // Decode and validate Selector Length 325 boolean isTsIpv4 = tsType == TRAFFIC_SELECTOR_TYPE_IPV4_ADDR_RANGE; 326 int expectedTsLen = isTsIpv4 ? TRAFFIC_SELECTOR_IPV4_LEN : TRAFFIC_SELECTOR_IPV6_LEN; 327 int tsLength = Short.toUnsignedInt(inputBuffer.getShort()); 328 if (expectedTsLen != tsLength) { 329 throw new InvalidSyntaxException("Invalid Traffic Selector Length."); 330 } 331 332 // Decode and validate ports 333 int startPort = Short.toUnsignedInt(inputBuffer.getShort()); 334 int endPort = Short.toUnsignedInt(inputBuffer.getShort()); 335 if (!isPortRangeValid(startPort, endPort)) { 336 throw new InvalidSyntaxException( 337 "Received invalid port range. startPort: " 338 + startPort 339 + " endPort: " 340 + endPort); 341 } 342 343 // Decode and validate IP addresses 344 int expectedAddrLen = isTsIpv4 ? IPV4_ADDR_LEN : IPV6_ADDR_LEN; 345 byte[] startAddressBytes = new byte[expectedAddrLen]; 346 byte[] endAddressBytes = new byte[expectedAddrLen]; 347 inputBuffer.get(startAddressBytes); 348 inputBuffer.get(endAddressBytes); 349 try { 350 InetAddress startAddress = InetAddress.getByAddress(startAddressBytes); 351 InetAddress endAddress = InetAddress.getByAddress(endAddressBytes); 352 353 boolean isStartAddrIpv4 = startAddress instanceof Inet4Address; 354 boolean isEndAddrIpv4 = endAddress instanceof Inet4Address; 355 if (isTsIpv4 != isStartAddrIpv4 || isTsIpv4 != isEndAddrIpv4) { 356 throw new InvalidSyntaxException("Invalid IP address family"); 357 } 358 359 // Validate address range. 360 if (compareInetAddressTo(startAddress, endAddress) > 0) { 361 throw new InvalidSyntaxException("Received invalid IP address range."); 362 } 363 364 return new IkeTrafficSelector( 365 tsType, 366 ipProtocolId, 367 expectedTsLen, 368 startPort, 369 endPort, 370 startAddress, 371 endAddress); 372 } catch (ClassCastException | UnknownHostException | IllegalArgumentException e) { 373 throw new InvalidSyntaxException(e); 374 } 375 } 376 377 // Validate port range. isPortRangeValid(int startPort, int endPort)378 private static boolean isPortRangeValid(int startPort, int endPort) { 379 return (startPort >= PORT_NUMBER_MIN 380 && startPort <= PORT_NUMBER_MAX 381 && endPort >= PORT_NUMBER_MIN 382 && endPort <= PORT_NUMBER_MAX 383 && startPort <= endPort); 384 } 385 386 // Compare two InetAddresses. Return -1 if the first input is smaller; 1 if the second input is 387 // smaller; 0 if two addresses are equal. 388 // TODO: Consider moving it to the platform code in the future./ compareInetAddressTo(InetAddress leftAddress, InetAddress rightAddress)389 private static int compareInetAddressTo(InetAddress leftAddress, InetAddress rightAddress) { 390 byte[] leftAddrBytes = leftAddress.getAddress(); 391 byte[] rightAddrBytes = rightAddress.getAddress(); 392 393 if (leftAddrBytes.length != rightAddrBytes.length) { 394 throw new IllegalArgumentException("Two addresses are different types."); 395 } 396 397 for (int i = 0; i < leftAddrBytes.length; i++) { 398 int unsignedByteLeft = Byte.toUnsignedInt(leftAddrBytes[i]); 399 int unsignedByteRight = Byte.toUnsignedInt(rightAddrBytes[i]); 400 401 int result = Integer.compare(unsignedByteLeft, unsignedByteRight); 402 if (result != 0) return result; 403 } 404 return 0; 405 } 406 407 /** 408 * Check if the input IkeTrafficSelector is a subset of this instance. 409 * 410 * @param ts the provided IkeTrafficSelector to check. 411 * @return true if the input IkeTrafficSelector is a subset of this instance, otherwise false. 412 * @hide 413 */ contains(IkeTrafficSelector ts)414 public boolean contains(IkeTrafficSelector ts) { 415 if (tsType == ts.tsType 416 && ipProtocolId == ts.ipProtocolId 417 && startPort <= ts.startPort 418 && endPort >= ts.endPort 419 && compareInetAddressTo(startingAddress, ts.startingAddress) <= 0 420 && compareInetAddressTo(endingAddress, ts.endingAddress) >= 0) { 421 return true; 422 } 423 return false; 424 } 425 426 /** @hide */ 427 @Override hashCode()428 public int hashCode() { 429 return Objects.hash( 430 tsType, 431 ipProtocolId, 432 selectorLength, 433 startPort, 434 endPort, 435 startingAddress, 436 endingAddress); 437 } 438 439 /** @hide */ 440 @Override equals(Object o)441 public boolean equals(Object o) { 442 if (!(o instanceof IkeTrafficSelector)) return false; 443 444 IkeTrafficSelector other = (IkeTrafficSelector) o; 445 446 if (tsType != other.tsType 447 || ipProtocolId != other.ipProtocolId 448 || startPort != other.startPort 449 || endPort != other.endPort) { 450 return false; 451 } 452 453 switch (tsType) { 454 case TRAFFIC_SELECTOR_TYPE_IPV4_ADDR_RANGE: 455 return (((Inet4Address) startingAddress) 456 .equals((Inet4Address) other.startingAddress) 457 && ((Inet4Address) endingAddress) 458 .equals((Inet4Address) other.endingAddress)); 459 case TRAFFIC_SELECTOR_TYPE_IPV6_ADDR_RANGE: 460 return (((Inet6Address) startingAddress) 461 .equals((Inet6Address) other.startingAddress) 462 && ((Inet6Address) endingAddress) 463 .equals((Inet6Address) other.endingAddress)); 464 default: 465 throw new UnsupportedOperationException("Unrecognized TS type"); 466 } 467 } 468 469 /** 470 * Encode traffic selector to ByteBuffer. 471 * 472 * <p>This method will be only called by IkeTsPayload for building an outbound IKE message. 473 * 474 * @param byteBuffer destination ByteBuffer that stores encoded traffic selector. 475 * @hide 476 */ encodeToByteBuffer(ByteBuffer byteBuffer)477 public void encodeToByteBuffer(ByteBuffer byteBuffer) { 478 byteBuffer 479 .put((byte) tsType) 480 .put((byte) ipProtocolId) 481 .putShort((short) selectorLength) 482 .putShort((short) startPort) 483 .putShort((short) endPort) 484 .put(startingAddress.getAddress()) 485 .put(endingAddress.getAddress()); 486 } 487 } 488