1 /*
2  * Copyright (C) 2017 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 android.net;
18 
19 import static android.system.OsConstants.AF_INET;
20 import static android.system.OsConstants.IPPROTO_UDP;
21 import static android.system.OsConstants.SOCK_DGRAM;
22 
23 import static org.junit.Assert.assertEquals;
24 import static org.junit.Assert.assertNotNull;
25 import static org.junit.Assert.fail;
26 import static org.mockito.Matchers.anyInt;
27 import static org.mockito.Matchers.anyObject;
28 import static org.mockito.Matchers.anyString;
29 import static org.mockito.Matchers.eq;
30 import static org.mockito.Mockito.mock;
31 import static org.mockito.Mockito.verify;
32 import static org.mockito.Mockito.when;
33 
34 import android.os.Build;
35 import android.system.Os;
36 import android.test.mock.MockContext;
37 
38 import androidx.test.filters.SmallTest;
39 
40 import com.android.server.IpSecService;
41 import com.android.testutils.DevSdkIgnoreRule;
42 import com.android.testutils.DevSdkIgnoreRunner;
43 
44 import org.junit.Before;
45 import org.junit.Test;
46 import org.junit.runner.RunWith;
47 
48 import java.net.InetAddress;
49 import java.net.Socket;
50 import java.net.UnknownHostException;
51 
52 /** Unit tests for {@link IpSecManager}. */
53 @SmallTest
54 @RunWith(DevSdkIgnoreRunner.class)
55 @DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.S_V2)
56 public class IpSecManagerTest {
57 
58     private static final int TEST_UDP_ENCAP_PORT = 34567;
59     private static final int DROID_SPI = 0xD1201D;
60     private static final int DUMMY_RESOURCE_ID = 0x1234;
61 
62     private static final InetAddress GOOGLE_DNS_4;
63     private static final String VTI_INTF_NAME = "ipsec_test";
64     private static final InetAddress VTI_LOCAL_ADDRESS;
65     private static final LinkAddress VTI_INNER_ADDRESS = new LinkAddress("10.0.1.1/24");
66 
67     static {
68         try {
69             // Google Public DNS Addresses;
70             GOOGLE_DNS_4 = InetAddress.getByName("8.8.8.8");
71             VTI_LOCAL_ADDRESS = InetAddress.getByName("8.8.4.4");
72         } catch (UnknownHostException e) {
73             throw new RuntimeException("Could not resolve DNS Addresses", e);
74         }
75     }
76 
77     private IpSecService mMockIpSecService;
78     private IpSecManager mIpSecManager;
79     private MockContext mMockContext = new MockContext() {
80         @Override
81         public String getOpPackageName() {
82             return "fooPackage";
83         }
84     };
85 
86     @Before
setUp()87     public void setUp() throws Exception {
88         mMockIpSecService = mock(IpSecService.class);
89         mIpSecManager = new IpSecManager(mMockContext, mMockIpSecService);
90     }
91 
92     /*
93      * Allocate a specific SPI
94      * Close SPIs
95      */
96     @Test
testAllocSpi()97     public void testAllocSpi() throws Exception {
98         IpSecSpiResponse spiResp =
99                 new IpSecSpiResponse(IpSecManager.Status.OK, DUMMY_RESOURCE_ID, DROID_SPI);
100         when(mMockIpSecService.allocateSecurityParameterIndex(
101                         eq(GOOGLE_DNS_4.getHostAddress()),
102                         eq(DROID_SPI),
103                         anyObject()))
104                 .thenReturn(spiResp);
105 
106         IpSecManager.SecurityParameterIndex droidSpi =
107                 mIpSecManager.allocateSecurityParameterIndex(GOOGLE_DNS_4, DROID_SPI);
108         assertEquals(DROID_SPI, droidSpi.getSpi());
109 
110         droidSpi.close();
111 
112         verify(mMockIpSecService).releaseSecurityParameterIndex(DUMMY_RESOURCE_ID);
113     }
114 
115     @Test
testAllocRandomSpi()116     public void testAllocRandomSpi() throws Exception {
117         IpSecSpiResponse spiResp =
118                 new IpSecSpiResponse(IpSecManager.Status.OK, DUMMY_RESOURCE_ID, DROID_SPI);
119         when(mMockIpSecService.allocateSecurityParameterIndex(
120                         eq(GOOGLE_DNS_4.getHostAddress()),
121                         eq(IpSecManager.INVALID_SECURITY_PARAMETER_INDEX),
122                         anyObject()))
123                 .thenReturn(spiResp);
124 
125         IpSecManager.SecurityParameterIndex randomSpi =
126                 mIpSecManager.allocateSecurityParameterIndex(GOOGLE_DNS_4);
127 
128         assertEquals(DROID_SPI, randomSpi.getSpi());
129 
130         randomSpi.close();
131 
132         verify(mMockIpSecService).releaseSecurityParameterIndex(DUMMY_RESOURCE_ID);
133     }
134 
135     /*
136      * Throws resource unavailable exception
137      */
138     @Test
testAllocSpiResUnavailableException()139     public void testAllocSpiResUnavailableException() throws Exception {
140         IpSecSpiResponse spiResp =
141                 new IpSecSpiResponse(IpSecManager.Status.RESOURCE_UNAVAILABLE, 0, 0);
142         when(mMockIpSecService.allocateSecurityParameterIndex(
143                         anyString(), anyInt(), anyObject()))
144                 .thenReturn(spiResp);
145 
146         try {
147             mIpSecManager.allocateSecurityParameterIndex(GOOGLE_DNS_4);
148             fail("ResourceUnavailableException was not thrown");
149         } catch (IpSecManager.ResourceUnavailableException e) {
150         }
151     }
152 
153     /*
154      * Throws spi unavailable exception
155      */
156     @Test
testAllocSpiSpiUnavailableException()157     public void testAllocSpiSpiUnavailableException() throws Exception {
158         IpSecSpiResponse spiResp = new IpSecSpiResponse(IpSecManager.Status.SPI_UNAVAILABLE, 0, 0);
159         when(mMockIpSecService.allocateSecurityParameterIndex(
160                         anyString(), anyInt(), anyObject()))
161                 .thenReturn(spiResp);
162 
163         try {
164             mIpSecManager.allocateSecurityParameterIndex(GOOGLE_DNS_4);
165             fail("ResourceUnavailableException was not thrown");
166         } catch (IpSecManager.ResourceUnavailableException e) {
167         }
168     }
169 
170     /*
171      * Should throw exception when request spi 0 in IpSecManager
172      */
173     @Test
testRequestAllocInvalidSpi()174     public void testRequestAllocInvalidSpi() throws Exception {
175         try {
176             mIpSecManager.allocateSecurityParameterIndex(GOOGLE_DNS_4, 0);
177             fail("Able to allocate invalid spi");
178         } catch (IllegalArgumentException e) {
179         }
180     }
181 
182     @Test
testOpenEncapsulationSocket()183     public void testOpenEncapsulationSocket() throws Exception {
184         IpSecUdpEncapResponse udpEncapResp =
185                 new IpSecUdpEncapResponse(
186                         IpSecManager.Status.OK,
187                         DUMMY_RESOURCE_ID,
188                         TEST_UDP_ENCAP_PORT,
189                         Os.socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP));
190         when(mMockIpSecService.openUdpEncapsulationSocket(eq(TEST_UDP_ENCAP_PORT), anyObject()))
191                 .thenReturn(udpEncapResp);
192 
193         IpSecManager.UdpEncapsulationSocket encapSocket =
194                 mIpSecManager.openUdpEncapsulationSocket(TEST_UDP_ENCAP_PORT);
195         assertNotNull(encapSocket.getFileDescriptor());
196         assertEquals(TEST_UDP_ENCAP_PORT, encapSocket.getPort());
197 
198         encapSocket.close();
199 
200         verify(mMockIpSecService).closeUdpEncapsulationSocket(DUMMY_RESOURCE_ID);
201     }
202 
203     @Test
testApplyTransportModeTransformEnsuresSocketCreation()204     public void testApplyTransportModeTransformEnsuresSocketCreation() throws Exception {
205         Socket socket = new Socket();
206         IpSecConfig dummyConfig = new IpSecConfig();
207         IpSecTransform dummyTransform = new IpSecTransform(null, dummyConfig);
208 
209         // Even if underlying SocketImpl is not initalized, this should force the init, and
210         // thereby succeed.
211         mIpSecManager.applyTransportModeTransform(
212                 socket, IpSecManager.DIRECTION_IN, dummyTransform);
213 
214         // Check to make sure the FileDescriptor is non-null
215         assertNotNull(socket.getFileDescriptor$());
216     }
217 
218     @Test
testRemoveTransportModeTransformsForcesSocketCreation()219     public void testRemoveTransportModeTransformsForcesSocketCreation() throws Exception {
220         Socket socket = new Socket();
221 
222         // Even if underlying SocketImpl is not initalized, this should force the init, and
223         // thereby succeed.
224         mIpSecManager.removeTransportModeTransforms(socket);
225 
226         // Check to make sure the FileDescriptor is non-null
227         assertNotNull(socket.getFileDescriptor$());
228     }
229 
230     @Test
testOpenEncapsulationSocketOnRandomPort()231     public void testOpenEncapsulationSocketOnRandomPort() throws Exception {
232         IpSecUdpEncapResponse udpEncapResp =
233                 new IpSecUdpEncapResponse(
234                         IpSecManager.Status.OK,
235                         DUMMY_RESOURCE_ID,
236                         TEST_UDP_ENCAP_PORT,
237                         Os.socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP));
238 
239         when(mMockIpSecService.openUdpEncapsulationSocket(eq(0), anyObject()))
240                 .thenReturn(udpEncapResp);
241 
242         IpSecManager.UdpEncapsulationSocket encapSocket =
243                 mIpSecManager.openUdpEncapsulationSocket();
244 
245         assertNotNull(encapSocket.getFileDescriptor());
246         assertEquals(TEST_UDP_ENCAP_PORT, encapSocket.getPort());
247 
248         encapSocket.close();
249 
250         verify(mMockIpSecService).closeUdpEncapsulationSocket(DUMMY_RESOURCE_ID);
251     }
252 
253     @Test
testOpenEncapsulationSocketWithInvalidPort()254     public void testOpenEncapsulationSocketWithInvalidPort() throws Exception {
255         try {
256             mIpSecManager.openUdpEncapsulationSocket(IpSecManager.INVALID_SECURITY_PARAMETER_INDEX);
257             fail("IllegalArgumentException was not thrown");
258         } catch (IllegalArgumentException e) {
259         }
260     }
261 
262     // TODO: add test when applicable transform builder interface is available
263 
createAndValidateVti(int resourceId, String intfName)264     private IpSecManager.IpSecTunnelInterface createAndValidateVti(int resourceId, String intfName)
265             throws Exception {
266         IpSecTunnelInterfaceResponse dummyResponse =
267                 new IpSecTunnelInterfaceResponse(IpSecManager.Status.OK, resourceId, intfName);
268         when(mMockIpSecService.createTunnelInterface(
269                 eq(VTI_LOCAL_ADDRESS.getHostAddress()), eq(GOOGLE_DNS_4.getHostAddress()),
270                 anyObject(), anyObject(), anyString()))
271                         .thenReturn(dummyResponse);
272 
273         IpSecManager.IpSecTunnelInterface tunnelIntf = mIpSecManager.createIpSecTunnelInterface(
274                 VTI_LOCAL_ADDRESS, GOOGLE_DNS_4, mock(Network.class));
275 
276         assertNotNull(tunnelIntf);
277         return tunnelIntf;
278     }
279 
280     @Test
testCreateVti()281     public void testCreateVti() throws Exception {
282         IpSecManager.IpSecTunnelInterface tunnelIntf =
283                 createAndValidateVti(DUMMY_RESOURCE_ID, VTI_INTF_NAME);
284 
285         assertEquals(VTI_INTF_NAME, tunnelIntf.getInterfaceName());
286 
287         tunnelIntf.close();
288         verify(mMockIpSecService).deleteTunnelInterface(eq(DUMMY_RESOURCE_ID), anyString());
289     }
290 
291     @Test
testAddRemoveAddressesFromVti()292     public void testAddRemoveAddressesFromVti() throws Exception {
293         IpSecManager.IpSecTunnelInterface tunnelIntf =
294                 createAndValidateVti(DUMMY_RESOURCE_ID, VTI_INTF_NAME);
295 
296         tunnelIntf.addAddress(VTI_INNER_ADDRESS.getAddress(),
297                 VTI_INNER_ADDRESS.getPrefixLength());
298         verify(mMockIpSecService)
299                 .addAddressToTunnelInterface(
300                         eq(DUMMY_RESOURCE_ID), eq(VTI_INNER_ADDRESS), anyString());
301 
302         tunnelIntf.removeAddress(VTI_INNER_ADDRESS.getAddress(),
303                 VTI_INNER_ADDRESS.getPrefixLength());
304         verify(mMockIpSecService)
305                 .addAddressToTunnelInterface(
306                         eq(DUMMY_RESOURCE_ID), eq(VTI_INNER_ADDRESS), anyString());
307     }
308 }
309