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