diff options
| -rw-r--r-- | core/java/android/net/IIpSecService.aidl | 5 | ||||
| -rw-r--r-- | core/java/android/net/IpSecManager.java | 16 | ||||
| -rw-r--r-- | services/core/java/com/android/server/IpSecService.java | 60 | ||||
| -rw-r--r-- | tests/net/java/android/net/IpSecManagerTest.java | 68 | ||||
| -rw-r--r-- | tests/net/java/com/android/server/IpSecServiceParameterizedTest.java | 115 |
5 files changed, 225 insertions, 39 deletions
diff --git a/core/java/android/net/IIpSecService.aidl b/core/java/android/net/IIpSecService.aidl index 3ce0283d7f23..3a3ddcc48360 100644 --- a/core/java/android/net/IIpSecService.aidl +++ b/core/java/android/net/IIpSecService.aidl @@ -16,6 +16,7 @@ package android.net; +import android.net.LinkAddress; import android.net.Network; import android.net.IpSecConfig; import android.net.IpSecUdpEncapResponse; @@ -48,11 +49,11 @@ interface IIpSecService void addAddressToTunnelInterface( int tunnelResourceId, - String localAddr); + in LinkAddress localAddr); void removeAddressFromTunnelInterface( int tunnelResourceId, - String localAddr); + in LinkAddress localAddr); void deleteTunnelInterface(int resourceId); diff --git a/core/java/android/net/IpSecManager.java b/core/java/android/net/IpSecManager.java index fbf305633a71..4e1f83430abf 100644 --- a/core/java/android/net/IpSecManager.java +++ b/core/java/android/net/IpSecManager.java @@ -656,10 +656,14 @@ public final class IpSecManager { * tunneled traffic. * * @param address the local address for traffic inside the tunnel - * @throws IOException if the address could not be added * @hide */ - public void addAddress(LinkAddress address) throws IOException { + public void addAddress(LinkAddress address) { + try { + mService.addAddressToTunnelInterface(mResourceId, address); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } } /** @@ -668,10 +672,14 @@ public final class IpSecManager { * <p>Remove an address which was previously added to the IpSecTunnelInterface * * @param address to be removed - * @throws IOException if the address could not be removed * @hide */ - public void removeAddress(LinkAddress address) throws IOException { + public void removeAddress(LinkAddress address) { + try { + mService.removeAddressFromTunnelInterface(mResourceId, address); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } } private IpSecTunnelInterface(@NonNull IIpSecService service, diff --git a/services/core/java/com/android/server/IpSecService.java b/services/core/java/com/android/server/IpSecService.java index 45a4dfb91bbf..45e9481c2255 100644 --- a/services/core/java/com/android/server/IpSecService.java +++ b/services/core/java/com/android/server/IpSecService.java @@ -36,6 +36,7 @@ import android.net.IpSecTransform; import android.net.IpSecTransformResponse; import android.net.IpSecTunnelInterfaceResponse; import android.net.IpSecUdpEncapResponse; +import android.net.LinkAddress; import android.net.Network; import android.net.NetworkUtils; import android.net.TrafficStats; @@ -618,10 +619,8 @@ public class IpSecService extends IIpSecService.Stub { spi, mConfig.getMarkValue(), mConfig.getMarkMask()); - } catch (ServiceSpecificException e) { - // FIXME: get the error code and throw is at an IOException from Errno Exception - } catch (RemoteException e) { - Log.e(TAG, "Failed to delete SA with ID: " + mResourceId); + } catch (RemoteException | ServiceSpecificException e) { + Log.e(TAG, "Failed to delete SA with ID: " + mResourceId, e); } getResourceTracker().give(); @@ -681,10 +680,8 @@ public class IpSecService extends IIpSecService.Stub { .getNetdInstance() .ipSecDeleteSecurityAssociation( mResourceId, mSourceAddress, mDestinationAddress, mSpi, 0, 0); - } catch (ServiceSpecificException e) { - // FIXME: get the error code and throw is at an IOException from Errno Exception - } catch (RemoteException e) { - Log.e(TAG, "Failed to delete SPI reservation with ID: " + mResourceId); + } catch (ServiceSpecificException | RemoteException e) { + Log.e(TAG, "Failed to delete SPI reservation with ID: " + mResourceId, e); } mSpi = IpSecManager.INVALID_SECURITY_PARAMETER_INDEX; @@ -829,15 +826,13 @@ public class IpSecService extends IIpSecService.Stub { 0, direction, wildcardAddr, wildcardAddr, mark, 0xffffffff); } } - } catch (ServiceSpecificException e) { - // FIXME: get the error code and throw is at an IOException from Errno Exception - } catch (RemoteException e) { + } catch (ServiceSpecificException | RemoteException e) { Log.e( TAG, "Failed to delete VTI with interface name: " + mInterfaceName + " and id: " - + mResourceId); + + mResourceId, e); } getResourceTracker().give(); @@ -1319,7 +1314,9 @@ public class IpSecService extends IIpSecService.Stub { * from multiple local IP addresses over the same tunnel. */ @Override - public synchronized void addAddressToTunnelInterface(int tunnelResourceId, String localAddr) { + public synchronized void addAddressToTunnelInterface( + int tunnelResourceId, LinkAddress localAddr) { + enforceNetworkStackPermission(); UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid()); // Get tunnelInterface record; if no such interface is found, will throw @@ -1327,8 +1324,21 @@ public class IpSecService extends IIpSecService.Stub { TunnelInterfaceRecord tunnelInterfaceInfo = userRecord.mTunnelInterfaceRecords.getResourceOrThrow(tunnelResourceId); - // TODO: Add calls to netd: - // Add address to TunnelInterface + try { + // We can assume general validity of the IP address, since we get them as a + // LinkAddress, which does some validation. + mSrvConfig + .getNetdInstance() + .interfaceAddAddress( + tunnelInterfaceInfo.mInterfaceName, + localAddr.getAddress().getHostAddress(), + localAddr.getPrefixLength()); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } catch (ServiceSpecificException e) { + // If we get here, one of the arguments provided was invalid. Wrap the SSE, and throw. + throw new IllegalArgumentException(e); + } } /** @@ -1337,7 +1347,8 @@ public class IpSecService extends IIpSecService.Stub { */ @Override public synchronized void removeAddressFromTunnelInterface( - int tunnelResourceId, String localAddr) { + int tunnelResourceId, LinkAddress localAddr) { + enforceNetworkStackPermission(); UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid()); // Get tunnelInterface record; if no such interface is found, will throw @@ -1345,8 +1356,21 @@ public class IpSecService extends IIpSecService.Stub { TunnelInterfaceRecord tunnelInterfaceInfo = userRecord.mTunnelInterfaceRecords.getResourceOrThrow(tunnelResourceId); - // TODO: Add calls to netd: - // Remove address from TunnelInterface + try { + // We can assume general validity of the IP address, since we get them as a + // LinkAddress, which does some validation. + mSrvConfig + .getNetdInstance() + .interfaceDelAddress( + tunnelInterfaceInfo.mInterfaceName, + localAddr.getAddress().getHostAddress(), + localAddr.getPrefixLength()); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } catch (ServiceSpecificException e) { + // If we get here, one of the arguments provided was invalid. Wrap the SSE, and throw. + throw new IllegalArgumentException(e); + } } /** diff --git a/tests/net/java/android/net/IpSecManagerTest.java b/tests/net/java/android/net/IpSecManagerTest.java index cc3366fbc832..0ca20dee427f 100644 --- a/tests/net/java/android/net/IpSecManagerTest.java +++ b/tests/net/java/android/net/IpSecManagerTest.java @@ -50,13 +50,18 @@ public class IpSecManagerTest { private static final int TEST_UDP_ENCAP_PORT = 34567; private static final int DROID_SPI = 0xD1201D; + private static final int DUMMY_RESOURCE_ID = 0x1234; private static final InetAddress GOOGLE_DNS_4; + private static final String VTI_INTF_NAME = "ipsec_test"; + private static final InetAddress VTI_LOCAL_ADDRESS; + private static final LinkAddress VTI_INNER_ADDRESS = new LinkAddress("10.0.1.1/24"); static { try { // Google Public DNS Addresses; GOOGLE_DNS_4 = InetAddress.getByName("8.8.8.8"); + VTI_LOCAL_ADDRESS = InetAddress.getByName("8.8.4.4"); } catch (UnknownHostException e) { throw new RuntimeException("Could not resolve DNS Addresses", e); } @@ -77,9 +82,8 @@ public class IpSecManagerTest { */ @Test public void testAllocSpi() throws Exception { - int resourceId = 1; IpSecSpiResponse spiResp = - new IpSecSpiResponse(IpSecManager.Status.OK, resourceId, DROID_SPI); + new IpSecSpiResponse(IpSecManager.Status.OK, DUMMY_RESOURCE_ID, DROID_SPI); when(mMockIpSecService.allocateSecurityParameterIndex( eq(GOOGLE_DNS_4.getHostAddress()), eq(DROID_SPI), @@ -92,14 +96,13 @@ public class IpSecManagerTest { droidSpi.close(); - verify(mMockIpSecService).releaseSecurityParameterIndex(resourceId); + verify(mMockIpSecService).releaseSecurityParameterIndex(DUMMY_RESOURCE_ID); } @Test public void testAllocRandomSpi() throws Exception { - int resourceId = 1; IpSecSpiResponse spiResp = - new IpSecSpiResponse(IpSecManager.Status.OK, resourceId, DROID_SPI); + new IpSecSpiResponse(IpSecManager.Status.OK, DUMMY_RESOURCE_ID, DROID_SPI); when(mMockIpSecService.allocateSecurityParameterIndex( eq(GOOGLE_DNS_4.getHostAddress()), eq(IpSecManager.INVALID_SECURITY_PARAMETER_INDEX), @@ -113,7 +116,7 @@ public class IpSecManagerTest { randomSpi.close(); - verify(mMockIpSecService).releaseSecurityParameterIndex(resourceId); + verify(mMockIpSecService).releaseSecurityParameterIndex(DUMMY_RESOURCE_ID); } /* @@ -165,11 +168,10 @@ public class IpSecManagerTest { @Test public void testOpenEncapsulationSocket() throws Exception { - int resourceId = 1; IpSecUdpEncapResponse udpEncapResp = new IpSecUdpEncapResponse( IpSecManager.Status.OK, - resourceId, + DUMMY_RESOURCE_ID, TEST_UDP_ENCAP_PORT, Os.socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)); when(mMockIpSecService.openUdpEncapsulationSocket(eq(TEST_UDP_ENCAP_PORT), anyObject())) @@ -182,16 +184,15 @@ public class IpSecManagerTest { encapSocket.close(); - verify(mMockIpSecService).closeUdpEncapsulationSocket(resourceId); + verify(mMockIpSecService).closeUdpEncapsulationSocket(DUMMY_RESOURCE_ID); } @Test public void testOpenEncapsulationSocketOnRandomPort() throws Exception { - int resourceId = 1; IpSecUdpEncapResponse udpEncapResp = new IpSecUdpEncapResponse( IpSecManager.Status.OK, - resourceId, + DUMMY_RESOURCE_ID, TEST_UDP_ENCAP_PORT, Os.socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)); @@ -206,7 +207,7 @@ public class IpSecManagerTest { encapSocket.close(); - verify(mMockIpSecService).closeUdpEncapsulationSocket(resourceId); + verify(mMockIpSecService).closeUdpEncapsulationSocket(DUMMY_RESOURCE_ID); } @Test @@ -219,4 +220,45 @@ public class IpSecManagerTest { } // TODO: add test when applicable transform builder interface is available -} + + private IpSecManager.IpSecTunnelInterface createAndValidateVti(int resourceId, String intfName) + throws Exception { + IpSecTunnelInterfaceResponse dummyResponse = + new IpSecTunnelInterfaceResponse(IpSecManager.Status.OK, resourceId, intfName); + when(mMockIpSecService.createTunnelInterface( + eq(VTI_LOCAL_ADDRESS.getHostAddress()), eq(GOOGLE_DNS_4.getHostAddress()), + anyObject(), anyObject())) + .thenReturn(dummyResponse); + + IpSecManager.IpSecTunnelInterface tunnelIntf = mIpSecManager.createIpSecTunnelInterface( + VTI_LOCAL_ADDRESS, GOOGLE_DNS_4, mock(Network.class)); + + assertNotNull(tunnelIntf); + return tunnelIntf; + } + + @Test + public void testCreateVti() throws Exception { + IpSecManager.IpSecTunnelInterface tunnelIntf = + createAndValidateVti(DUMMY_RESOURCE_ID, VTI_INTF_NAME); + + assertEquals(VTI_INTF_NAME, tunnelIntf.getInterfaceName()); + + tunnelIntf.close(); + verify(mMockIpSecService).deleteTunnelInterface(eq(DUMMY_RESOURCE_ID)); + } + + @Test + public void testAddRemoveAddressesFromVti() throws Exception { + IpSecManager.IpSecTunnelInterface tunnelIntf = + createAndValidateVti(DUMMY_RESOURCE_ID, VTI_INTF_NAME); + + tunnelIntf.addAddress(VTI_INNER_ADDRESS); + verify(mMockIpSecService) + .addAddressToTunnelInterface(eq(DUMMY_RESOURCE_ID), eq(VTI_INNER_ADDRESS)); + + tunnelIntf.removeAddress(VTI_INNER_ADDRESS); + verify(mMockIpSecService) + .addAddressToTunnelInterface(eq(DUMMY_RESOURCE_ID), eq(VTI_INNER_ADDRESS)); + } +}
\ No newline at end of file diff --git a/tests/net/java/com/android/server/IpSecServiceParameterizedTest.java b/tests/net/java/com/android/server/IpSecServiceParameterizedTest.java index 3e1ff6dd5f32..a5c55e8d844e 100644 --- a/tests/net/java/com/android/server/IpSecServiceParameterizedTest.java +++ b/tests/net/java/com/android/server/IpSecServiceParameterizedTest.java @@ -17,6 +17,7 @@ package com.android.server; 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.anyString; @@ -32,6 +33,9 @@ import android.net.IpSecConfig; import android.net.IpSecManager; import android.net.IpSecSpiResponse; import android.net.IpSecTransformResponse; +import android.net.IpSecTunnelInterfaceResponse; +import android.net.LinkAddress; +import android.net.Network; import android.net.NetworkUtils; import android.os.Binder; import android.os.ParcelFileDescriptor; @@ -56,10 +60,15 @@ public class IpSecServiceParameterizedTest { private final String mDestinationAddr; private final String mSourceAddr; + private final LinkAddress mLocalInnerAddress; @Parameterized.Parameters public static Collection ipSecConfigs() { - return Arrays.asList(new Object[][] {{"1.2.3.4", "8.8.4.4"}, {"2601::2", "2601::10"}}); + return Arrays.asList( + new Object[][] { + {"1.2.3.4", "8.8.4.4", "10.0.1.1/24"}, + {"2601::2", "2601::10", "2001:db8::1/64"} + }); } private static final byte[] AEAD_KEY = { @@ -86,6 +95,7 @@ public class IpSecServiceParameterizedTest { INetd mMockNetd; IpSecService.IpSecServiceConfiguration mMockIpSecSrvConfig; IpSecService mIpSecService; + Network fakeNetwork = new Network(0xAB); private static final IpSecAlgorithm AUTH_ALGO = new IpSecAlgorithm(IpSecAlgorithm.AUTH_HMAC_SHA256, AUTH_KEY, AUTH_KEY.length * 4); @@ -94,9 +104,11 @@ public class IpSecServiceParameterizedTest { private static final IpSecAlgorithm AEAD_ALGO = new IpSecAlgorithm(IpSecAlgorithm.AUTH_CRYPT_AES_GCM, AEAD_KEY, 128); - public IpSecServiceParameterizedTest(String sourceAddr, String destAddr) { + public IpSecServiceParameterizedTest( + String sourceAddr, String destAddr, String localInnerAddr) { mSourceAddr = sourceAddr; mDestinationAddr = destAddr; + mLocalInnerAddress = new LinkAddress(localInnerAddr); } @Before @@ -406,4 +418,103 @@ public class IpSecServiceParameterizedTest { verify(mMockNetd).ipSecRemoveTransportModeTransform(pfd.getFileDescriptor()); } + + private IpSecTunnelInterfaceResponse createAndValidateTunnel( + String localAddr, String remoteAddr) { + IpSecTunnelInterfaceResponse createTunnelResp = + mIpSecService.createTunnelInterface( + mSourceAddr, mDestinationAddr, fakeNetwork, new Binder()); + + assertNotNull(createTunnelResp); + assertEquals(IpSecManager.Status.OK, createTunnelResp.status); + return createTunnelResp; + } + + @Test + public void testCreateTunnelInterface() throws Exception { + IpSecTunnelInterfaceResponse createTunnelResp = + createAndValidateTunnel(mSourceAddr, mDestinationAddr); + + // Check that we have stored the tracking object, and retrieve it + IpSecService.UserRecord userRecord = + mIpSecService.mUserResourceTracker.getUserRecord(Os.getuid()); + IpSecService.RefcountedResource refcountedRecord = + userRecord.mTunnelInterfaceRecords.getRefcountedResourceOrThrow( + createTunnelResp.resourceId); + + assertEquals(1, userRecord.mTunnelQuotaTracker.mCurrent); + verify(mMockNetd) + .addVirtualTunnelInterface( + eq(createTunnelResp.interfaceName), + eq(mSourceAddr), + eq(mDestinationAddr), + anyInt(), + anyInt()); + } + + @Test + public void testDeleteTunnelInterface() throws Exception { + IpSecTunnelInterfaceResponse createTunnelResp = + createAndValidateTunnel(mSourceAddr, mDestinationAddr); + + IpSecService.UserRecord userRecord = + mIpSecService.mUserResourceTracker.getUserRecord(Os.getuid()); + + mIpSecService.deleteTunnelInterface(createTunnelResp.resourceId); + + // Verify quota and RefcountedResource objects cleaned up + assertEquals(0, userRecord.mTunnelQuotaTracker.mCurrent); + verify(mMockNetd).removeVirtualTunnelInterface(eq(createTunnelResp.interfaceName)); + try { + userRecord.mTunnelInterfaceRecords.getRefcountedResourceOrThrow( + createTunnelResp.resourceId); + fail("Expected IllegalArgumentException on attempt to access deleted resource"); + } catch (IllegalArgumentException expected) { + } + } + + @Test + public void testTunnelInterfaceBinderDeath() throws Exception { + IpSecTunnelInterfaceResponse createTunnelResp = + createAndValidateTunnel(mSourceAddr, mDestinationAddr); + + IpSecService.UserRecord userRecord = + mIpSecService.mUserResourceTracker.getUserRecord(Os.getuid()); + IpSecService.RefcountedResource refcountedRecord = + userRecord.mTunnelInterfaceRecords.getRefcountedResourceOrThrow( + createTunnelResp.resourceId); + + refcountedRecord.binderDied(); + + // Verify quota and RefcountedResource objects cleaned up + assertEquals(0, userRecord.mTunnelQuotaTracker.mCurrent); + verify(mMockNetd).removeVirtualTunnelInterface(eq(createTunnelResp.interfaceName)); + try { + userRecord.mTunnelInterfaceRecords.getRefcountedResourceOrThrow( + createTunnelResp.resourceId); + fail("Expected IllegalArgumentException on attempt to access deleted resource"); + } catch (IllegalArgumentException expected) { + } + } + + @Test + public void testAddRemoveAddressFromTunnelInterface() throws Exception { + IpSecTunnelInterfaceResponse createTunnelResp = + createAndValidateTunnel(mSourceAddr, mDestinationAddr); + + mIpSecService.addAddressToTunnelInterface(createTunnelResp.resourceId, mLocalInnerAddress); + verify(mMockNetd) + .interfaceAddAddress( + eq(createTunnelResp.interfaceName), + eq(mLocalInnerAddress.getAddress().getHostAddress()), + eq(mLocalInnerAddress.getPrefixLength())); + + mIpSecService.removeAddressFromTunnelInterface( + createTunnelResp.resourceId, mLocalInnerAddress); + verify(mMockNetd) + .interfaceDelAddress( + eq(createTunnelResp.interfaceName), + eq(mLocalInnerAddress.getAddress().getHostAddress()), + eq(mLocalInnerAddress.getPrefixLength())); + } } |