diff options
| -rw-r--r-- | core/java/android/net/IpSecTransform.java | 10 | ||||
| -rw-r--r-- | core/jni/android_os_HwBinder.cpp | 3 | ||||
| -rw-r--r-- | services/core/java/com/android/server/IpSecService.java | 63 | ||||
| -rw-r--r-- | tests/net/java/android/net/IpSecManagerTest.java | 225 | ||||
| -rw-r--r-- | tests/net/java/android/net/ip/IpManagerTest.java | 2 | ||||
| -rw-r--r-- | tests/net/java/com/android/server/IpSecServiceTest.java | 435 |
6 files changed, 716 insertions, 22 deletions
diff --git a/core/java/android/net/IpSecTransform.java b/core/java/android/net/IpSecTransform.java index e65f534f3dc6..8e3a61297078 100644 --- a/core/java/android/net/IpSecTransform.java +++ b/core/java/android/net/IpSecTransform.java @@ -26,6 +26,7 @@ import android.os.IBinder; import android.os.RemoteException; import android.os.ServiceManager; import android.util.Log; +import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.Preconditions; import dalvik.system.CloseGuard; import java.io.IOException; @@ -487,5 +488,14 @@ public final class IpSecTransform implements AutoCloseable { mContext = context; mConfig = new IpSecConfig(); } + + /** + * Return an {@link IpSecConfig} object for testing purposes. + * @hide + */ + @VisibleForTesting + public IpSecConfig getIpSecConfig() { + return mConfig; + } } } diff --git a/core/jni/android_os_HwBinder.cpp b/core/jni/android_os_HwBinder.cpp index 81063204447f..1b14d2828702 100644 --- a/core/jni/android_os_HwBinder.cpp +++ b/core/jni/android_os_HwBinder.cpp @@ -383,8 +383,7 @@ static jobject JHwBinder_native_getService( return NULL; } - sp<hardware::IBinder> service = hardware::toBinder< - hidl::base::V1_0::IBase, hidl::base::V1_0::BpHwBase>(ret); + sp<hardware::IBinder> service = hardware::toBinder<hidl::base::V1_0::IBase>(ret); if (service == NULL) { signalExceptionForError(env, NAME_NOT_FOUND); diff --git a/services/core/java/com/android/server/IpSecService.java b/services/core/java/com/android/server/IpSecService.java index ac5da9361c4d..f72cbc9d68af 100644 --- a/services/core/java/com/android/server/IpSecService.java +++ b/services/core/java/com/android/server/IpSecService.java @@ -46,6 +46,7 @@ import android.util.Log; import android.util.Slog; import android.util.SparseArray; import com.android.internal.annotations.GuardedBy; +import com.android.internal.annotations.VisibleForTesting; import java.io.FileDescriptor; import java.io.IOException; import java.io.PrintWriter; @@ -64,7 +65,7 @@ public class IpSecService extends IIpSecService.Stub { private static final int[] DIRECTIONS = new int[] {IpSecTransform.DIRECTION_OUT, IpSecTransform.DIRECTION_IN}; - private static final int NETD_FETCH_TIMEOUT = 5000; //ms + private static final int NETD_FETCH_TIMEOUT_MS = 5000; // ms private static final int MAX_PORT_BIND_ATTEMPTS = 10; private static final InetAddress INADDR_ANY; @@ -96,6 +97,24 @@ public class IpSecService extends IIpSecService.Stub { private final ManagedResourceArray<UdpSocketRecord> mUdpSocketRecords = new ManagedResourceArray<>(); + interface IpSecServiceConfiguration { + INetd getNetdInstance() throws RemoteException; + + static IpSecServiceConfiguration GETSRVINSTANCE = + new IpSecServiceConfiguration() { + @Override + public INetd getNetdInstance() throws RemoteException { + final INetd netd = NetdService.getInstance(); + if (netd == null) { + throw new RemoteException("Failed to Get Netd Instance"); + } + return netd; + } + }; + } + + private final IpSecServiceConfiguration mSrvConfig; + /** * The ManagedResource class provides a facility to cleanly and reliably release system * resources. It relies on two things: an IBinder that allows ManagedResource to automatically @@ -198,8 +217,7 @@ public class IpSecService extends IIpSecService.Stub { }; /** - * Minimal wrapper around SparseArray that performs ownership - * validation on element accesses. + * Minimal wrapper around SparseArray that performs ownership validation on element accesses. */ private class ManagedResourceArray<T extends ManagedResource> { SparseArray<T> mArray = new SparseArray<>(); @@ -264,7 +282,8 @@ public class IpSecService extends IIpSecService.Stub { for (int direction : DIRECTIONS) { int spi = mSpis[direction].getSpi(); try { - getNetdInstance() + mSrvConfig + .getNetdInstance() .ipSecDeleteSecurityAssociation( mResourceId, direction, @@ -328,7 +347,8 @@ public class IpSecService extends IIpSecService.Stub { } try { - getNetdInstance() + mSrvConfig + .getNetdInstance() .ipSecDeleteSecurityAssociation( mResourceId, mDirection, mLocalAddress, mRemoteAddress, mSpi); } catch (ServiceSpecificException e) { @@ -387,7 +407,7 @@ public class IpSecService extends IIpSecService.Stub { * @param context Binder context for this service */ private IpSecService(Context context) { - mContext = context; + this(context, IpSecServiceConfiguration.GETSRVINSTANCE); } static IpSecService create(Context context) throws InterruptedException { @@ -396,6 +416,13 @@ public class IpSecService extends IIpSecService.Stub { return service; } + /** @hide */ + @VisibleForTesting + public IpSecService(Context context, IpSecServiceConfiguration config) { + mContext = context; + mSrvConfig = config; + } + public void systemReady() { if (isNetdAlive()) { Slog.d(TAG, "IpSecService is ready"); @@ -410,23 +437,15 @@ public class IpSecService extends IIpSecService.Stub { @Override public void run() { synchronized (IpSecService.this) { - NetdService.get(NETD_FETCH_TIMEOUT); + NetdService.get(NETD_FETCH_TIMEOUT_MS); } } }.start(); } - INetd getNetdInstance() throws RemoteException { - final INetd netd = NetdService.getInstance(); - if (netd == null) { - throw new RemoteException("Failed to Get Netd Instance"); - } - return netd; - } - synchronized boolean isNetdAlive() { try { - final INetd netd = getNetdInstance(); + final INetd netd = mSrvConfig.getNetdInstance(); if (netd == null) { return false; } @@ -447,7 +466,8 @@ public class IpSecService extends IIpSecService.Stub { String localAddress = ""; try { spi = - getNetdInstance() + mSrvConfig + .getNetdInstance() .ipSecAllocateSpi( resourceId, direction, @@ -606,7 +626,7 @@ public class IpSecService extends IIpSecService.Stub { spis[direction] = mSpiRecords.get(c.getSpiResourceId(direction)); int spi = spis[direction].getSpi(); try { - getNetdInstance() + mSrvConfig.getNetdInstance() .ipSecAddSecurityAssociation( resourceId, c.getMode(), @@ -676,7 +696,8 @@ public class IpSecService extends IIpSecService.Stub { IpSecConfig c = info.getConfig(); try { for (int direction : DIRECTIONS) { - getNetdInstance() + mSrvConfig + .getNetdInstance() .ipSecApplyTransportModeTransform( socket.getFileDescriptor(), resourceId, @@ -704,7 +725,9 @@ public class IpSecService extends IIpSecService.Stub { public void removeTransportModeTransform(ParcelFileDescriptor socket, int resourceId) throws RemoteException { try { - getNetdInstance().ipSecRemoveTransportModeTransform(socket.getFileDescriptor()); + mSrvConfig + .getNetdInstance() + .ipSecRemoveTransportModeTransform(socket.getFileDescriptor()); } catch (ServiceSpecificException e) { // FIXME: get the error code and throw is at an IOException from Errno Exception } diff --git a/tests/net/java/android/net/IpSecManagerTest.java b/tests/net/java/android/net/IpSecManagerTest.java new file mode 100644 index 000000000000..9f31d27508b2 --- /dev/null +++ b/tests/net/java/android/net/IpSecManagerTest.java @@ -0,0 +1,225 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.net; + +import static android.system.OsConstants.AF_INET; +import static android.system.OsConstants.IPPROTO_UDP; +import static android.system.OsConstants.SOCK_DGRAM; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.fail; +import static org.mockito.Matchers.anyInt; +import static org.mockito.Matchers.anyObject; +import static org.mockito.Matchers.anyString; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.support.test.filters.SmallTest; +import android.system.Os; +import android.test.AndroidTestCase; +import com.android.server.IpSecService; +import java.net.InetAddress; +import java.net.UnknownHostException; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +/** Unit tests for {@link IpSecManager}. */ +@SmallTest +@RunWith(JUnit4.class) +public class IpSecManagerTest { + + private static final int TEST_UDP_ENCAP_PORT = 34567; + private static final int DROID_SPI = 0xD1201D; + + private static final InetAddress GOOGLE_DNS_4; + + static { + try { + // Google Public DNS Addresses; + GOOGLE_DNS_4 = InetAddress.getByName("8.8.8.8"); + } catch (UnknownHostException e) { + throw new RuntimeException("Could not resolve DNS Addresses", e); + } + } + + private IpSecService mMockIpSecService; + private IpSecManager mIpSecManager; + + @Before + public void setUp() throws Exception { + mMockIpSecService = mock(IpSecService.class); + mIpSecManager = new IpSecManager(mMockIpSecService); + } + + /* + * Allocate a specific SPI + * Close SPIs + */ + @Test + public void testAllocSpi() throws Exception { + int resourceId = 1; + IpSecSpiResponse spiResp = + new IpSecSpiResponse(IpSecManager.Status.OK, resourceId, DROID_SPI); + when(mMockIpSecService.reserveSecurityParameterIndex( + eq(IpSecTransform.DIRECTION_IN), + eq(GOOGLE_DNS_4.getHostAddress()), + eq(DROID_SPI), + anyObject())) + .thenReturn(spiResp); + + IpSecManager.SecurityParameterIndex droidSpi = + mIpSecManager.reserveSecurityParameterIndex( + IpSecTransform.DIRECTION_IN, GOOGLE_DNS_4, DROID_SPI); + assertEquals(DROID_SPI, droidSpi.getSpi()); + + droidSpi.close(); + + verify(mMockIpSecService).releaseSecurityParameterIndex(resourceId); + } + + @Test + public void testAllocRandomSpi() throws Exception { + int resourceId = 1; + IpSecSpiResponse spiResp = + new IpSecSpiResponse(IpSecManager.Status.OK, resourceId, DROID_SPI); + when(mMockIpSecService.reserveSecurityParameterIndex( + eq(IpSecTransform.DIRECTION_OUT), + eq(GOOGLE_DNS_4.getHostAddress()), + eq(IpSecManager.INVALID_SECURITY_PARAMETER_INDEX), + anyObject())) + .thenReturn(spiResp); + + IpSecManager.SecurityParameterIndex randomSpi = + mIpSecManager.reserveSecurityParameterIndex( + IpSecTransform.DIRECTION_OUT, GOOGLE_DNS_4); + + assertEquals(DROID_SPI, randomSpi.getSpi()); + + randomSpi.close(); + + verify(mMockIpSecService).releaseSecurityParameterIndex(resourceId); + } + + /* + * Throws resource unavailable exception + */ + @Test + public void testAllocSpiResUnavaiableExeption() throws Exception { + IpSecSpiResponse spiResp = + new IpSecSpiResponse(IpSecManager.Status.RESOURCE_UNAVAILABLE, 0, 0); + when(mMockIpSecService.reserveSecurityParameterIndex( + anyInt(), anyString(), anyInt(), anyObject())) + .thenReturn(spiResp); + + try { + mIpSecManager.reserveSecurityParameterIndex(IpSecTransform.DIRECTION_OUT, GOOGLE_DNS_4); + fail("ResourceUnavailableException was not thrown"); + } catch (IpSecManager.ResourceUnavailableException e) { + } + } + + /* + * Throws spi unavailable exception + */ + @Test + public void testAllocSpiSpiUnavaiableExeption() throws Exception { + IpSecSpiResponse spiResp = new IpSecSpiResponse(IpSecManager.Status.SPI_UNAVAILABLE, 0, 0); + when(mMockIpSecService.reserveSecurityParameterIndex( + anyInt(), anyString(), anyInt(), anyObject())) + .thenReturn(spiResp); + + try { + mIpSecManager.reserveSecurityParameterIndex(IpSecTransform.DIRECTION_OUT, GOOGLE_DNS_4); + fail("ResourceUnavailableException was not thrown"); + } catch (IpSecManager.ResourceUnavailableException e) { + } + } + + /* + * Should throw exception when request spi 0 in IpSecManager + */ + @Test + public void testRequestAllocInvalidSpi() throws Exception { + try { + mIpSecManager.reserveSecurityParameterIndex( + IpSecTransform.DIRECTION_OUT, GOOGLE_DNS_4, 0); + fail("Able to allocate invalid spi"); + } catch (IllegalArgumentException e) { + } + } + + @Test + public void testOpenEncapsulationSocket() throws Exception { + int resourceId = 1; + IpSecUdpEncapResponse udpEncapResp = + new IpSecUdpEncapResponse( + IpSecManager.Status.OK, + resourceId, + TEST_UDP_ENCAP_PORT, + Os.socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)); + when(mMockIpSecService.openUdpEncapsulationSocket(eq(TEST_UDP_ENCAP_PORT), anyObject())) + .thenReturn(udpEncapResp); + + IpSecManager.UdpEncapsulationSocket encapSocket = + mIpSecManager.openUdpEncapsulationSocket(TEST_UDP_ENCAP_PORT); + assertNotNull(encapSocket.getSocket()); + assertEquals(TEST_UDP_ENCAP_PORT, encapSocket.getPort()); + + encapSocket.close(); + + verify(mMockIpSecService).closeUdpEncapsulationSocket(resourceId); + } + + @Test + public void testOpenEncapsulationSocketOnRandomPort() throws Exception { + int resourceId = 1; + IpSecUdpEncapResponse udpEncapResp = + new IpSecUdpEncapResponse( + IpSecManager.Status.OK, + resourceId, + TEST_UDP_ENCAP_PORT, + Os.socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)); + + when(mMockIpSecService.openUdpEncapsulationSocket(eq(0), anyObject())) + .thenReturn(udpEncapResp); + + IpSecManager.UdpEncapsulationSocket encapSocket = + mIpSecManager.openUdpEncapsulationSocket(); + + assertNotNull(encapSocket.getSocket()); + assertEquals(TEST_UDP_ENCAP_PORT, encapSocket.getPort()); + + encapSocket.close(); + + verify(mMockIpSecService).closeUdpEncapsulationSocket(resourceId); + } + + @Test + public void testOpenEncapsulationSocketWithInvalidPort() throws Exception { + try { + mIpSecManager.openUdpEncapsulationSocket(IpSecManager.INVALID_SECURITY_PARAMETER_INDEX); + fail("IllegalArgumentException was not thrown"); + } catch (IllegalArgumentException e) { + } + } + + // TODO: add test when applicable transform builder interface is available +} diff --git a/tests/net/java/android/net/ip/IpManagerTest.java b/tests/net/java/android/net/ip/IpManagerTest.java index 867324d99920..dc77e22082ba 100644 --- a/tests/net/java/android/net/ip/IpManagerTest.java +++ b/tests/net/java/android/net/ip/IpManagerTest.java @@ -22,6 +22,7 @@ import static org.mockito.Mockito.any; import static org.mockito.Mockito.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; +import static org.mockito.Mockito.reset; import static org.mockito.Mockito.timeout; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; @@ -93,6 +94,7 @@ public class IpManagerTest { final IpManager ipm = new IpManager(mContext, ifname, mCb, mNMService); verify(mNMService, timeout(100).times(1)).disableIpv6(ifname); verify(mNMService, timeout(100).times(1)).clearInterfaceAddresses(ifname); + reset(mNMService); return ipm; } diff --git a/tests/net/java/com/android/server/IpSecServiceTest.java b/tests/net/java/com/android/server/IpSecServiceTest.java new file mode 100644 index 000000000000..23fee286b8e7 --- /dev/null +++ b/tests/net/java/com/android/server/IpSecServiceTest.java @@ -0,0 +1,435 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server; + +import static android.system.OsConstants.AF_INET; +import static android.system.OsConstants.EADDRINUSE; +import static android.system.OsConstants.IPPROTO_UDP; +import static android.system.OsConstants.SOCK_DGRAM; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.fail; +import static org.mockito.Matchers.anyInt; +import static org.mockito.Matchers.anyLong; +import static org.mockito.Matchers.anyObject; +import static org.mockito.Matchers.anyString; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.content.Context; +import android.net.INetd; +import android.net.IpSecAlgorithm; +import android.net.IpSecConfig; +import android.net.IpSecManager; +import android.net.IpSecSpiResponse; +import android.net.IpSecTransform; +import android.net.IpSecTransformResponse; +import android.net.IpSecUdpEncapResponse; +import android.os.Binder; +import android.os.ParcelFileDescriptor; +import android.support.test.filters.SmallTest; +import android.system.ErrnoException; +import android.system.Os; +import java.io.FileDescriptor; +import java.net.InetAddress; +import java.net.ServerSocket; +import java.net.Socket; +import java.net.UnknownHostException; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +/** Unit tests for {@link IpSecService}. */ +@SmallTest +@RunWith(JUnit4.class) +public class IpSecServiceTest { + + private static final int DROID_SPI = 0xD1201D; + private static final int DROID_SPI2 = DROID_SPI + 1; + private static final int TEST_UDP_ENCAP_INVALID_PORT = 100; + private static final int TEST_UDP_ENCAP_PORT_OUT_RANGE = 100000; + private static final int TEST_UDP_ENCAP_PORT = 34567; + + private static final String IPV4_LOOPBACK = "127.0.0.1"; + private static final String IPV4_ADDR = "192.168.0.2"; + + private static final InetAddress INADDR_ANY; + + static { + try { + INADDR_ANY = InetAddress.getByAddress(new byte[] {0, 0, 0, 0}); + } catch (UnknownHostException e) { + throw new RuntimeException(e); + } + } + + private static final int[] DIRECTIONS = + new int[] {IpSecTransform.DIRECTION_OUT, IpSecTransform.DIRECTION_IN}; + private static final byte[] CRYPT_KEY = { + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, + 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F + }; + private static final byte[] AUTH_KEY = { + 0x7A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F, + 0x7A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F + }; + + Context mMockContext; + INetd mMockNetd; + IpSecService.IpSecServiceConfiguration mMockIpSecSrvConfig; + IpSecService mIpSecService; + + @Before + public void setUp() throws Exception { + mMockContext = mock(Context.class); + mMockNetd = mock(INetd.class); + mMockIpSecSrvConfig = mock(IpSecService.IpSecServiceConfiguration.class); + mIpSecService = new IpSecService(mMockContext, mMockIpSecSrvConfig); + + // Injecting mock netd + when(mMockIpSecSrvConfig.getNetdInstance()).thenReturn(mMockNetd); + } + + @Test + public void testIpSecServiceCreate() throws InterruptedException { + IpSecService ipSecSrv = IpSecService.create(mMockContext); + assertNotNull(ipSecSrv); + } + + @Test + public void testIpSecServiceReserveSpi() throws Exception { + when(mMockNetd.ipSecAllocateSpi( + anyInt(), + eq(IpSecTransform.DIRECTION_OUT), + anyString(), + eq(IPV4_LOOPBACK), + eq(DROID_SPI))) + .thenReturn(DROID_SPI); + + IpSecSpiResponse spiResp = + mIpSecService.reserveSecurityParameterIndex( + IpSecTransform.DIRECTION_OUT, IPV4_LOOPBACK, DROID_SPI, new Binder()); + assertEquals(IpSecManager.Status.OK, spiResp.status); + assertEquals(DROID_SPI, spiResp.spi); + } + + @Test + public void testReleaseSecurityParameterIndex() throws Exception { + when(mMockNetd.ipSecAllocateSpi( + anyInt(), + eq(IpSecTransform.DIRECTION_OUT), + anyString(), + eq(IPV4_LOOPBACK), + eq(DROID_SPI))) + .thenReturn(DROID_SPI); + + IpSecSpiResponse spiResp = + mIpSecService.reserveSecurityParameterIndex( + IpSecTransform.DIRECTION_OUT, IPV4_LOOPBACK, DROID_SPI, new Binder()); + + mIpSecService.releaseSecurityParameterIndex(spiResp.resourceId); + + verify(mMockNetd) + .ipSecDeleteSecurityAssociation( + eq(spiResp.resourceId), anyInt(), anyString(), anyString(), eq(DROID_SPI)); + } + + @Test + public void testReleaseInvalidSecurityParameterIndex() throws Exception { + try { + mIpSecService.releaseSecurityParameterIndex(1); + fail("IllegalArgumentException not thrown"); + } catch (IllegalArgumentException e) { + } + } + + /** This function finds an available port */ + int findUnusedPort() throws Exception { + // Get an available port. + ServerSocket s = new ServerSocket(0); + int port = s.getLocalPort(); + s.close(); + return port; + } + + @Test + public void testOpenAndCloseUdpEncapsulationSocket() throws Exception { + int localport = findUnusedPort(); + + IpSecUdpEncapResponse udpEncapResp = + mIpSecService.openUdpEncapsulationSocket(localport, new Binder()); + assertNotNull(udpEncapResp); + assertEquals(IpSecManager.Status.OK, udpEncapResp.status); + assertEquals(localport, udpEncapResp.port); + + mIpSecService.closeUdpEncapsulationSocket(udpEncapResp.resourceId); + udpEncapResp.fileDescriptor.close(); + + // TODO: Added check for the resource tracker + } + + @Test + public void testOpenUdpEncapsulationSocketAfterClose() throws Exception { + int localport = findUnusedPort(); + IpSecUdpEncapResponse udpEncapResp = + mIpSecService.openUdpEncapsulationSocket(localport, new Binder()); + assertNotNull(udpEncapResp); + assertEquals(IpSecManager.Status.OK, udpEncapResp.status); + assertEquals(localport, udpEncapResp.port); + + mIpSecService.closeUdpEncapsulationSocket(udpEncapResp.resourceId); + udpEncapResp.fileDescriptor.close(); + + /** Check if localport is available. */ + FileDescriptor newSocket = Os.socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); + Os.bind(newSocket, INADDR_ANY, localport); + Os.close(newSocket); + } + + /** + * This function checks if the IpSecService holds the reserved port. If + * closeUdpEncapsulationSocket is not called, the socket cleanup should not be complete. + */ + @Test + public void testUdpEncapPortNotReleased() throws Exception { + int localport = findUnusedPort(); + IpSecUdpEncapResponse udpEncapResp = + mIpSecService.openUdpEncapsulationSocket(localport, new Binder()); + assertNotNull(udpEncapResp); + assertEquals(IpSecManager.Status.OK, udpEncapResp.status); + assertEquals(localport, udpEncapResp.port); + + udpEncapResp.fileDescriptor.close(); + + FileDescriptor newSocket = Os.socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); + try { + Os.bind(newSocket, INADDR_ANY, localport); + fail("ErrnoException not thrown"); + } catch (ErrnoException e) { + assertEquals(EADDRINUSE, e.errno); + } + mIpSecService.closeUdpEncapsulationSocket(udpEncapResp.resourceId); + } + + @Test + public void testOpenUdpEncapsulationSocketOnRandomPort() throws Exception { + IpSecUdpEncapResponse udpEncapResp = + mIpSecService.openUdpEncapsulationSocket(0, new Binder()); + assertNotNull(udpEncapResp); + assertEquals(IpSecManager.Status.OK, udpEncapResp.status); + mIpSecService.closeUdpEncapsulationSocket(udpEncapResp.resourceId); + udpEncapResp.fileDescriptor.close(); + } + + @Test + public void testOpenUdpEncapsulationSocketPortRange() throws Exception { + try { + mIpSecService.openUdpEncapsulationSocket(TEST_UDP_ENCAP_INVALID_PORT, new Binder()); + fail("IllegalArgumentException not thrown"); + } catch (IllegalArgumentException e) { + } + + try { + mIpSecService.openUdpEncapsulationSocket(TEST_UDP_ENCAP_PORT_OUT_RANGE, new Binder()); + fail("IllegalArgumentException not thrown"); + } catch (IllegalArgumentException e) { + } + } + + @Test + public void testOpenUdpEncapsulationSocketTwice() throws Exception { + int localport = findUnusedPort(); + + IpSecUdpEncapResponse udpEncapResp = + mIpSecService.openUdpEncapsulationSocket(localport, new Binder()); + assertNotNull(udpEncapResp); + assertEquals(IpSecManager.Status.OK, udpEncapResp.status); + assertEquals(localport, udpEncapResp.port); + mIpSecService.openUdpEncapsulationSocket(localport, new Binder()); + + IpSecUdpEncapResponse testUdpEncapResp = + mIpSecService.openUdpEncapsulationSocket(localport, new Binder()); + assertEquals(IpSecManager.Status.RESOURCE_UNAVAILABLE, testUdpEncapResp.status); + + mIpSecService.closeUdpEncapsulationSocket(udpEncapResp.resourceId); + udpEncapResp.fileDescriptor.close(); + } + + @Test + public void testCloseInvalidUdpEncapsulationSocket() throws Exception { + try { + mIpSecService.closeUdpEncapsulationSocket(1); + fail("IllegalArgumentException not thrown"); + } catch (IllegalArgumentException e) { + } + } + + IpSecConfig buildIpSecConfig() throws Exception { + IpSecManager ipSecManager = new IpSecManager(mIpSecService); + + // Mocking the netd to allocate SPI + when(mMockNetd.ipSecAllocateSpi(anyInt(), anyInt(), anyString(), anyString(), anyInt())) + .thenReturn(DROID_SPI) + .thenReturn(DROID_SPI2); + + IpSecAlgorithm encryptAlgo = new IpSecAlgorithm(IpSecAlgorithm.CRYPT_AES_CBC, CRYPT_KEY); + IpSecAlgorithm authAlgo = + new IpSecAlgorithm(IpSecAlgorithm.AUTH_HMAC_SHA256, AUTH_KEY, AUTH_KEY.length * 8); + + InetAddress localAddr = InetAddress.getByAddress(new byte[] {127, 0, 0, 1}); + + /** Allocate and add SPI records in the IpSecService through IpSecManager interface. */ + IpSecManager.SecurityParameterIndex outSpi = + ipSecManager.reserveSecurityParameterIndex(IpSecTransform.DIRECTION_OUT, localAddr); + IpSecManager.SecurityParameterIndex inSpi = + ipSecManager.reserveSecurityParameterIndex(IpSecTransform.DIRECTION_IN, localAddr); + + IpSecConfig ipSecConfig = + new IpSecTransform.Builder(mMockContext) + .setSpi(IpSecTransform.DIRECTION_OUT, outSpi) + .setSpi(IpSecTransform.DIRECTION_IN, inSpi) + .setEncryption(IpSecTransform.DIRECTION_OUT, encryptAlgo) + .setAuthentication(IpSecTransform.DIRECTION_OUT, authAlgo) + .setEncryption(IpSecTransform.DIRECTION_IN, encryptAlgo) + .setAuthentication(IpSecTransform.DIRECTION_IN, authAlgo) + .getIpSecConfig(); + return ipSecConfig; + } + + @Test + public void testCreateTransportModeTransform() throws Exception { + IpSecConfig ipSecConfig = buildIpSecConfig(); + + IpSecTransformResponse createTransformResp = + mIpSecService.createTransportModeTransform(ipSecConfig, new Binder()); + assertEquals(IpSecManager.Status.OK, createTransformResp.status); + + verify(mMockNetd) + .ipSecAddSecurityAssociation( + eq(createTransformResp.resourceId), + anyInt(), + eq(IpSecTransform.DIRECTION_OUT), + anyString(), + anyString(), + anyLong(), + eq(DROID_SPI), + eq(IpSecAlgorithm.AUTH_HMAC_SHA256), + eq(AUTH_KEY), + anyInt(), + eq(IpSecAlgorithm.CRYPT_AES_CBC), + eq(CRYPT_KEY), + anyInt(), + anyInt(), + anyInt(), + anyInt()); + verify(mMockNetd) + .ipSecAddSecurityAssociation( + eq(createTransformResp.resourceId), + anyInt(), + eq(IpSecTransform.DIRECTION_IN), + anyString(), + anyString(), + anyLong(), + eq(DROID_SPI2), + eq(IpSecAlgorithm.AUTH_HMAC_SHA256), + eq(AUTH_KEY), + anyInt(), + eq(IpSecAlgorithm.CRYPT_AES_CBC), + eq(CRYPT_KEY), + anyInt(), + anyInt(), + anyInt(), + anyInt()); + } + + @Test + public void testDeleteTransportModeTransform() throws Exception { + IpSecConfig ipSecConfig = buildIpSecConfig(); + + IpSecTransformResponse createTransformResp = + mIpSecService.createTransportModeTransform(ipSecConfig, new Binder()); + mIpSecService.deleteTransportModeTransform(createTransformResp.resourceId); + + verify(mMockNetd) + .ipSecDeleteSecurityAssociation( + eq(createTransformResp.resourceId), + eq(IpSecTransform.DIRECTION_OUT), + anyString(), + anyString(), + eq(DROID_SPI)); + verify(mMockNetd) + .ipSecDeleteSecurityAssociation( + eq(createTransformResp.resourceId), + eq(IpSecTransform.DIRECTION_IN), + anyString(), + anyString(), + eq(DROID_SPI2)); + } + + @Test + public void testDeleteInvalidTransportModeTransform() throws Exception { + try { + mIpSecService.deleteTransportModeTransform(1); + fail("IllegalArgumentException not thrown"); + } catch (IllegalArgumentException e) { + } + } + + @Test + public void testApplyTransportModeTransform() throws Exception { + IpSecConfig ipSecConfig = buildIpSecConfig(); + + IpSecTransformResponse createTransformResp = + mIpSecService.createTransportModeTransform(ipSecConfig, new Binder()); + ParcelFileDescriptor pfd = ParcelFileDescriptor.fromSocket(new Socket()); + + int resourceId = createTransformResp.resourceId; + mIpSecService.applyTransportModeTransform(pfd, resourceId); + + verify(mMockNetd) + .ipSecApplyTransportModeTransform( + eq(pfd.getFileDescriptor()), + eq(resourceId), + eq(IpSecTransform.DIRECTION_OUT), + anyString(), + anyString(), + eq(DROID_SPI)); + verify(mMockNetd) + .ipSecApplyTransportModeTransform( + eq(pfd.getFileDescriptor()), + eq(resourceId), + eq(IpSecTransform.DIRECTION_IN), + anyString(), + anyString(), + eq(DROID_SPI2)); + } + + @Test + public void testRemoveTransportModeTransform() throws Exception { + ParcelFileDescriptor pfd = ParcelFileDescriptor.fromSocket(new Socket()); + mIpSecService.removeTransportModeTransform(pfd, 1); + + verify(mMockNetd).ipSecRemoveTransportModeTransform(pfd.getFileDescriptor()); + } +} |