• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2020 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 package android.ipsec.ike.cts;
17 
18 import static android.ipsec.ike.cts.PacketUtils.BytePayload;
19 import static android.ipsec.ike.cts.PacketUtils.IP4_HDRLEN;
20 import static android.ipsec.ike.cts.PacketUtils.IP6_HDRLEN;
21 import static android.ipsec.ike.cts.PacketUtils.Ip4Header;
22 import static android.ipsec.ike.cts.PacketUtils.Ip6Header;
23 import static android.ipsec.ike.cts.PacketUtils.IpHeader;
24 import static android.ipsec.ike.cts.PacketUtils.Payload;
25 import static android.ipsec.ike.cts.PacketUtils.UDP_HDRLEN;
26 import static android.ipsec.ike.cts.PacketUtils.UdpHeader;
27 import static android.system.OsConstants.IPPROTO_UDP;
28 
29 import static com.android.internal.util.HexDump.hexStringToByteArray;
30 
31 import static org.junit.Assert.fail;
32 
33 import android.os.ParcelFileDescriptor;
34 
35 import java.net.Inet4Address;
36 import java.net.Inet6Address;
37 import java.net.InetAddress;
38 import java.nio.ByteBuffer;
39 import java.util.ArrayList;
40 import java.util.Arrays;
41 import java.util.List;
42 import java.util.function.Predicate;
43 
44 public class IkeTunUtils extends TunUtils {
45     private static final int PORT_LEN = 2;
46 
47     private static final int NON_ESP_MARKER_LEN = 4;
48     private static final byte[] NON_ESP_MARKER = new byte[NON_ESP_MARKER_LEN];
49 
50     private static final int IKE_INIT_SPI_OFFSET = 0;
51     private static final int IKE_FIRST_PAYLOAD_OFFSET = 16;
52     private static final int IKE_IS_RESP_BYTE_OFFSET = 19;
53     private static final int IKE_MSG_ID_OFFSET = 20;
54     private static final int IKE_HEADER_LEN = 28;
55     private static final int IKE_FRAG_NUM_OFFSET = 32;
56     private static final int IKE_PAYLOAD_TYPE_SKF = 53;
57 
58     private static final int RSP_FLAG_MASK = 0x20;
59 
IkeTunUtils(ParcelFileDescriptor tunFd)60     public IkeTunUtils(ParcelFileDescriptor tunFd) {
61         super(tunFd);
62     }
63 
64     /**
65      * Await the expected IKE request inject an IKE response (or a list of response fragments)
66      *
67      * @param ikeRespDataFragmentsHex IKE response hex (or a list of response fragments) without
68      *     IP/UDP headers or NON ESP MARKER.
69      */
awaitReqAndInjectResp( long expectedInitIkeSpi, int expectedMsgId, boolean expectedUseEncap, String... ikeRespDataFragmentsHex)70     public byte[] awaitReqAndInjectResp(
71             long expectedInitIkeSpi,
72             int expectedMsgId,
73             boolean expectedUseEncap,
74             String... ikeRespDataFragmentsHex)
75             throws Exception {
76         return awaitReqAndInjectResp(
77                         expectedInitIkeSpi,
78                         expectedMsgId,
79                         expectedUseEncap,
80                         1 /* expectedReqPktCnt */,
81                         ikeRespDataFragmentsHex)
82                 .get(0);
83     }
84 
85     /**
86      * Await the expected IKE request (or the list of IKE request fragments) and inject an IKE
87      * response (or a list of response fragments)
88      *
89      * @param ikeRespDataFragmentsHex IKE response hex (or a list of response fragments) without
90      *     IP/UDP headers or NON ESP MARKER.
91      */
awaitReqAndInjectResp( long expectedInitIkeSpi, int expectedMsgId, boolean expectedUseEncap, int expectedReqPktCnt, String... ikeRespDataFragmentsHex)92     public List<byte[]> awaitReqAndInjectResp(
93             long expectedInitIkeSpi,
94             int expectedMsgId,
95             boolean expectedUseEncap,
96             int expectedReqPktCnt,
97             String... ikeRespDataFragmentsHex)
98             throws Exception {
99         List<byte[]> reqList = new ArrayList<>(expectedReqPktCnt);
100         if (expectedReqPktCnt == 1) {
101             // Expecting one complete IKE packet
102             byte[] req =
103                     awaitIkePacket(
104                             (pkt) -> {
105                                 return isExpectedIkePkt(
106                                         pkt,
107                                         expectedInitIkeSpi,
108                                         expectedMsgId,
109                                         false /* expectedResp */,
110                                         expectedUseEncap);
111                             });
112             reqList.add(req);
113         } else {
114             // Expecting "expectedReqPktCnt" number of request fragments
115             for (int i = 0; i < expectedReqPktCnt; i++) {
116                 // IKE Fragment number always starts from 1
117                 int expectedFragNum = i + 1;
118                 byte[] req =
119                         awaitIkePacket(
120                                 (pkt) -> {
121                                     return isExpectedIkeFragPkt(
122                                             pkt,
123                                             expectedInitIkeSpi,
124                                             expectedMsgId,
125                                             false /* expectedResp */,
126                                             expectedUseEncap,
127                                             expectedFragNum);
128                                 });
129                 reqList.add(req);
130             }
131         }
132 
133         // All request fragments have the same addresses and ports
134         byte[] request = reqList.get(0);
135 
136         // Build response header by flipping address and port
137         InetAddress srcAddr = getAddress(request, false /* shouldGetSource */);
138         InetAddress dstAddr = getAddress(request, true /* shouldGetSource */);
139         int srcPort = getPort(request, false /* shouldGetSource */);
140         int dstPort = getPort(request, true /* shouldGetSource */);
141         for (String resp : ikeRespDataFragmentsHex) {
142             byte[] response =
143                     buildIkePacket(
144                             srcAddr,
145                             dstAddr,
146                             srcPort,
147                             dstPort,
148                             expectedUseEncap,
149                             hexStringToByteArray(resp));
150             injectPacket(response);
151         }
152 
153         return reqList;
154     }
155 
156     /** Await the expected IKE response */
awaitResp(long expectedInitIkeSpi, int expectedMsgId, boolean expectedUseEncap)157     public byte[] awaitResp(long expectedInitIkeSpi, int expectedMsgId, boolean expectedUseEncap)
158             throws Exception {
159         return awaitIkePacket(
160                 (pkt) -> {
161                     return isExpectedIkePkt(
162                             pkt,
163                             expectedInitIkeSpi,
164                             expectedMsgId,
165                             true /* expectedResp*/,
166                             expectedUseEncap);
167                 });
168     }
169 
awaitIkePacket(Predicate<byte[]> pktVerifier)170     private byte[] awaitIkePacket(Predicate<byte[]> pktVerifier) throws Exception {
171         long endTime = System.currentTimeMillis() + TIMEOUT;
172         int startIndex = 0;
173         synchronized (mPackets) {
174             while (System.currentTimeMillis() < endTime) {
175                 byte[] ikePkt = getFirstMatchingPacket(pktVerifier, startIndex);
176                 if (ikePkt != null) {
177                     return ikePkt; // We've found the packet we're looking for.
178                 }
179 
180                 startIndex = mPackets.size();
181 
182                 // Try to prevent waiting too long. If waitTimeout <= 0, we've already hit timeout
183                 long waitTimeout = endTime - System.currentTimeMillis();
184                 if (waitTimeout > 0) {
185                     mPackets.wait(waitTimeout);
186                 }
187             }
188 
189             fail("No matching packet found");
190         }
191 
192         throw new IllegalStateException(
193                 "Hit an impossible case where fail() didn't throw an exception");
194     }
195 
isExpectedIkePkt( byte[] pkt, long expectedInitIkeSpi, int expectedMsgId, boolean expectedResp, boolean expectedUseEncap)196     private static boolean isExpectedIkePkt(
197             byte[] pkt,
198             long expectedInitIkeSpi,
199             int expectedMsgId,
200             boolean expectedResp,
201             boolean expectedUseEncap) {
202         int ipProtocolOffset = isIpv6(pkt) ? IP6_PROTO_OFFSET : IP4_PROTO_OFFSET;
203         int ikeOffset = getIkeOffset(pkt, expectedUseEncap);
204 
205         return pkt[ipProtocolOffset] == IPPROTO_UDP
206                 && expectedUseEncap == hasNonEspMarker(pkt)
207                 && isExpectedSpiAndMsgId(
208                         pkt, ikeOffset, expectedInitIkeSpi, expectedMsgId, expectedResp);
209     }
210 
isExpectedIkeFragPkt( byte[] pkt, long expectedInitIkeSpi, int expectedMsgId, boolean expectedResp, boolean expectedUseEncap, int expectedFragNum)211     private static boolean isExpectedIkeFragPkt(
212             byte[] pkt,
213             long expectedInitIkeSpi,
214             int expectedMsgId,
215             boolean expectedResp,
216             boolean expectedUseEncap,
217             int expectedFragNum) {
218         return isExpectedIkePkt(
219                         pkt, expectedInitIkeSpi, expectedMsgId, expectedResp, expectedUseEncap)
220                 && isExpectedFragNum(pkt, getIkeOffset(pkt, expectedUseEncap), expectedFragNum);
221     }
222 
getIkeOffset(byte[] pkt, boolean useEncap)223     private static int getIkeOffset(byte[] pkt, boolean useEncap) {
224         int hdrLen = isIpv6(pkt) ? IP6_HDRLEN : IP4_HDRLEN;
225         int ikeOffset = UDP_HDRLEN + hdrLen;
226 
227         // Port 4500 is used during MOBIKE (and a non-ESP marker is added). This is always done,
228         // regardless of whether the IP address is IPv4 or IPv6
229         return useEncap ? ikeOffset + NON_ESP_MARKER_LEN : ikeOffset;
230     }
231 
hasNonEspMarker(byte[] pkt)232     private static boolean hasNonEspMarker(byte[] pkt) {
233         ByteBuffer buffer = ByteBuffer.wrap(pkt);
234         int hdrLen = isIpv6(pkt) ? IP6_HDRLEN : IP4_HDRLEN;
235         int ikeOffset = UDP_HDRLEN + hdrLen;
236         if (buffer.remaining() < ikeOffset) return false;
237 
238         buffer.get(new byte[ikeOffset]); // Skip IP and UDP header
239         byte[] nonEspMarker = new byte[NON_ESP_MARKER_LEN];
240         if (buffer.remaining() < NON_ESP_MARKER_LEN) return false;
241 
242         buffer.get(nonEspMarker);
243         return Arrays.equals(NON_ESP_MARKER, nonEspMarker);
244     }
245 
isExpectedSpiAndMsgId( byte[] pkt, int ikeOffset, long expectedInitIkeSpi, int expectedMsgId, boolean expectedResp)246     private static boolean isExpectedSpiAndMsgId(
247             byte[] pkt,
248             int ikeOffset,
249             long expectedInitIkeSpi,
250             int expectedMsgId,
251             boolean expectedResp) {
252         if (pkt.length <= ikeOffset + IKE_HEADER_LEN) return false;
253 
254         ByteBuffer buffer = ByteBuffer.wrap(pkt);
255         buffer.get(new byte[ikeOffset]); // Skip IP, UDP header (and NON_ESP_MARKER)
256         buffer.mark(); // Mark this position so that later we can reset back here
257 
258         // Check SPI
259         buffer.get(new byte[IKE_INIT_SPI_OFFSET]);
260         long initSpi = buffer.getLong();
261         if (expectedInitIkeSpi != initSpi) {
262             return false;
263         }
264 
265         // Check direction
266         buffer.reset();
267         buffer.get(new byte[IKE_IS_RESP_BYTE_OFFSET]);
268         byte flagsByte = buffer.get();
269         boolean isResp = ((flagsByte & RSP_FLAG_MASK) != 0);
270         if (expectedResp != isResp) {
271             return false;
272         }
273 
274         // Check message ID
275         buffer.reset();
276         buffer.get(new byte[IKE_MSG_ID_OFFSET]);
277 
278         // Both the expected message ID and the packet's msgId are signed integers, so directly
279         // compare them.
280         int msgId = buffer.getInt();
281         if (expectedMsgId != msgId) {
282             return false;
283         }
284 
285         return true;
286     }
287 
isExpectedFragNum(byte[] pkt, int ikeOffset, int expectedFragNum)288     private static boolean isExpectedFragNum(byte[] pkt, int ikeOffset, int expectedFragNum) {
289         ByteBuffer buffer = ByteBuffer.wrap(pkt);
290         buffer.get(new byte[ikeOffset]);
291         buffer.mark(); // Mark this position so that later we can reset back here
292 
293         // Check if it is a fragment packet
294         buffer.get(new byte[IKE_FIRST_PAYLOAD_OFFSET]);
295         int firstPayload = Byte.toUnsignedInt(buffer.get());
296         if (firstPayload != IKE_PAYLOAD_TYPE_SKF) {
297             return false;
298         }
299 
300         // Check fragment number
301         buffer.reset();
302         buffer.get(new byte[IKE_FRAG_NUM_OFFSET]);
303         int fragNum = Short.toUnsignedInt(buffer.getShort());
304         return expectedFragNum == fragNum;
305     }
306 
307     public static class PortPair {
308         public final int srcPort;
309         public final int dstPort;
310 
PortPair(int sourcePort, int destinationPort)311         public PortPair(int sourcePort, int destinationPort) {
312             srcPort = sourcePort;
313             dstPort = destinationPort;
314         }
315     }
316 
getSrcDestPortPair(byte[] outboundIkePkt)317     public static PortPair getSrcDestPortPair(byte[] outboundIkePkt) throws Exception {
318         return new PortPair(
319                 getPort(outboundIkePkt, true /* shouldGetSource */),
320                 getPort(outboundIkePkt, false /* shouldGetSource */));
321     }
322 
getAddress(byte[] pkt, boolean shouldGetSource)323     private static InetAddress getAddress(byte[] pkt, boolean shouldGetSource) throws Exception {
324         int ipLen = isIpv6(pkt) ? IP6_ADDR_LEN : IP4_ADDR_LEN;
325         int srcIpOffset = isIpv6(pkt) ? IP6_ADDR_OFFSET : IP4_ADDR_OFFSET;
326         int ipOffset = shouldGetSource ? srcIpOffset : srcIpOffset + ipLen;
327 
328         ByteBuffer buffer = ByteBuffer.wrap(pkt);
329         buffer.get(new byte[ipOffset]);
330         byte[] ipAddrBytes = new byte[ipLen];
331         buffer.get(ipAddrBytes);
332         return InetAddress.getByAddress(ipAddrBytes);
333     }
334 
getPort(byte[] pkt, boolean shouldGetSource)335     private static int getPort(byte[] pkt, boolean shouldGetSource) {
336         ByteBuffer buffer = ByteBuffer.wrap(pkt);
337         int srcPortOffset = isIpv6(pkt) ? IP6_HDRLEN : IP4_HDRLEN;
338         int portOffset = shouldGetSource ? srcPortOffset : srcPortOffset + PORT_LEN;
339 
340         buffer.get(new byte[portOffset]);
341         return Short.toUnsignedInt(buffer.getShort());
342     }
343 
buildIkePacket( InetAddress srcAddr, InetAddress dstAddr, int srcPort, int dstPort, boolean useEncap, byte[] ikePacket)344     public static byte[] buildIkePacket(
345             InetAddress srcAddr,
346             InetAddress dstAddr,
347             int srcPort,
348             int dstPort,
349             boolean useEncap,
350             byte[] ikePacket)
351             throws Exception {
352         if (useEncap) {
353             ByteBuffer buffer = ByteBuffer.allocate(NON_ESP_MARKER_LEN + ikePacket.length);
354             buffer.put(NON_ESP_MARKER);
355             buffer.put(ikePacket);
356             ikePacket = buffer.array();
357         }
358 
359         UdpHeader udpPkt = new UdpHeader(srcPort, dstPort, new BytePayload(ikePacket));
360         IpHeader ipPkt = getIpHeader(udpPkt.getProtocolId(), srcAddr, dstAddr, udpPkt);
361         return ipPkt.getPacketBytes();
362     }
363 
getIpHeader( int protocol, InetAddress src, InetAddress dst, Payload payload)364     private static IpHeader getIpHeader(
365             int protocol, InetAddress src, InetAddress dst, Payload payload) {
366         if ((src instanceof Inet6Address) != (dst instanceof Inet6Address)) {
367             throw new IllegalArgumentException("Invalid src/dst address combination");
368         }
369 
370         if (src instanceof Inet6Address) {
371             return new Ip6Header(protocol, (Inet6Address) src, (Inet6Address) dst, payload);
372         } else {
373             return new Ip4Header(protocol, (Inet4Address) src, (Inet4Address) dst, payload);
374         }
375     }
376 }
377