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