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