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