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