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