• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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