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 17 package com.android.internal.net.ipsec.ike; 18 19 import static android.net.ipsec.ike.IkeManager.getIkeLog; 20 import static android.system.OsConstants.F_SETFL; 21 import static android.system.OsConstants.SOCK_DGRAM; 22 import static android.system.OsConstants.SOCK_NONBLOCK; 23 24 import android.net.IpSecManager; 25 import android.net.IpSecManager.ResourceUnavailableException; 26 import android.net.IpSecManager.UdpEncapsulationSocket; 27 import android.net.Network; 28 import android.os.Handler; 29 import android.os.Looper; 30 import android.system.ErrnoException; 31 import android.system.Os; 32 import android.util.LongSparseArray; 33 34 import com.android.internal.annotations.VisibleForTesting; 35 36 import java.io.FileDescriptor; 37 import java.io.IOException; 38 import java.net.InetAddress; 39 import java.nio.ByteBuffer; 40 import java.util.Arrays; 41 import java.util.HashMap; 42 import java.util.Map; 43 44 /** 45 * IkeUdpEncapSocket uses an {@link UdpEncapsulationSocket} to send and receive IKE packets. 46 * 47 * <p>Caller MUST provide one {@link Network} when trying to get an instance of IkeUdpEncapSocket. 48 * Each {@link Network} can only be bound with one IkeUdpEncapSocket instance. When caller requests 49 * for IkeUdpEncapSocket with an already bound {@link Network}, an existing instance will be 50 * returned. 51 */ 52 public final class IkeUdpEncapSocket extends IkeSocket { 53 private static final String TAG = "IkeUdpEncapSocket"; 54 55 // A Non-ESP marker helps the recipient to distinguish IKE packets from ESP packets. 56 @VisibleForTesting static final int NON_ESP_MARKER_LEN = 4; 57 @VisibleForTesting static final byte[] NON_ESP_MARKER = new byte[NON_ESP_MARKER_LEN]; 58 59 // Map from Network to IkeSocket instances. 60 private static Map<Network, IkeUdpEncapSocket> sNetworkToIkeSocketMap = new HashMap<>(); 61 62 private static IPacketReceiver sPacketReceiver = new PacketReceiver(); 63 64 // UdpEncapsulationSocket for sending and receving IKE packet. 65 private final UdpEncapsulationSocket mUdpEncapSocket; 66 IkeUdpEncapSocket( UdpEncapsulationSocket udpEncapSocket, Network network, Handler handler)67 private IkeUdpEncapSocket( 68 UdpEncapsulationSocket udpEncapSocket, Network network, Handler handler) { 69 super(network, handler); 70 mUdpEncapSocket = udpEncapSocket; 71 } 72 73 /** 74 * Get an IkeUdpEncapSocket instance. 75 * 76 * <p>Return the existing IkeUdpEncapSocket instance if it has been created for the input 77 * Network. Otherwise, create and return a new IkeUdpEncapSocket instance. 78 * 79 * @param network the Network this socket will be bound to 80 * @param ipsecManager for creating {@link UdpEncapsulationSocket} 81 * @param ikeSession the IkeSessionStateMachine that is requesting an IkeUdpEncapSocket. 82 * @return an IkeUdpEncapSocket instance 83 */ getIkeUdpEncapSocket( Network network, IpSecManager ipsecManager, IkeSessionStateMachine ikeSession, Looper looper)84 public static IkeUdpEncapSocket getIkeUdpEncapSocket( 85 Network network, 86 IpSecManager ipsecManager, 87 IkeSessionStateMachine ikeSession, 88 Looper looper) 89 throws ErrnoException, IOException, ResourceUnavailableException { 90 IkeUdpEncapSocket ikeSocket = sNetworkToIkeSocketMap.get(network); 91 if (ikeSocket == null) { 92 UdpEncapsulationSocket udpEncapSocket = ipsecManager.openUdpEncapsulationSocket(); 93 FileDescriptor fd = udpEncapSocket.getFileDescriptor(); 94 95 // {@link PacketReader} requires non-blocking I/O access. Set SOCK_NONBLOCK here. 96 Os.fcntlInt(fd, F_SETFL, SOCK_DGRAM | SOCK_NONBLOCK); 97 network.bindSocket(fd); 98 99 ikeSocket = new IkeUdpEncapSocket(udpEncapSocket, network, new Handler(looper)); 100 101 // Create and register FileDescriptor for receiving IKE packet on current thread. 102 ikeSocket.start(); 103 104 sNetworkToIkeSocketMap.put(network, ikeSocket); 105 } 106 ikeSocket.mAliveIkeSessions.add(ikeSession); 107 return ikeSocket; 108 } 109 110 /** 111 * Returns the {@link UdpEncapsulationSocket} 112 * 113 * @return the {@link UdpEncapsulationSocket} for sending and receiving IKE packets. 114 */ getUdpEncapsulationSocket()115 public UdpEncapsulationSocket getUdpEncapsulationSocket() { 116 return mUdpEncapSocket; 117 } 118 119 /** Get FileDescriptor for use with the packet reader polling loop */ 120 @Override getFd()121 protected FileDescriptor getFd() { 122 return mUdpEncapSocket.getFileDescriptor(); // Returns cached FD 123 } 124 125 /** Package private */ 126 @VisibleForTesting 127 static final class PacketReceiver implements IkeSocket.IPacketReceiver { handlePacket( byte[] recvbuf, LongSparseArray<IkeSessionStateMachine> spiToIkeSession)128 public void handlePacket( 129 byte[] recvbuf, LongSparseArray<IkeSessionStateMachine> spiToIkeSession) { 130 ByteBuffer byteBuffer = ByteBuffer.wrap(recvbuf); 131 132 // Check the existence of the Non-ESP Marker. A received packet can be either an IKE 133 // packet starts with 4 zero-valued bytes Non-ESP Marker or an ESP packet starts with 4 134 // bytes ESP SPI. ESP SPI value can never be zero. 135 byte[] espMarker = new byte[NON_ESP_MARKER_LEN]; 136 byteBuffer.get(espMarker); 137 if (!Arrays.equals(NON_ESP_MARKER, espMarker)) { 138 // Drop the received ESP packet. 139 getIkeLog().e(TAG, "Receive an ESP packet."); 140 return; 141 } 142 143 // Re-direct IKE packet to IkeSessionStateMachine according to the locally generated 144 // IKE SPI. 145 byte[] ikePacketBytes = new byte[byteBuffer.remaining()]; 146 byteBuffer.get(ikePacketBytes); 147 parseAndDemuxIkePacket(ikePacketBytes, spiToIkeSession, TAG); 148 } 149 } 150 151 /** Package private */ 152 @VisibleForTesting setPacketReceiver(IkeSocket.IPacketReceiver receiver)153 static void setPacketReceiver(IkeSocket.IPacketReceiver receiver) { 154 sPacketReceiver = receiver; 155 } 156 157 /** 158 * Handle received IKE packet. Invoked when there is a read event. Any desired copies of 159 * |recvbuf| should be made in here, as the underlying byte array is reused across all reads. 160 */ 161 @Override handlePacket(byte[] recvbuf, int length)162 protected void handlePacket(byte[] recvbuf, int length) { 163 sPacketReceiver.handlePacket(Arrays.copyOfRange(recvbuf, 0, length), mSpiToIkeSession); 164 } 165 166 /** 167 * Send encoded IKE packet to destination address 168 * 169 * @param ikePacket encoded IKE packet 170 * @param serverAddress IP address of remote server 171 */ 172 @Override sendIkePacket(byte[] ikePacket, InetAddress serverAddress)173 public void sendIkePacket(byte[] ikePacket, InetAddress serverAddress) { 174 getIkeLog() 175 .d( 176 TAG, 177 "Send packet to " 178 + serverAddress.getHostAddress() 179 + "( " 180 + ikePacket.length 181 + " bytes)"); 182 try { 183 ByteBuffer buffer = ByteBuffer.allocate(NON_ESP_MARKER_LEN + ikePacket.length); 184 185 // Build outbound UDP Encapsulation packet body for sending IKE message. 186 buffer.put(NON_ESP_MARKER).put(ikePacket); 187 buffer.rewind(); 188 189 // Use unconnected UDP socket because one {@UdpEncapsulationSocket} may be shared by 190 // multiple IKE sessions that send messages to different destinations. 191 Os.sendto( 192 mUdpEncapSocket.getFileDescriptor(), 193 buffer, 194 0, 195 serverAddress, 196 SERVER_PORT_UDP_ENCAPSULATED); 197 } catch (ErrnoException | IOException e) { 198 // TODO: Handle exception 199 } 200 } 201 202 @Override getIkeServerPort()203 public int getIkeServerPort() { 204 return SERVER_PORT_UDP_ENCAPSULATED; 205 } 206 207 /** Implement {@link AutoCloseable#close()} */ 208 @Override close()209 public void close() { 210 sNetworkToIkeSocketMap.remove(getNetwork()); 211 212 try { 213 mUdpEncapSocket.close(); 214 } catch (IOException e) { 215 getIkeLog() 216 .e( 217 TAG, 218 "Failed to close UDP Encapsulation Socket with Port= " 219 + mUdpEncapSocket.getPort()); 220 } 221 222 // PacketReader unregisters file descriptor on thread with which the Handler constructor 223 // argument is associated. 224 super.close(); 225 } 226 } 227