1 /*
2  * Copyright (C) 2020 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.F_SETFL;
21 import static android.system.OsConstants.SOCK_DGRAM;
22 import static android.system.OsConstants.SOCK_NONBLOCK;
23 
24 import android.net.IpSecManager;
25 import android.net.IpSecManager.ResourceUnavailableException;
26 import android.net.IpSecManager.UdpEncapsulationSocket;
27 import android.net.Network;
28 import android.os.Handler;
29 import android.os.Looper;
30 import android.system.ErrnoException;
31 import android.system.Os;
32 import android.util.LongSparseArray;
33 
34 import com.android.internal.annotations.VisibleForTesting;
35 
36 import java.io.FileDescriptor;
37 import java.io.IOException;
38 import java.net.InetAddress;
39 import java.nio.ByteBuffer;
40 import java.util.Arrays;
41 import java.util.HashMap;
42 import java.util.Map;
43 
44 /**
45  * IkeUdpEncapSocket uses an {@link UdpEncapsulationSocket} to send and receive IKE packets.
46  *
47  * <p>Caller MUST provide one {@link Network} when trying to get an instance of IkeUdpEncapSocket.
48  * Each {@link Network} can only be bound with one IkeUdpEncapSocket instance. When caller requests
49  * for IkeUdpEncapSocket with an already bound {@link Network}, an existing instance will be
50  * returned.
51  */
52 public final class IkeUdpEncapSocket extends IkeSocket {
53     private static final String TAG = "IkeUdpEncapSocket";
54 
55     // A Non-ESP marker helps the recipient to distinguish IKE packets from ESP packets.
56     @VisibleForTesting static final int NON_ESP_MARKER_LEN = 4;
57     @VisibleForTesting static final byte[] NON_ESP_MARKER = new byte[NON_ESP_MARKER_LEN];
58 
59     // Map from Network to IkeSocket instances.
60     private static Map<Network, IkeUdpEncapSocket> sNetworkToIkeSocketMap = new HashMap<>();
61 
62     private static IPacketReceiver sPacketReceiver = new PacketReceiver();
63 
64     // UdpEncapsulationSocket for sending and receving IKE packet.
65     private final UdpEncapsulationSocket mUdpEncapSocket;
66 
IkeUdpEncapSocket( UdpEncapsulationSocket udpEncapSocket, Network network, Handler handler)67     private IkeUdpEncapSocket(
68             UdpEncapsulationSocket udpEncapSocket, Network network, Handler handler) {
69         super(network, handler);
70         mUdpEncapSocket = udpEncapSocket;
71     }
72 
73     /**
74      * Get an IkeUdpEncapSocket instance.
75      *
76      * <p>Return the existing IkeUdpEncapSocket instance if it has been created for the input
77      * Network. Otherwise, create and return a new IkeUdpEncapSocket instance.
78      *
79      * @param network the Network this socket will be bound to
80      * @param ipsecManager for creating {@link UdpEncapsulationSocket}
81      * @param ikeSession the IkeSessionStateMachine that is requesting an IkeUdpEncapSocket.
82      * @return an IkeUdpEncapSocket instance
83      */
getIkeUdpEncapSocket( Network network, IpSecManager ipsecManager, IkeSessionStateMachine ikeSession, Looper looper)84     public static IkeUdpEncapSocket getIkeUdpEncapSocket(
85             Network network,
86             IpSecManager ipsecManager,
87             IkeSessionStateMachine ikeSession,
88             Looper looper)
89             throws ErrnoException, IOException, ResourceUnavailableException {
90         IkeUdpEncapSocket ikeSocket = sNetworkToIkeSocketMap.get(network);
91         if (ikeSocket == null) {
92             UdpEncapsulationSocket udpEncapSocket = ipsecManager.openUdpEncapsulationSocket();
93             FileDescriptor fd = udpEncapSocket.getFileDescriptor();
94 
95             // {@link PacketReader} requires non-blocking I/O access. Set SOCK_NONBLOCK here.
96             Os.fcntlInt(fd, F_SETFL, SOCK_DGRAM | SOCK_NONBLOCK);
97             network.bindSocket(fd);
98 
99             ikeSocket = new IkeUdpEncapSocket(udpEncapSocket, network, new Handler(looper));
100 
101             // Create and register FileDescriptor for receiving IKE packet on current thread.
102             ikeSocket.start();
103 
104             sNetworkToIkeSocketMap.put(network, ikeSocket);
105         }
106         ikeSocket.mAliveIkeSessions.add(ikeSession);
107         return ikeSocket;
108     }
109 
110     /**
111      * Returns the {@link UdpEncapsulationSocket}
112      *
113      * @return the {@link UdpEncapsulationSocket} for sending and receiving IKE packets.
114      */
getUdpEncapsulationSocket()115     public UdpEncapsulationSocket getUdpEncapsulationSocket() {
116         return mUdpEncapSocket;
117     }
118 
119     /** Get FileDescriptor for use with the packet reader polling loop */
120     @Override
getFd()121     protected FileDescriptor getFd() {
122         return mUdpEncapSocket.getFileDescriptor(); // Returns cached FD
123     }
124 
125     /** Package private */
126     @VisibleForTesting
127     static final class PacketReceiver implements IkeSocket.IPacketReceiver {
handlePacket( byte[] recvbuf, LongSparseArray<IkeSessionStateMachine> spiToIkeSession)128         public void handlePacket(
129                 byte[] recvbuf, LongSparseArray<IkeSessionStateMachine> spiToIkeSession) {
130             ByteBuffer byteBuffer = ByteBuffer.wrap(recvbuf);
131 
132             // Check the existence of the Non-ESP Marker. A received packet can be either an IKE
133             // packet starts with 4 zero-valued bytes Non-ESP Marker or an ESP packet starts with 4
134             // bytes ESP SPI. ESP SPI value can never be zero.
135             byte[] espMarker = new byte[NON_ESP_MARKER_LEN];
136             byteBuffer.get(espMarker);
137             if (!Arrays.equals(NON_ESP_MARKER, espMarker)) {
138                 // Drop the received ESP packet.
139                 getIkeLog().e(TAG, "Receive an ESP packet.");
140                 return;
141             }
142 
143             // Re-direct IKE packet to IkeSessionStateMachine according to the locally generated
144             // IKE SPI.
145             byte[] ikePacketBytes = new byte[byteBuffer.remaining()];
146             byteBuffer.get(ikePacketBytes);
147             parseAndDemuxIkePacket(ikePacketBytes, spiToIkeSession, TAG);
148         }
149     }
150 
151     /** Package private */
152     @VisibleForTesting
setPacketReceiver(IkeSocket.IPacketReceiver receiver)153     static void setPacketReceiver(IkeSocket.IPacketReceiver receiver) {
154         sPacketReceiver = receiver;
155     }
156 
157     /**
158      * Handle received IKE packet. Invoked when there is a read event. Any desired copies of
159      * |recvbuf| should be made in here, as the underlying byte array is reused across all reads.
160      */
161     @Override
handlePacket(byte[] recvbuf, int length)162     protected void handlePacket(byte[] recvbuf, int length) {
163         sPacketReceiver.handlePacket(Arrays.copyOfRange(recvbuf, 0, length), mSpiToIkeSession);
164     }
165 
166     /**
167      * Send encoded IKE packet to destination address
168      *
169      * @param ikePacket encoded IKE packet
170      * @param serverAddress IP address of remote server
171      */
172     @Override
sendIkePacket(byte[] ikePacket, InetAddress serverAddress)173     public void sendIkePacket(byte[] ikePacket, InetAddress serverAddress) {
174         getIkeLog()
175                 .d(
176                         TAG,
177                         "Send packet to "
178                                 + serverAddress.getHostAddress()
179                                 + "( "
180                                 + ikePacket.length
181                                 + " bytes)");
182         try {
183             ByteBuffer buffer = ByteBuffer.allocate(NON_ESP_MARKER_LEN + ikePacket.length);
184 
185             // Build outbound UDP Encapsulation packet body for sending IKE message.
186             buffer.put(NON_ESP_MARKER).put(ikePacket);
187             buffer.rewind();
188 
189             // Use unconnected UDP socket because one {@UdpEncapsulationSocket} may be shared by
190             // multiple IKE sessions that send messages to different destinations.
191             Os.sendto(
192                     mUdpEncapSocket.getFileDescriptor(),
193                     buffer,
194                     0,
195                     serverAddress,
196                     SERVER_PORT_UDP_ENCAPSULATED);
197         } catch (ErrnoException | IOException e) {
198             // TODO: Handle exception
199         }
200     }
201 
202     @Override
getIkeServerPort()203     public int getIkeServerPort() {
204         return SERVER_PORT_UDP_ENCAPSULATED;
205     }
206 
207     /** Implement {@link AutoCloseable#close()} */
208     @Override
close()209     public void close() {
210         sNetworkToIkeSocketMap.remove(getNetwork());
211 
212         try {
213             mUdpEncapSocket.close();
214         } catch (IOException e) {
215             getIkeLog()
216                     .e(
217                             TAG,
218                             "Failed to close UDP Encapsulation Socket with Port= "
219                                     + mUdpEncapSocket.getPort());
220         }
221 
222         // PacketReader unregisters file descriptor on thread with which the Handler constructor
223         // argument is associated.
224         super.close();
225     }
226 }
227