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 com.android.internal.net.ipsec.ike; 18 19 import static android.net.ipsec.ike.IkeManager.getIkeLog; 20 21 import android.net.Network; 22 import android.net.ipsec.ike.exceptions.IkeProtocolException; 23 import android.os.Handler; 24 import android.system.ErrnoException; 25 import android.system.Os; 26 import android.util.LongSparseArray; 27 28 import com.android.internal.annotations.VisibleForTesting; 29 import com.android.internal.net.ipsec.ike.message.IkeHeader; 30 31 import java.io.FileDescriptor; 32 import java.io.FileInputStream; 33 import java.io.IOException; 34 import java.net.InetAddress; 35 import java.net.InetSocketAddress; 36 import java.util.Arrays; 37 import java.util.HashSet; 38 import java.util.Set; 39 40 /** 41 * IkeSocket is used for sending and receiving IKE packets for {@link IkeSessionStateMachine}s. 42 * 43 * <p>To function as a packet receiver, a subclass MUST override #createFd() and #handlePacket() so 44 * that it can register a file descriptor with a thread's Looper and handle read events (and 45 * errors). Users can expect a call life-cycle like the following: 46 * 47 * <pre> 48 * [1] when user gets a new initiated IkeSocket, start() is called and followed by createFd(). 49 * [2] yield, waiting for a read event which will invoke handlePacket() 50 * [3] when user closes this IkeSocket, its reference count decreases. Then stop() is called when 51 * there is no reference of this instance. 52 * </pre> 53 * 54 * <p>IkeSocket is constructed and called only on a single IKE working thread by {@link 55 * IkeSessionStateMachine}. Since all {@link IkeSessionStateMachine}s run on the same working 56 * thread, there will not be concurrent modification problems. 57 */ 58 public abstract class IkeSocket implements AutoCloseable { 59 private static final String TAG = "IkeSocket"; 60 61 /** Non-udp-encapsulated IKE packets MUST be sent to 500. */ 62 public static final int SERVER_PORT_NON_UDP_ENCAPSULATED = 500; 63 /** UDP-encapsulated IKE packets MUST be sent to 4500. */ 64 public static final int SERVER_PORT_UDP_ENCAPSULATED = 4500; 65 66 // Network this socket bound to. 67 private final Network mNetwork; 68 private final Handler mHandler; 69 70 // Map from locally generated IKE SPI to IkeSessionStateMachine instances. 71 @VisibleForTesting 72 protected final LongSparseArray<IkeSessionStateMachine> mSpiToIkeSession = 73 new LongSparseArray<>(); 74 75 // Set to store all running IkeSessionStateMachines that are using this IkeSocket instance. 76 @VisibleForTesting 77 protected final Set<IkeSessionStateMachine> mAliveIkeSessions = new HashSet<>(); 78 IkeSocket(Network network, Handler handler)79 protected IkeSocket(Network network, Handler handler) { 80 mHandler = handler; 81 mNetwork = network; 82 } 83 parseAndDemuxIkePacket( byte[] ikePacketBytes, LongSparseArray<IkeSessionStateMachine> spiToIkeSession, String tag)84 protected static void parseAndDemuxIkePacket( 85 byte[] ikePacketBytes, 86 LongSparseArray<IkeSessionStateMachine> spiToIkeSession, 87 String tag) { 88 try { 89 // TODO: Retrieve and log the source address 90 getIkeLog().d(tag, "Receive packet of " + ikePacketBytes.length + " bytes)"); 91 getIkeLog().d(tag, getIkeLog().pii(ikePacketBytes)); 92 93 IkeHeader ikeHeader = new IkeHeader(ikePacketBytes); 94 95 long localGeneratedSpi = 96 ikeHeader.fromIkeInitiator 97 ? ikeHeader.ikeResponderSpi 98 : ikeHeader.ikeInitiatorSpi; 99 100 IkeSessionStateMachine ikeStateMachine = spiToIkeSession.get(localGeneratedSpi); 101 if (ikeStateMachine == null) { 102 getIkeLog().w(tag, "Unrecognized IKE SPI."); 103 // TODO(b/148479270): Handle invalid IKE SPI error 104 } else { 105 ikeStateMachine.receiveIkePacket(ikeHeader, ikePacketBytes); 106 } 107 } catch (IkeProtocolException e) { 108 // Handle invalid IKE header 109 getIkeLog().i(tag, "Can't parse malformed IKE packet header."); 110 } 111 } 112 113 /** Starts the packet reading poll-loop. */ start()114 public void start() { 115 // Start background reader thread 116 new Thread( 117 () -> { 118 try { 119 // Loop will exit and thread will quit when the retrieved fd is closed. 120 // Receiving either EOF or an exception will exit this reader loop. 121 // FileInputStream in uninterruptable, so there's no good way to ensure that 122 // that this thread shuts down except upon FD closure. 123 while (true) { 124 byte[] intercepted = receiveFromFd(); 125 if (intercepted == null) { 126 // Exit once we've hit EOF 127 return; 128 } else if (intercepted.length > 0) { 129 // Only save packet if we've received any bytes. 130 getIkeLog() 131 .d( 132 this.getClass().getSimpleName(), 133 "Received packet"); 134 mHandler.post( 135 () -> { 136 handlePacket(intercepted, intercepted.length); 137 }); 138 } 139 } 140 } catch (IOException ignored) { 141 // Simply exit this reader thread 142 return; 143 } 144 }).start(); 145 } 146 receiveFromFd()147 private byte[] receiveFromFd() throws IOException { 148 FileInputStream in = new FileInputStream(getFd()); 149 byte[] inBytes = new byte[4096]; 150 int bytesRead = in.read(inBytes); 151 152 if (bytesRead < 0) { 153 return null; // return null for EOF 154 } else if (bytesRead >= 4096) { 155 throw new IllegalStateException("Too big packet. Fragmentation unsupported"); 156 } 157 return Arrays.copyOf(inBytes, bytesRead); 158 } 159 160 /** 161 * Returns the port that this IKE socket is listening on (bound to). 162 */ getLocalPort()163 public final int getLocalPort() throws ErrnoException { 164 InetSocketAddress localAddr = (InetSocketAddress) Os.getsockname(getFd()); 165 return localAddr.getPort(); 166 } 167 getFd()168 protected abstract FileDescriptor getFd(); 169 createFd()170 protected FileDescriptor createFd() { 171 return getFd(); 172 } 173 handlePacket(byte[] recvbuf, int length)174 protected abstract void handlePacket(byte[] recvbuf, int length); 175 176 /** 177 * Return Network this socket bound to 178 * 179 * @return the bound Network 180 */ getNetwork()181 public final Network getNetwork() { 182 return mNetwork; 183 } 184 185 /** 186 * Register new created IKE SA 187 * 188 * @param spi the locally generated IKE SPI 189 * @param ikeSession the IKE session this IKE SA belongs to 190 */ registerIke(long spi, IkeSessionStateMachine ikeSession)191 public final void registerIke(long spi, IkeSessionStateMachine ikeSession) { 192 mSpiToIkeSession.put(spi, ikeSession); 193 } 194 195 /** 196 * Unregister a deleted IKE SA 197 * 198 * @param spi the locally generated IKE SPI 199 */ unregisterIke(long spi)200 public final void unregisterIke(long spi) { 201 mSpiToIkeSession.remove(spi); 202 } 203 204 /** 205 * Release reference of current IkeSocket when the IKE session no longer needs it. 206 * 207 * @param ikeSession IKE session that is being closed. 208 */ releaseReference(IkeSessionStateMachine ikeSession)209 public final void releaseReference(IkeSessionStateMachine ikeSession) { 210 mAliveIkeSessions.remove(ikeSession); 211 if (mAliveIkeSessions.isEmpty()) close(); 212 } 213 214 /** 215 * Send an encoded IKE packet to destination address 216 * 217 * @param ikePacket encoded IKE packet 218 * @param serverAddress IP address of remote server 219 */ sendIkePacket(byte[] ikePacket, InetAddress serverAddress)220 public abstract void sendIkePacket(byte[] ikePacket, InetAddress serverAddress); 221 222 /** 223 * Returns port of remote IKE sever (destination port of outbound packet) 224 * 225 * @return destination port in remote IKE sever. 226 */ getIkeServerPort()227 public abstract int getIkeServerPort(); 228 229 /** Implement {@link AutoCloseable#close()} */ 230 @Override close()231 public void close() { 232 stop(); 233 } 234 235 /** Stops the packet reading loop */ stop()236 public void stop() { 237 // No additional cleanup at this time. Subclasses are in charge of closing their sockets, 238 // which will result in the packet reading poll loop exiting. 239 } 240 241 /** 242 * IPacketReceiver provides a package private interface for handling received packet. 243 * 244 * <p>IPacketReceiver exists so that the interface is injectable for testing. 245 */ 246 interface IPacketReceiver { handlePacket(byte[] recvbuf, LongSparseArray<IkeSessionStateMachine> spiToIkeSession)247 void handlePacket(byte[] recvbuf, LongSparseArray<IkeSessionStateMachine> spiToIkeSession); 248 } 249 } 250