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.net.cts;
17 
18 import static android.net.cts.PacketUtils.BytePayload;
19 import static android.net.cts.PacketUtils.IP4_HDRLEN;
20 import static android.net.cts.PacketUtils.IP6_HDRLEN;
21 import static android.net.cts.PacketUtils.IpHeader;
22 import static android.net.cts.PacketUtils.UDP_HDRLEN;
23 import static android.net.cts.PacketUtils.UdpHeader;
24 import static android.net.cts.PacketUtils.getIpHeader;
25 import static android.system.OsConstants.IPPROTO_UDP;
26 
27 import android.os.ParcelFileDescriptor;
28 
29 import java.net.InetAddress;
30 import java.nio.ByteBuffer;
31 import java.util.Arrays;
32 
33 // TODO: Merge this with the version in the IPsec module (IKEv2 library) CTS tests.
34 /** An extension of the TunUtils class with IKE-specific packet handling. */
35 public class IkeTunUtils extends TunUtils {
36     private static final int PORT_LEN = 2;
37 
38     private static final byte[] NON_ESP_MARKER = new byte[] {0, 0, 0, 0};
39 
40     private static final int IKE_HEADER_LEN = 28;
41     private static final int IKE_SPI_LEN = 8;
42     private static final int IKE_IS_RESP_BYTE_OFFSET = 19;
43     private static final int IKE_MSG_ID_OFFSET = 20;
44     private static final int IKE_MSG_ID_LEN = 4;
45 
IkeTunUtils(ParcelFileDescriptor tunFd)46     public IkeTunUtils(ParcelFileDescriptor tunFd) {
47         super(tunFd);
48     }
49 
50     /**
51      * Await an expected IKE request and inject an IKE response.
52      *
53      * @param respIkePkt IKE response packet without IP/UDP headers or NON ESP MARKER.
54      */
awaitReqAndInjectResp(long expectedInitIkeSpi, int expectedMsgId, boolean encapExpected, byte[] respIkePkt)55     public byte[] awaitReqAndInjectResp(long expectedInitIkeSpi, int expectedMsgId,
56             boolean encapExpected, byte[] respIkePkt) throws Exception {
57         final byte[] request = awaitIkePacket(expectedInitIkeSpi, expectedMsgId, encapExpected);
58 
59         // Build response header by flipping address and port
60         final InetAddress srcAddr = getDstAddress(request);
61         final InetAddress dstAddr = getSrcAddress(request);
62         final int srcPort = getDstPort(request);
63         final int dstPort = getSrcPort(request);
64 
65         final byte[] response =
66                 buildIkePacket(srcAddr, dstAddr, srcPort, dstPort, encapExpected, respIkePkt);
67         injectPacket(response);
68         return request;
69     }
70 
awaitIkePacket(long expectedInitIkeSpi, int expectedMsgId, boolean expectEncap)71     private byte[] awaitIkePacket(long expectedInitIkeSpi, int expectedMsgId, boolean expectEncap)
72             throws Exception {
73         return super.awaitPacket(pkt -> isIke(pkt, expectedInitIkeSpi, expectedMsgId, expectEncap));
74     }
75 
isIke( byte[] pkt, long expectedInitIkeSpi, int expectedMsgId, boolean encapExpected)76     private static boolean isIke(
77             byte[] pkt, long expectedInitIkeSpi, int expectedMsgId, boolean encapExpected) {
78         final int ipProtocolOffset;
79         final int ikeOffset;
80 
81         if (isIpv6(pkt)) {
82             ipProtocolOffset = IP6_PROTO_OFFSET;
83             ikeOffset = IP6_HDRLEN + UDP_HDRLEN;
84         } else {
85             if (encapExpected && !hasNonEspMarkerv4(pkt)) {
86                 return false;
87             }
88 
89             // Use default IPv4 header length (assuming no options)
90             final int encapMarkerLen = encapExpected ? NON_ESP_MARKER.length : 0;
91             ipProtocolOffset = IP4_PROTO_OFFSET;
92             ikeOffset = IP4_HDRLEN + UDP_HDRLEN + encapMarkerLen;
93         }
94 
95         return pkt[ipProtocolOffset] == IPPROTO_UDP
96                 && areSpiAndMsgIdEqual(pkt, ikeOffset, expectedInitIkeSpi, expectedMsgId);
97     }
98 
99     /** Checks if the provided IPv4 packet has a UDP-encapsulation NON-ESP marker */
hasNonEspMarkerv4(byte[] ipv4Pkt)100     private static boolean hasNonEspMarkerv4(byte[] ipv4Pkt) {
101         final int nonEspMarkerOffset = IP4_HDRLEN + UDP_HDRLEN;
102         if (ipv4Pkt.length < nonEspMarkerOffset + NON_ESP_MARKER.length) {
103             return false;
104         }
105 
106         final byte[] nonEspMarker = Arrays.copyOfRange(
107                 ipv4Pkt, nonEspMarkerOffset, nonEspMarkerOffset + NON_ESP_MARKER.length);
108         return Arrays.equals(NON_ESP_MARKER, nonEspMarker);
109     }
110 
areSpiAndMsgIdEqual( byte[] pkt, int ikeOffset, long expectedIkeInitSpi, int expectedMsgId)111     private static boolean areSpiAndMsgIdEqual(
112             byte[] pkt, int ikeOffset, long expectedIkeInitSpi, int expectedMsgId) {
113         if (pkt.length <= ikeOffset + IKE_HEADER_LEN) {
114             return false;
115         }
116 
117         final ByteBuffer buffer = ByteBuffer.wrap(pkt);
118         final long spi = buffer.getLong(ikeOffset);
119         final int msgId = buffer.getInt(ikeOffset + IKE_MSG_ID_OFFSET);
120 
121         return expectedIkeInitSpi == spi && expectedMsgId == msgId;
122     }
123 
getSrcAddress(byte[] pkt)124     private static InetAddress getSrcAddress(byte[] pkt) throws Exception {
125         return getAddress(pkt, true);
126     }
127 
getDstAddress(byte[] pkt)128     private static InetAddress getDstAddress(byte[] pkt) throws Exception {
129         return getAddress(pkt, false);
130     }
131 
getAddress(byte[] pkt, boolean getSrcAddr)132     private static InetAddress getAddress(byte[] pkt, boolean getSrcAddr) throws Exception {
133         final int ipLen = isIpv6(pkt) ? IP6_ADDR_LEN : IP4_ADDR_LEN;
134         final int srcIpOffset = isIpv6(pkt) ? IP6_ADDR_OFFSET : IP4_ADDR_OFFSET;
135         final int ipOffset = getSrcAddr ? srcIpOffset : srcIpOffset + ipLen;
136 
137         if (pkt.length < ipOffset + ipLen) {
138             // Should be impossible; getAddress() is only called with a full IKE request including
139             // the IP and UDP headers.
140             throw new IllegalArgumentException("Packet was too short to contain IP address");
141         }
142 
143         return InetAddress.getByAddress(Arrays.copyOfRange(pkt, ipOffset, ipOffset + ipLen));
144     }
145 
getSrcPort(byte[] pkt)146     private static int getSrcPort(byte[] pkt) throws Exception {
147         return getPort(pkt, true);
148     }
149 
getDstPort(byte[] pkt)150     private static int getDstPort(byte[] pkt) throws Exception {
151         return getPort(pkt, false);
152     }
153 
getPort(byte[] pkt, boolean getSrcPort)154     private static int getPort(byte[] pkt, boolean getSrcPort) {
155         final int srcPortOffset = isIpv6(pkt) ? IP6_HDRLEN : IP4_HDRLEN;
156         final int portOffset = getSrcPort ? srcPortOffset : srcPortOffset + PORT_LEN;
157 
158         if (pkt.length < portOffset + PORT_LEN) {
159             // Should be impossible; getPort() is only called with a full IKE request including the
160             // IP and UDP headers.
161             throw new IllegalArgumentException("Packet was too short to contain port");
162         }
163 
164         final ByteBuffer buffer = ByteBuffer.wrap(pkt);
165         return Short.toUnsignedInt(buffer.getShort(portOffset));
166     }
167 
buildIkePacket( InetAddress srcAddr, InetAddress dstAddr, int srcPort, int dstPort, boolean useEncap, byte[] payload)168     private static byte[] buildIkePacket(
169             InetAddress srcAddr,
170             InetAddress dstAddr,
171             int srcPort,
172             int dstPort,
173             boolean useEncap,
174             byte[] payload)
175             throws Exception {
176         // Append non-ESP marker if encap is enabled
177         if (useEncap) {
178             final ByteBuffer buffer = ByteBuffer.allocate(NON_ESP_MARKER.length + payload.length);
179             buffer.put(NON_ESP_MARKER);
180             buffer.put(payload);
181             payload = buffer.array();
182         }
183 
184         final UdpHeader udpPkt = new UdpHeader(srcPort, dstPort, new BytePayload(payload));
185         final IpHeader ipPkt = getIpHeader(udpPkt.getProtocolId(), srcAddr, dstAddr, udpPkt);
186         return ipPkt.getPacketBytes();
187     }
188 }
189