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