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 
21 import android.net.IpSecManager;
22 import android.net.IpSecManager.ResourceUnavailableException;
23 import android.net.IpSecManager.UdpEncapsulationSocket;
24 import android.os.Handler;
25 import android.os.Looper;
26 import android.system.ErrnoException;
27 
28 import com.android.internal.annotations.VisibleForTesting;
29 
30 import java.io.FileDescriptor;
31 import java.io.IOException;
32 import java.net.InetAddress;
33 import java.util.Arrays;
34 import java.util.HashMap;
35 import java.util.Map;
36 
37 /**
38  * IkeUdpEncapSocket uses an {@link UdpEncapsulationSocket} to send and receive IKE packets.
39  *
40  * <p>Caller MUST provide one IkeSocketConfig when trying to get an instance of IkeUdpEncapSocket.
41  * Each IkeSocketConfig can only be bound by one IkeUdpEncapSocket instance. When caller requests
42  * for IkeUdpEncapSocket with an already bound IkeSocketConfig, an existing instance will be
43  * returned.
44  */
45 public final class IkeUdpEncapSocket extends IkeSocket {
46     private static final String TAG = "IkeUdpEncapSocket";
47 
48     // Map from IkeSocketConfig to IkeSocket instances.
49     private static Map<IkeSocketConfig, IkeUdpEncapSocket> sConfigToSocketMap = new HashMap<>();
50 
51     private static IPacketReceiver sPacketReceiver =
52             new IkeUdpEncapPortPacketHandler.PacketReceiver();
53 
54     // UdpEncapsulationSocket for sending and receving IKE packet.
55     private final UdpEncapsulationSocket mUdpEncapSocket;
56 
57     private final IkeUdpEncapPortPacketHandler mUdpEncapPortPacketHandler;
58 
IkeUdpEncapSocket( UdpEncapsulationSocket udpEncapSocket, IkeSocketConfig sockConfig, Handler handler)59     private IkeUdpEncapSocket(
60             UdpEncapsulationSocket udpEncapSocket, IkeSocketConfig sockConfig, Handler handler) {
61         super(sockConfig, handler);
62         mUdpEncapSocket = udpEncapSocket;
63 
64         mUdpEncapPortPacketHandler = new IkeUdpEncapPortPacketHandler(getFd());
65     }
66 
67     /**
68      * Get an IkeUdpEncapSocket instance.
69      *
70      * <p>Return the existing IkeUdpEncapSocket instance if it has been created for the input
71      * IkeSocketConfig. Otherwise, create and return a new IkeUdpEncapSocket instance.
72      *
73      * @param sockConfig the socket configuration
74      * @param ipsecManager for creating {@link UdpEncapsulationSocket}
75      * @param callback the callback for signalling IkeSocket events
76      * @return an IkeUdpEncapSocket instance
77      */
getIkeUdpEncapSocket( IkeSocketConfig sockConfig, IpSecManager ipsecManager, IkeSocket.Callback callback, Looper looper)78     public static IkeUdpEncapSocket getIkeUdpEncapSocket(
79             IkeSocketConfig sockConfig,
80             IpSecManager ipsecManager,
81             IkeSocket.Callback callback,
82             Looper looper)
83             throws ErrnoException, IOException, ResourceUnavailableException {
84         IkeUdpEncapSocket ikeSocket = sConfigToSocketMap.get(sockConfig);
85         if (ikeSocket == null) {
86             UdpEncapsulationSocket udpEncapSocket = ipsecManager.openUdpEncapsulationSocket();
87             FileDescriptor fd = udpEncapSocket.getFileDescriptor();
88             applySocketConfig(sockConfig, fd, false /* isIpv6 */);
89 
90             ikeSocket = new IkeUdpEncapSocket(udpEncapSocket, sockConfig, new Handler(looper));
91 
92             // Create and register FileDescriptor for receiving IKE packet on current thread.
93             ikeSocket.start();
94 
95             sConfigToSocketMap.put(sockConfig, ikeSocket);
96         }
97         ikeSocket.mRegisteredCallbacks.add(callback);
98         return ikeSocket;
99     }
100 
101     /**
102      * Returns the {@link UdpEncapsulationSocket}
103      *
104      * @return the {@link UdpEncapsulationSocket} for sending and receiving IKE packets.
105      */
getUdpEncapsulationSocket()106     public UdpEncapsulationSocket getUdpEncapsulationSocket() {
107         return mUdpEncapSocket;
108     }
109 
110     /** Get FileDescriptor for use with the packet reader polling loop */
111     @Override
getFd()112     protected FileDescriptor getFd() {
113         return mUdpEncapSocket.getFileDescriptor(); // Returns cached FD
114     }
115 
116     /** Package private */
117     @VisibleForTesting
setPacketReceiver(IkeSocket.IPacketReceiver receiver)118     static void setPacketReceiver(IkeSocket.IPacketReceiver receiver) {
119         sPacketReceiver = receiver;
120     }
121 
122     /**
123      * Handle received IKE packet. Invoked when there is a read event. Any desired copies of
124      * |recvbuf| should be made in here, as the underlying byte array is reused across all reads.
125      */
126     @Override
handlePacket(byte[] recvbuf, int length)127     protected void handlePacket(byte[] recvbuf, int length) {
128         sPacketReceiver.handlePacket(Arrays.copyOfRange(recvbuf, 0, length), mSpiToCallback);
129     }
130 
131     @Override
sendIkePacket(byte[] ikePacket, InetAddress serverAddress)132     public void sendIkePacket(byte[] ikePacket, InetAddress serverAddress) {
133         mUdpEncapPortPacketHandler.sendIkePacket(ikePacket, serverAddress);
134     }
135 
136     @Override
getIkeServerPort()137     public int getIkeServerPort() {
138         return SERVER_PORT_UDP_ENCAPSULATED;
139     }
140 
141     /** Implement {@link AutoCloseable#close()} */
142     @Override
close()143     public void close() {
144         sConfigToSocketMap.remove(getIkeSocketConfig());
145 
146         try {
147             mUdpEncapSocket.close();
148         } catch (IOException e) {
149             getIkeLog()
150                     .e(
151                             TAG,
152                             "Failed to close UDP Encapsulation Socket with Port= "
153                                     + mUdpEncapSocket.getPort());
154         }
155 
156         // PacketReader unregisters file descriptor on thread with which the Handler constructor
157         // argument is associated.
158         super.close();
159     }
160 }
161