OSDN Git Service

Add tunnel-mode calls to netd in IpSecService
authorBenedict Wong <benedictwong@google.com>
Sat, 20 Jan 2018 01:36:02 +0000 (17:36 -0800)
committerBenedict Wong <benedictwong@google.com>
Wed, 24 Jan 2018 08:19:55 +0000 (08:19 +0000)
Adds calls to relevant netd methods in IpSecService, enabling Tunnel
mode functionality.

Bug: 63588681
Test: Compiles, passing CTS + unit tests
Change-Id: I6deb68584cddb03f21bd76370d4ef69cadc1bf16

services/core/java/com/android/server/IpSecService.java
tests/net/java/com/android/server/IpSecServiceParameterizedTest.java

index c2adbfa..fe4ac6d 100644 (file)
@@ -65,7 +65,6 @@ import java.net.InetAddress;
 import java.net.InetSocketAddress;
 import java.net.UnknownHostException;
 import java.util.ArrayList;
-import java.util.BitSet;
 import java.util.List;
 
 import libcore.io.IoUtils;
@@ -596,6 +595,10 @@ public class IpSecService extends IIpSecService.Stub {
             return mSpi;
         }
 
+        public EncapSocketRecord getSocketRecord() {
+            return mSocket;
+        }
+
         /** always guarded by IpSecService#this */
         @Override
         public void freeUnderlyingResources() {
@@ -806,9 +809,29 @@ public class IpSecService extends IIpSecService.Stub {
         /** always guarded by IpSecService#this */
         @Override
         public void freeUnderlyingResources() {
-            // TODO: Add calls to netd
+            // Calls to netd
             //       Teardown VTI
             //       Delete global policies
+            try {
+                mSrvConfig.getNetdInstance().removeVirtualTunnelInterface(mInterfaceName);
+
+                for (int direction : DIRECTIONS) {
+                    int mark = (direction == IpSecManager.DIRECTION_IN) ? mIkey : mOkey;
+                    mSrvConfig
+                            .getNetdInstance()
+                            .ipSecDeleteSecurityPolicy(
+                                    0, direction, mLocalAddress, mRemoteAddress, mark, 0xffffffff);
+                }
+            } 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 VTI with interface name: "
+                                + mInterfaceName
+                                + " and id: "
+                                + mResourceId);
+            }
 
             getResourceTracker().give();
             releaseNetId(mIkey);
@@ -1229,24 +1252,57 @@ public class IpSecService extends IIpSecService.Stub {
         final int okey = reserveNetId();
         String intfName = String.format("%s%d", TUNNEL_INTERFACE_PREFIX, resourceId);
 
-        // TODO: Add calls to netd:
-        //       Create VTI
-        //       Add inbound/outbound global policies
-        //              (use reqid = 0)
+        try {
+            // Calls to netd:
+            //       Create VTI
+            //       Add inbound/outbound global policies
+            //              (use reqid = 0)
+            mSrvConfig
+                    .getNetdInstance()
+                    .addVirtualTunnelInterface(intfName, localAddr, remoteAddr, ikey, okey);
 
-        userRecord.mTunnelInterfaceRecords.put(
-                resourceId,
-                new RefcountedResource<TunnelInterfaceRecord>(
-                        new TunnelInterfaceRecord(
-                                resourceId,
-                                intfName,
-                                underlyingNetwork,
-                                localAddr,
-                                remoteAddr,
-                                ikey,
-                                okey),
-                        binder));
-        return new IpSecTunnelInterfaceResponse(IpSecManager.Status.OK, resourceId, intfName);
+            for (int direction : DIRECTIONS) {
+                int mark = (direction == IpSecManager.DIRECTION_OUT) ? okey : ikey;
+
+                mSrvConfig
+                        .getNetdInstance()
+                        .ipSecAddSecurityPolicy(
+                                0, // Use 0 for reqId
+                                direction,
+                                "",
+                                "",
+                                0,
+                                mark,
+                                0xffffffff);
+            }
+
+            userRecord.mTunnelInterfaceRecords.put(
+                    resourceId,
+                    new RefcountedResource<TunnelInterfaceRecord>(
+                            new TunnelInterfaceRecord(
+                                    resourceId,
+                                    intfName,
+                                    underlyingNetwork,
+                                    localAddr,
+                                    remoteAddr,
+                                    ikey,
+                                    okey),
+                            binder));
+            return new IpSecTunnelInterfaceResponse(IpSecManager.Status.OK, resourceId, intfName);
+        } catch (RemoteException e) {
+            // Release keys if we got an error.
+            releaseNetId(ikey);
+            releaseNetId(okey);
+            throw e.rethrowFromSystemServer();
+        } catch (ServiceSpecificException e) {
+            // FIXME: get the error code and throw is at an IOException from Errno Exception
+        }
+
+        // If we make it to here, then something has gone wrong and we couldn't create a VTI.
+        // Release the keys that we reserved, and return an error status.
+        releaseNetId(ikey);
+        releaseNetId(okey);
+        return new IpSecTunnelInterfaceResponse(IpSecManager.Status.RESOURCE_UNAVAILABLE);
     }
 
     /**
@@ -1381,12 +1437,50 @@ public class IpSecService extends IIpSecService.Stub {
         }
     }
 
+    private void createOrUpdateTransform(
+            IpSecConfig c, int resourceId, SpiRecord spiRecord, EncapSocketRecord socketRecord)
+            throws RemoteException {
+
+        int encapType = c.getEncapType(), encapLocalPort = 0, encapRemotePort = 0;
+        if (encapType != IpSecTransform.ENCAP_NONE) {
+            encapLocalPort = socketRecord.getPort();
+            encapRemotePort = c.getEncapRemotePort();
+        }
+
+        IpSecAlgorithm auth = c.getAuthentication();
+        IpSecAlgorithm crypt = c.getEncryption();
+        IpSecAlgorithm authCrypt = c.getAuthenticatedEncryption();
+
+        mSrvConfig
+                .getNetdInstance()
+                .ipSecAddSecurityAssociation(
+                        resourceId,
+                        c.getMode(),
+                        c.getSourceAddress(),
+                        c.getDestinationAddress(),
+                        (c.getNetwork() != null) ? c.getNetwork().netId : 0,
+                        spiRecord.getSpi(),
+                        c.getMarkValue(),
+                        c.getMarkMask(),
+                        (auth != null) ? auth.getName() : "",
+                        (auth != null) ? auth.getKey() : new byte[] {},
+                        (auth != null) ? auth.getTruncationLengthBits() : 0,
+                        (crypt != null) ? crypt.getName() : "",
+                        (crypt != null) ? crypt.getKey() : new byte[] {},
+                        (crypt != null) ? crypt.getTruncationLengthBits() : 0,
+                        (authCrypt != null) ? authCrypt.getName() : "",
+                        (authCrypt != null) ? authCrypt.getKey() : new byte[] {},
+                        (authCrypt != null) ? authCrypt.getTruncationLengthBits() : 0,
+                        encapType,
+                        encapLocalPort,
+                        encapRemotePort);
+    }
+
     /**
-     * Create a transport mode transform, which represent two security associations (one in each
-     * direction) in the kernel. The transform will be cached by the system server and must be freed
-     * when no longer needed. It is possible to free one, deleting the SA from underneath sockets
-     * that are using it, which will result in all of those sockets becoming unable to send or
-     * receive data.
+     * Create a IPsec transform, which represents a single security association in the kernel. The
+     * transform will be cached by the system server and must be freed when no longer needed. It is
+     * possible to free one, deleting the SA from underneath sockets that are using it, which will
+     * result in all of those sockets becoming unable to send or receive data.
      */
     @Override
     public synchronized IpSecTransformResponse createTransform(IpSecConfig c, IBinder binder)
@@ -1402,58 +1496,28 @@ public class IpSecService extends IIpSecService.Stub {
             return new IpSecTransformResponse(IpSecManager.Status.RESOURCE_UNAVAILABLE);
         }
 
-        int encapType, encapLocalPort = 0, encapRemotePort = 0;
         EncapSocketRecord socketRecord = null;
-        encapType = c.getEncapType();
-        if (encapType != IpSecTransform.ENCAP_NONE) {
+        if (c.getEncapType() != IpSecTransform.ENCAP_NONE) {
             RefcountedResource<EncapSocketRecord> refcountedSocketRecord =
                     userRecord.mEncapSocketRecords.getRefcountedResourceOrThrow(
                             c.getEncapSocketResourceId());
             dependencies.add(refcountedSocketRecord);
-
             socketRecord = refcountedSocketRecord.getResource();
-            encapLocalPort = socketRecord.getPort();
-            encapRemotePort = c.getEncapRemotePort();
         }
 
-        IpSecAlgorithm auth = c.getAuthentication();
-        IpSecAlgorithm crypt = c.getEncryption();
-        IpSecAlgorithm authCrypt = c.getAuthenticatedEncryption();
-
         RefcountedResource<SpiRecord> refcountedSpiRecord =
                 userRecord.mSpiRecords.getRefcountedResourceOrThrow(c.getSpiResourceId());
         dependencies.add(refcountedSpiRecord);
         SpiRecord spiRecord = refcountedSpiRecord.getResource();
 
         try {
-            mSrvConfig
-                    .getNetdInstance()
-                    .ipSecAddSecurityAssociation(
-                            resourceId,
-                            c.getMode(),
-                            c.getSourceAddress(),
-                            c.getDestinationAddress(),
-                            (c.getNetwork() != null) ? c.getNetwork().netId : 0,
-                            spiRecord.getSpi(),
-                            c.getMarkValue(),
-                            c.getMarkMask(),
-                            (auth != null) ? auth.getName() : "",
-                            (auth != null) ? auth.getKey() : new byte[] {},
-                            (auth != null) ? auth.getTruncationLengthBits() : 0,
-                            (crypt != null) ? crypt.getName() : "",
-                            (crypt != null) ? crypt.getKey() : new byte[] {},
-                            (crypt != null) ? crypt.getTruncationLengthBits() : 0,
-                            (authCrypt != null) ? authCrypt.getName() : "",
-                            (authCrypt != null) ? authCrypt.getKey() : new byte[] {},
-                            (authCrypt != null) ? authCrypt.getTruncationLengthBits() : 0,
-                            encapType,
-                            encapLocalPort,
-                            encapRemotePort);
+            createOrUpdateTransform(c, resourceId, spiRecord, socketRecord);
         } catch (ServiceSpecificException e) {
             // FIXME: get the error code and throw is at an IOException from Errno Exception
             return new IpSecTransformResponse(IpSecManager.Status.RESOURCE_UNAVAILABLE);
         }
-        // Both SAs were created successfully, time to construct a record and lock it away
+
+        // SA was created successfully, time to construct a record and lock it away
         userRecord.mTransformRecords.put(
                 resourceId,
                 new RefcountedResource<TransformRecord>(
@@ -1561,14 +1625,48 @@ public class IpSecService extends IIpSecService.Stub {
                 c.getMode() == IpSecTransform.MODE_TUNNEL,
                 "Transform mode was not Tunnel mode; cannot be applied to a tunnel interface");
 
+        EncapSocketRecord socketRecord = null;
+        if (c.getEncapType() != IpSecTransform.ENCAP_NONE) {
+            socketRecord =
+                    userRecord.mEncapSocketRecords.getResourceOrThrow(c.getEncapSocketResourceId());
+        }
+        SpiRecord spiRecord = userRecord.mSpiRecords.getResourceOrThrow(c.getSpiResourceId());
+
         int mark =
                 (direction == IpSecManager.DIRECTION_IN)
                         ? tunnelInterfaceInfo.getIkey()
                         : tunnelInterfaceInfo.getOkey();
 
-        // TODO: Add calls to netd:
-        //       Update SA with tunnel mark (ikey or okey based on direction)
-        //       If outbound, add SPI to policy
+        try {
+            c.setMarkValue(mark);
+            c.setMarkMask(0xffffffff);
+
+            if (direction == IpSecManager.DIRECTION_OUT) {
+                // Set output mark via underlying network (output only)
+                c.setNetwork(tunnelInterfaceInfo.getUnderlyingNetwork());
+
+                // If outbound, also add SPI to the policy.
+                mSrvConfig
+                        .getNetdInstance()
+                        .ipSecUpdateSecurityPolicy(
+                                0, // Use 0 for reqId
+                                direction,
+                                "",
+                                "",
+                                transformInfo.getSpiRecord().getSpi(),
+                                mark,
+                                0xffffffff);
+            }
+
+            // Update SA with tunnel mark (ikey or okey based on direction)
+            createOrUpdateTransform(c, transformResourceId, spiRecord, socketRecord);
+        } catch (ServiceSpecificException e) {
+            if (e.errorCode == EINVAL) {
+                throw new IllegalArgumentException(e.toString());
+            } else {
+                throw e;
+            }
+        }
     }
 
     @Override
index 801a396..66e0955 100644 (file)
@@ -19,7 +19,6 @@ package com.android.server;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.fail;
 import static org.mockito.Matchers.anyInt;
-import static org.mockito.Matchers.anyLong;
 import static org.mockito.Matchers.anyString;
 import static org.mockito.Matchers.eq;
 import static org.mockito.Mockito.mock;
@@ -229,7 +228,7 @@ public class IpSecServiceParameterizedTest {
                         anyInt(),
                         anyString(),
                         anyString(),
-                        anyLong(),
+                        anyInt(),
                         eq(TEST_SPI),
                         anyInt(),
                         anyInt(),
@@ -264,7 +263,7 @@ public class IpSecServiceParameterizedTest {
                         anyInt(),
                         anyString(),
                         anyString(),
-                        anyLong(),
+                        anyInt(),
                         eq(TEST_SPI),
                         anyInt(),
                         anyInt(),