• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 com.android.server;
18 
19 import static android.system.OsConstants.AF_INET;
20 import static android.system.OsConstants.EADDRINUSE;
21 import static android.system.OsConstants.IPPROTO_UDP;
22 import static android.system.OsConstants.SOCK_DGRAM;
23 import static org.junit.Assert.assertEquals;
24 import static org.junit.Assert.assertNotEquals;
25 import static org.junit.Assert.assertNotNull;
26 import static org.junit.Assert.assertTrue;
27 import static org.junit.Assert.fail;
28 import static org.mockito.Matchers.anyInt;
29 import static org.mockito.Matchers.anyString;
30 import static org.mockito.Matchers.argThat;
31 import static org.mockito.Matchers.eq;
32 import static org.mockito.Mockito.mock;
33 import static org.mockito.Mockito.verify;
34 import static org.mockito.Mockito.when;
35 
36 import android.content.Context;
37 import android.net.INetd;
38 import android.net.IpSecAlgorithm;
39 import android.net.IpSecConfig;
40 import android.net.IpSecManager;
41 import android.net.IpSecSpiResponse;
42 import android.net.IpSecTransform;
43 import android.net.IpSecUdpEncapResponse;
44 import android.os.Binder;
45 import android.os.ParcelFileDescriptor;
46 import android.os.Process;
47 import android.support.test.filters.SmallTest;
48 import android.support.test.runner.AndroidJUnit4;
49 import android.system.ErrnoException;
50 import android.system.Os;
51 import android.system.StructStat;
52 
53 import dalvik.system.SocketTagger;
54 
55 import java.io.FileDescriptor;
56 import java.net.InetAddress;
57 import java.net.ServerSocket;
58 import java.net.Socket;
59 import java.net.UnknownHostException;
60 import java.util.ArrayList;
61 import java.util.List;
62 
63 import org.junit.Before;
64 import org.junit.Test;
65 import org.junit.runner.RunWith;
66 import org.mockito.ArgumentMatcher;
67 
68 /** Unit tests for {@link IpSecService}. */
69 @SmallTest
70 @RunWith(AndroidJUnit4.class)
71 public class IpSecServiceTest {
72 
73     private static final int DROID_SPI = 0xD1201D;
74     private static final int MAX_NUM_ENCAP_SOCKETS = 100;
75     private static final int MAX_NUM_SPIS = 100;
76     private static final int TEST_UDP_ENCAP_INVALID_PORT = 100;
77     private static final int TEST_UDP_ENCAP_PORT_OUT_RANGE = 100000;
78 
79     private static final InetAddress INADDR_ANY;
80 
81     private static final byte[] AEAD_KEY = {
82         0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
83         0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
84         0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
85         0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F,
86         0x73, 0x61, 0x6C, 0x74
87     };
88     private static final byte[] CRYPT_KEY = {
89         0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
90         0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
91         0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
92         0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F
93     };
94     private static final byte[] AUTH_KEY = {
95         0x7A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
96         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F,
97         0x7A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
98         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F
99     };
100 
101     private static final IpSecAlgorithm AUTH_ALGO =
102             new IpSecAlgorithm(IpSecAlgorithm.AUTH_HMAC_SHA256, AUTH_KEY, AUTH_KEY.length * 4);
103     private static final IpSecAlgorithm CRYPT_ALGO =
104             new IpSecAlgorithm(IpSecAlgorithm.CRYPT_AES_CBC, CRYPT_KEY);
105     private static final IpSecAlgorithm AEAD_ALGO =
106             new IpSecAlgorithm(IpSecAlgorithm.AUTH_CRYPT_AES_GCM, AEAD_KEY, 128);
107 
108     static {
109         try {
110             INADDR_ANY = InetAddress.getByAddress(new byte[] {0, 0, 0, 0});
111         } catch (UnknownHostException e) {
112             throw new RuntimeException(e);
113         }
114     }
115 
116     Context mMockContext;
117     INetd mMockNetd;
118     IpSecService.IpSecServiceConfiguration mMockIpSecSrvConfig;
119     IpSecService mIpSecService;
120 
121     @Before
setUp()122     public void setUp() throws Exception {
123         mMockContext = mock(Context.class);
124         mMockNetd = mock(INetd.class);
125         mMockIpSecSrvConfig = mock(IpSecService.IpSecServiceConfiguration.class);
126         mIpSecService = new IpSecService(mMockContext, mMockIpSecSrvConfig);
127 
128         // Injecting mock netd
129         when(mMockIpSecSrvConfig.getNetdInstance()).thenReturn(mMockNetd);
130     }
131 
132     @Test
testIpSecServiceCreate()133     public void testIpSecServiceCreate() throws InterruptedException {
134         IpSecService ipSecSrv = IpSecService.create(mMockContext);
135         assertNotNull(ipSecSrv);
136     }
137 
138     @Test
testReleaseInvalidSecurityParameterIndex()139     public void testReleaseInvalidSecurityParameterIndex() throws Exception {
140         try {
141             mIpSecService.releaseSecurityParameterIndex(1);
142             fail("IllegalArgumentException not thrown");
143         } catch (IllegalArgumentException e) {
144         }
145     }
146 
147     /** This function finds an available port */
findUnusedPort()148     int findUnusedPort() throws Exception {
149         // Get an available port.
150         ServerSocket s = new ServerSocket(0);
151         int port = s.getLocalPort();
152         s.close();
153         return port;
154     }
155 
156     @Test
testOpenAndCloseUdpEncapsulationSocket()157     public void testOpenAndCloseUdpEncapsulationSocket() throws Exception {
158         int localport = findUnusedPort();
159 
160         IpSecUdpEncapResponse udpEncapResp =
161                 mIpSecService.openUdpEncapsulationSocket(localport, new Binder());
162         assertNotNull(udpEncapResp);
163         assertEquals(IpSecManager.Status.OK, udpEncapResp.status);
164         assertEquals(localport, udpEncapResp.port);
165 
166         mIpSecService.closeUdpEncapsulationSocket(udpEncapResp.resourceId);
167         udpEncapResp.fileDescriptor.close();
168 
169         // Verify quota and RefcountedResource objects cleaned up
170         IpSecService.UserRecord userRecord =
171                 mIpSecService.mUserResourceTracker.getUserRecord(Os.getuid());
172         assertEquals(0, userRecord.mSocketQuotaTracker.mCurrent);
173         try {
174             userRecord.mEncapSocketRecords.getRefcountedResourceOrThrow(udpEncapResp.resourceId);
175             fail("Expected IllegalArgumentException on attempt to access deleted resource");
176         } catch (IllegalArgumentException expected) {
177 
178         }
179     }
180 
181     @Test
testUdpEncapsulationSocketBinderDeath()182     public void testUdpEncapsulationSocketBinderDeath() throws Exception {
183         IpSecUdpEncapResponse udpEncapResp =
184                 mIpSecService.openUdpEncapsulationSocket(0, new Binder());
185 
186         IpSecService.UserRecord userRecord =
187                 mIpSecService.mUserResourceTracker.getUserRecord(Os.getuid());
188         IpSecService.RefcountedResource refcountedRecord =
189                 userRecord.mEncapSocketRecords.getRefcountedResourceOrThrow(
190                         udpEncapResp.resourceId);
191 
192         refcountedRecord.binderDied();
193 
194         // Verify quota and RefcountedResource objects cleaned up
195         assertEquals(0, userRecord.mSocketQuotaTracker.mCurrent);
196         try {
197             userRecord.mEncapSocketRecords.getRefcountedResourceOrThrow(udpEncapResp.resourceId);
198             fail("Expected IllegalArgumentException on attempt to access deleted resource");
199         } catch (IllegalArgumentException expected) {
200 
201         }
202     }
203 
204     @Test
testOpenUdpEncapsulationSocketAfterClose()205     public void testOpenUdpEncapsulationSocketAfterClose() throws Exception {
206         int localport = findUnusedPort();
207         IpSecUdpEncapResponse udpEncapResp =
208                 mIpSecService.openUdpEncapsulationSocket(localport, new Binder());
209         assertNotNull(udpEncapResp);
210         assertEquals(IpSecManager.Status.OK, udpEncapResp.status);
211         assertEquals(localport, udpEncapResp.port);
212 
213         mIpSecService.closeUdpEncapsulationSocket(udpEncapResp.resourceId);
214         udpEncapResp.fileDescriptor.close();
215 
216         /** Check if localport is available. */
217         FileDescriptor newSocket = Os.socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
218         Os.bind(newSocket, INADDR_ANY, localport);
219         Os.close(newSocket);
220     }
221 
222     /**
223      * This function checks if the IpSecService holds the reserved port. If
224      * closeUdpEncapsulationSocket is not called, the socket cleanup should not be complete.
225      */
226     @Test
testUdpEncapPortNotReleased()227     public void testUdpEncapPortNotReleased() throws Exception {
228         int localport = findUnusedPort();
229         IpSecUdpEncapResponse udpEncapResp =
230                 mIpSecService.openUdpEncapsulationSocket(localport, new Binder());
231         assertNotNull(udpEncapResp);
232         assertEquals(IpSecManager.Status.OK, udpEncapResp.status);
233         assertEquals(localport, udpEncapResp.port);
234 
235         udpEncapResp.fileDescriptor.close();
236 
237         FileDescriptor newSocket = Os.socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
238         try {
239             Os.bind(newSocket, INADDR_ANY, localport);
240             fail("ErrnoException not thrown");
241         } catch (ErrnoException e) {
242             assertEquals(EADDRINUSE, e.errno);
243         }
244         mIpSecService.closeUdpEncapsulationSocket(udpEncapResp.resourceId);
245     }
246 
247     @Test
testOpenUdpEncapsulationSocketOnRandomPort()248     public void testOpenUdpEncapsulationSocketOnRandomPort() throws Exception {
249         IpSecUdpEncapResponse udpEncapResp =
250                 mIpSecService.openUdpEncapsulationSocket(0, new Binder());
251         assertNotNull(udpEncapResp);
252         assertEquals(IpSecManager.Status.OK, udpEncapResp.status);
253         assertNotEquals(0, udpEncapResp.port);
254         mIpSecService.closeUdpEncapsulationSocket(udpEncapResp.resourceId);
255         udpEncapResp.fileDescriptor.close();
256     }
257 
258     @Test
testOpenUdpEncapsulationSocketPortRange()259     public void testOpenUdpEncapsulationSocketPortRange() throws Exception {
260         try {
261             mIpSecService.openUdpEncapsulationSocket(TEST_UDP_ENCAP_INVALID_PORT, new Binder());
262             fail("IllegalArgumentException not thrown");
263         } catch (IllegalArgumentException e) {
264         }
265 
266         try {
267             mIpSecService.openUdpEncapsulationSocket(TEST_UDP_ENCAP_PORT_OUT_RANGE, new Binder());
268             fail("IllegalArgumentException not thrown");
269         } catch (IllegalArgumentException e) {
270         }
271     }
272 
273     @Test
testOpenUdpEncapsulationSocketTwice()274     public void testOpenUdpEncapsulationSocketTwice() throws Exception {
275         int localport = findUnusedPort();
276 
277         IpSecUdpEncapResponse udpEncapResp =
278                 mIpSecService.openUdpEncapsulationSocket(localport, new Binder());
279         assertNotNull(udpEncapResp);
280         assertEquals(IpSecManager.Status.OK, udpEncapResp.status);
281         assertEquals(localport, udpEncapResp.port);
282         mIpSecService.openUdpEncapsulationSocket(localport, new Binder());
283 
284         IpSecUdpEncapResponse testUdpEncapResp =
285                 mIpSecService.openUdpEncapsulationSocket(localport, new Binder());
286         assertEquals(IpSecManager.Status.RESOURCE_UNAVAILABLE, testUdpEncapResp.status);
287 
288         mIpSecService.closeUdpEncapsulationSocket(udpEncapResp.resourceId);
289         udpEncapResp.fileDescriptor.close();
290     }
291 
292     @Test
testCloseInvalidUdpEncapsulationSocket()293     public void testCloseInvalidUdpEncapsulationSocket() throws Exception {
294         try {
295             mIpSecService.closeUdpEncapsulationSocket(1);
296             fail("IllegalArgumentException not thrown");
297         } catch (IllegalArgumentException e) {
298         }
299     }
300 
301     @Test
testValidateAlgorithmsAuth()302     public void testValidateAlgorithmsAuth() {
303         // Validate that correct algorithm type succeeds
304         IpSecConfig config = new IpSecConfig();
305         config.setAuthentication(AUTH_ALGO);
306         mIpSecService.validateAlgorithms(config);
307 
308         // Validate that incorrect algorithm types fails
309         for (IpSecAlgorithm algo : new IpSecAlgorithm[] {CRYPT_ALGO, AEAD_ALGO}) {
310             try {
311                 config = new IpSecConfig();
312                 config.setAuthentication(algo);
313                 mIpSecService.validateAlgorithms(config);
314                 fail("Did not throw exception on invalid algorithm type");
315             } catch (IllegalArgumentException expected) {
316             }
317         }
318     }
319 
320     @Test
testValidateAlgorithmsCrypt()321     public void testValidateAlgorithmsCrypt() {
322         // Validate that correct algorithm type succeeds
323         IpSecConfig config = new IpSecConfig();
324         config.setEncryption(CRYPT_ALGO);
325         mIpSecService.validateAlgorithms(config);
326 
327         // Validate that incorrect algorithm types fails
328         for (IpSecAlgorithm algo : new IpSecAlgorithm[] {AUTH_ALGO, AEAD_ALGO}) {
329             try {
330                 config = new IpSecConfig();
331                 config.setEncryption(algo);
332                 mIpSecService.validateAlgorithms(config);
333                 fail("Did not throw exception on invalid algorithm type");
334             } catch (IllegalArgumentException expected) {
335             }
336         }
337     }
338 
339     @Test
testValidateAlgorithmsAead()340     public void testValidateAlgorithmsAead() {
341         // Validate that correct algorithm type succeeds
342         IpSecConfig config = new IpSecConfig();
343         config.setAuthenticatedEncryption(AEAD_ALGO);
344         mIpSecService.validateAlgorithms(config);
345 
346         // Validate that incorrect algorithm types fails
347         for (IpSecAlgorithm algo : new IpSecAlgorithm[] {AUTH_ALGO, CRYPT_ALGO}) {
348             try {
349                 config = new IpSecConfig();
350                 config.setAuthenticatedEncryption(algo);
351                 mIpSecService.validateAlgorithms(config);
352                 fail("Did not throw exception on invalid algorithm type");
353             } catch (IllegalArgumentException expected) {
354             }
355         }
356     }
357 
358     @Test
testValidateAlgorithmsAuthCrypt()359     public void testValidateAlgorithmsAuthCrypt() {
360         // Validate that correct algorithm type succeeds
361         IpSecConfig config = new IpSecConfig();
362         config.setAuthentication(AUTH_ALGO);
363         config.setEncryption(CRYPT_ALGO);
364         mIpSecService.validateAlgorithms(config);
365     }
366 
367     @Test
testValidateAlgorithmsNoAlgorithms()368     public void testValidateAlgorithmsNoAlgorithms() {
369         IpSecConfig config = new IpSecConfig();
370         try {
371             mIpSecService.validateAlgorithms(config);
372             fail("Expected exception; no algorithms specified");
373         } catch (IllegalArgumentException expected) {
374         }
375     }
376 
377     @Test
testValidateAlgorithmsAeadWithAuth()378     public void testValidateAlgorithmsAeadWithAuth() {
379         IpSecConfig config = new IpSecConfig();
380         config.setAuthenticatedEncryption(AEAD_ALGO);
381         config.setAuthentication(AUTH_ALGO);
382         try {
383             mIpSecService.validateAlgorithms(config);
384             fail("Expected exception; both AEAD and auth algorithm specified");
385         } catch (IllegalArgumentException expected) {
386         }
387     }
388 
389     @Test
testValidateAlgorithmsAeadWithCrypt()390     public void testValidateAlgorithmsAeadWithCrypt() {
391         IpSecConfig config = new IpSecConfig();
392         config.setAuthenticatedEncryption(AEAD_ALGO);
393         config.setEncryption(CRYPT_ALGO);
394         try {
395             mIpSecService.validateAlgorithms(config);
396             fail("Expected exception; both AEAD and crypt algorithm specified");
397         } catch (IllegalArgumentException expected) {
398         }
399     }
400 
401     @Test
testValidateAlgorithmsAeadWithAuthAndCrypt()402     public void testValidateAlgorithmsAeadWithAuthAndCrypt() {
403         IpSecConfig config = new IpSecConfig();
404         config.setAuthenticatedEncryption(AEAD_ALGO);
405         config.setAuthentication(AUTH_ALGO);
406         config.setEncryption(CRYPT_ALGO);
407         try {
408             mIpSecService.validateAlgorithms(config);
409             fail("Expected exception; AEAD, auth and crypt algorithm specified");
410         } catch (IllegalArgumentException expected) {
411         }
412     }
413 
414     @Test
testDeleteInvalidTransform()415     public void testDeleteInvalidTransform() throws Exception {
416         try {
417             mIpSecService.deleteTransform(1);
418             fail("IllegalArgumentException not thrown");
419         } catch (IllegalArgumentException e) {
420         }
421     }
422 
423     @Test
testRemoveTransportModeTransform()424     public void testRemoveTransportModeTransform() throws Exception {
425         ParcelFileDescriptor pfd = ParcelFileDescriptor.fromSocket(new Socket());
426         mIpSecService.removeTransportModeTransforms(pfd);
427 
428         verify(mMockNetd).ipSecRemoveTransportModeTransform(pfd.getFileDescriptor());
429     }
430 
431     @Test
testValidateIpAddresses()432     public void testValidateIpAddresses() throws Exception {
433         String[] invalidAddresses =
434                 new String[] {"www.google.com", "::", "2001::/64", "0.0.0.0", ""};
435         for (String address : invalidAddresses) {
436             try {
437                 IpSecSpiResponse spiResp =
438                         mIpSecService.allocateSecurityParameterIndex(
439                                 address, DROID_SPI, new Binder());
440                 fail("Invalid address was passed through IpSecService validation: " + address);
441             } catch (IllegalArgumentException e) {
442             } catch (Exception e) {
443                 fail(
444                         "Invalid InetAddress was not caught in validation: "
445                                 + address
446                                 + ", Exception: "
447                                 + e);
448             }
449         }
450     }
451 
452     /**
453      * This function checks if the number of encap UDP socket that one UID can reserve has a
454      * reasonable limit.
455      */
456     @Test
testSocketResourceTrackerLimitation()457     public void testSocketResourceTrackerLimitation() throws Exception {
458         List<IpSecUdpEncapResponse> openUdpEncapSockets = new ArrayList<IpSecUdpEncapResponse>();
459         // Reserve sockets until it fails.
460         for (int i = 0; i < MAX_NUM_ENCAP_SOCKETS; i++) {
461             IpSecUdpEncapResponse newUdpEncapSocket =
462                     mIpSecService.openUdpEncapsulationSocket(0, new Binder());
463             assertNotNull(newUdpEncapSocket);
464             if (IpSecManager.Status.OK != newUdpEncapSocket.status) {
465                 break;
466             }
467             openUdpEncapSockets.add(newUdpEncapSocket);
468         }
469         // Assert that the total sockets quota has a reasonable limit.
470         assertTrue("No UDP encap socket was open", !openUdpEncapSockets.isEmpty());
471         assertTrue(
472                 "Number of open UDP encap sockets is out of bound",
473                 openUdpEncapSockets.size() < MAX_NUM_ENCAP_SOCKETS);
474 
475         // Try to reserve one more UDP encapsulation socket, and should fail.
476         IpSecUdpEncapResponse extraUdpEncapSocket =
477                 mIpSecService.openUdpEncapsulationSocket(0, new Binder());
478         assertNotNull(extraUdpEncapSocket);
479         assertEquals(IpSecManager.Status.RESOURCE_UNAVAILABLE, extraUdpEncapSocket.status);
480 
481         // Close one of the open UDP encapsulation sockets.
482         mIpSecService.closeUdpEncapsulationSocket(openUdpEncapSockets.get(0).resourceId);
483         openUdpEncapSockets.get(0).fileDescriptor.close();
484         openUdpEncapSockets.remove(0);
485 
486         // Try to reserve one more UDP encapsulation socket, and should be successful.
487         extraUdpEncapSocket = mIpSecService.openUdpEncapsulationSocket(0, new Binder());
488         assertNotNull(extraUdpEncapSocket);
489         assertEquals(IpSecManager.Status.OK, extraUdpEncapSocket.status);
490         openUdpEncapSockets.add(extraUdpEncapSocket);
491 
492         // Close open UDP sockets.
493         for (IpSecUdpEncapResponse openSocket : openUdpEncapSockets) {
494             mIpSecService.closeUdpEncapsulationSocket(openSocket.resourceId);
495             openSocket.fileDescriptor.close();
496         }
497     }
498 
499     /**
500      * This function checks if the number of SPI that one UID can reserve has a reasonable limit.
501      * This test does not test for both address families or duplicate SPIs because resource tracking
502      * code does not depend on them.
503      */
504     @Test
testSpiResourceTrackerLimitation()505     public void testSpiResourceTrackerLimitation() throws Exception {
506         List<IpSecSpiResponse> reservedSpis = new ArrayList<IpSecSpiResponse>();
507         // Return the same SPI for all SPI allocation since IpSecService only
508         // tracks the resource ID.
509         when(mMockNetd.ipSecAllocateSpi(
510                         anyInt(),
511                         anyString(),
512                         eq(InetAddress.getLoopbackAddress().getHostAddress()),
513                         anyInt()))
514                 .thenReturn(DROID_SPI);
515         // Reserve spis until it fails.
516         for (int i = 0; i < MAX_NUM_SPIS; i++) {
517             IpSecSpiResponse newSpi =
518                     mIpSecService.allocateSecurityParameterIndex(
519                             InetAddress.getLoopbackAddress().getHostAddress(),
520                             DROID_SPI + i,
521                             new Binder());
522             assertNotNull(newSpi);
523             if (IpSecManager.Status.OK != newSpi.status) {
524                 break;
525             }
526             reservedSpis.add(newSpi);
527         }
528         // Assert that the SPI quota has a reasonable limit.
529         assertTrue(reservedSpis.size() > 0 && reservedSpis.size() < MAX_NUM_SPIS);
530 
531         // Try to reserve one more SPI, and should fail.
532         IpSecSpiResponse extraSpi =
533                 mIpSecService.allocateSecurityParameterIndex(
534                         InetAddress.getLoopbackAddress().getHostAddress(),
535                         DROID_SPI + MAX_NUM_SPIS,
536                         new Binder());
537         assertNotNull(extraSpi);
538         assertEquals(IpSecManager.Status.RESOURCE_UNAVAILABLE, extraSpi.status);
539 
540         // Release one reserved spi.
541         mIpSecService.releaseSecurityParameterIndex(reservedSpis.get(0).resourceId);
542         reservedSpis.remove(0);
543 
544         // Should successfully reserve one more spi.
545         extraSpi =
546                 mIpSecService.allocateSecurityParameterIndex(
547                         InetAddress.getLoopbackAddress().getHostAddress(),
548                         DROID_SPI + MAX_NUM_SPIS,
549                         new Binder());
550         assertNotNull(extraSpi);
551         assertEquals(IpSecManager.Status.OK, extraSpi.status);
552 
553         // Release reserved SPIs.
554         for (IpSecSpiResponse spiResp : reservedSpis) {
555             mIpSecService.releaseSecurityParameterIndex(spiResp.resourceId);
556         }
557     }
558 
559     @Test
560     public void testUidFdtagger() throws Exception {
561         SocketTagger actualSocketTagger = SocketTagger.get();
562 
563         try {
564             FileDescriptor sockFd = Os.socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
565 
566             // Has to be done after socket creation because BlockGuardOS calls tag on new sockets
567             SocketTagger mockSocketTagger = mock(SocketTagger.class);
568             SocketTagger.set(mockSocketTagger);
569 
570             mIpSecService.mUidFdTagger.tag(sockFd, Process.LAST_APPLICATION_UID);
571             verify(mockSocketTagger).tag(eq(sockFd));
572         } finally {
573             SocketTagger.set(actualSocketTagger);
574         }
575     }
576 
577     /**
578      * Checks if two file descriptors point to the same file.
579      *
580      * <p>According to stat.h documentation, the correct way to check for equivalent or duplicated
581      * file descriptors is to check their inode and device. These two entries uniquely identify any
582      * file.
583      */
584     private boolean fileDescriptorsEqual(FileDescriptor fd1, FileDescriptor fd2) {
585         try {
586             StructStat fd1Stat = Os.fstat(fd1);
587             StructStat fd2Stat = Os.fstat(fd2);
588 
589             return fd1Stat.st_ino == fd2Stat.st_ino && fd1Stat.st_dev == fd2Stat.st_dev;
590         } catch (ErrnoException e) {
591             return false;
592         }
593     }
594 
595     @Test
596     public void testOpenUdpEncapSocketTagsSocket() throws Exception {
597         IpSecService.UidFdTagger mockTagger = mock(IpSecService.UidFdTagger.class);
598         IpSecService testIpSecService =
599                 new IpSecService(mMockContext, mMockIpSecSrvConfig, mockTagger);
600 
601         IpSecUdpEncapResponse udpEncapResp =
602                 testIpSecService.openUdpEncapsulationSocket(0, new Binder());
603         assertNotNull(udpEncapResp);
604         assertEquals(IpSecManager.Status.OK, udpEncapResp.status);
605 
606         FileDescriptor sockFd = udpEncapResp.fileDescriptor.getFileDescriptor();
607         ArgumentMatcher<FileDescriptor> fdMatcher =
608                 (argFd) -> {
609                     return fileDescriptorsEqual(sockFd, argFd);
610                 };
611         verify(mockTagger).tag(argThat(fdMatcher), eq(Os.getuid()));
612 
613         testIpSecService.closeUdpEncapsulationSocket(udpEncapResp.resourceId);
614         udpEncapResp.fileDescriptor.close();
615     }
616 
617     @Test
testOpenUdpEncapsulationSocketCallsSetEncapSocketOwner()618     public void testOpenUdpEncapsulationSocketCallsSetEncapSocketOwner() throws Exception {
619         IpSecUdpEncapResponse udpEncapResp =
620                 mIpSecService.openUdpEncapsulationSocket(0, new Binder());
621 
622         FileDescriptor sockFd = udpEncapResp.fileDescriptor.getFileDescriptor();
623         ArgumentMatcher<FileDescriptor> fdMatcher = (arg) -> {
624                     try {
625                         StructStat sockStat = Os.fstat(sockFd);
626                         StructStat argStat = Os.fstat(arg);
627 
628                         return sockStat.st_ino == argStat.st_ino
629                                 && sockStat.st_dev == argStat.st_dev;
630                     } catch (ErrnoException e) {
631                         return false;
632                     }
633                 };
634 
635         verify(mMockNetd).ipSecSetEncapSocketOwner(argThat(fdMatcher), eq(Os.getuid()));
636         mIpSecService.closeUdpEncapsulationSocket(udpEncapResp.resourceId);
637     }
638 
639     @Test
testReserveNetId()640     public void testReserveNetId() {
641         int start = mIpSecService.TUN_INTF_NETID_START;
642         for (int i = 0; i < mIpSecService.TUN_INTF_NETID_RANGE; i++) {
643             assertEquals(start + i, mIpSecService.reserveNetId());
644         }
645 
646         // Check that resource exhaustion triggers an exception
647         try {
648             mIpSecService.reserveNetId();
649             fail("Did not throw error for all netIds reserved");
650         } catch (IllegalStateException expected) {
651         }
652 
653         // Now release one and try again
654         int releasedNetId =
655                 mIpSecService.TUN_INTF_NETID_START + mIpSecService.TUN_INTF_NETID_RANGE / 2;
656         mIpSecService.releaseNetId(releasedNetId);
657         assertEquals(releasedNetId, mIpSecService.reserveNetId());
658     }
659 }
660