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