diff --git a/core/java/android/net/IIpSecService.aidl b/core/java/android/net/IIpSecService.aidl index eeb30e23d0..3ce0283d7f 100644 --- a/core/java/android/net/IIpSecService.aidl +++ b/core/java/android/net/IIpSecService.aidl @@ -21,6 +21,7 @@ import android.net.IpSecConfig; import android.net.IpSecUdpEncapResponse; import android.net.IpSecSpiResponse; import android.net.IpSecTransformResponse; +import android.net.IpSecTunnelInterfaceResponse; import android.os.Bundle; import android.os.IBinder; import android.os.ParcelFileDescriptor; @@ -39,11 +40,29 @@ interface IIpSecService void closeUdpEncapsulationSocket(int resourceId); + IpSecTunnelInterfaceResponse createTunnelInterface( + in String localAddr, + in String remoteAddr, + in Network underlyingNetwork, + in IBinder binder); + + void addAddressToTunnelInterface( + int tunnelResourceId, + String localAddr); + + void removeAddressFromTunnelInterface( + int tunnelResourceId, + String localAddr); + + void deleteTunnelInterface(int resourceId); + IpSecTransformResponse createTransform(in IpSecConfig c, in IBinder binder); void deleteTransform(int transformId); void applyTransportModeTransform(in ParcelFileDescriptor socket, int direction, int transformId); + void applyTunnelModeTransform(int tunnelResourceId, int direction, int transformResourceId); + void removeTransportModeTransforms(in ParcelFileDescriptor socket); } diff --git a/core/java/android/net/IpSecManager.java b/core/java/android/net/IpSecManager.java index 6125394ab5..24a078fccc 100644 --- a/core/java/android/net/IpSecManager.java +++ b/core/java/android/net/IpSecManager.java @@ -685,7 +685,30 @@ public final class IpSecManager { mLocalAddress = localAddress; mRemoteAddress = remoteAddress; mUnderlyingNetwork = underlyingNetwork; - // TODO: Call IpSecService + + try { + IpSecTunnelInterfaceResponse result = + mService.createTunnelInterface( + localAddress.getHostAddress(), + remoteAddress.getHostAddress(), + underlyingNetwork, + new Binder()); + switch (result.status) { + case Status.OK: + break; + case Status.RESOURCE_UNAVAILABLE: + throw new ResourceUnavailableException( + "No more tunnel interfaces may be allocated by this requester."); + default: + throw new RuntimeException( + "Unknown status returned by IpSecService: " + result.status); + } + mResourceId = result.resourceId; + mInterfaceName = result.interfaceName; + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + mCloseGuard.open("constructor"); } /** @@ -697,12 +720,12 @@ public final class IpSecManager { */ @Override public void close() { - // try { - // TODO: Call IpSecService - mResourceId = INVALID_RESOURCE_ID; - // } catch (RemoteException e) { - // throw e.rethrowFromSystemServer(); - // } + try { + mService.deleteTunnelInterface(mResourceId); + mResourceId = INVALID_RESOURCE_ID; + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } mCloseGuard.close(); } @@ -714,11 +737,20 @@ public final class IpSecManager { } close(); } + + /** @hide */ + @VisibleForTesting + public int getResourceId() { + return mResourceId; + } } /** * Create a new IpSecTunnelInterface as a local endpoint for tunneled IPsec traffic. * + *
An application that creates tunnels is responsible for cleaning up the tunnel when the
+ * underlying network goes away, and the onLost() callback is received.
+ *
* @param localAddress The local addres of the tunnel
* @param remoteAddress The local addres of the tunnel
* @param underlyingNetwork the {@link Network} that will carry traffic for this tunnel.
@@ -750,7 +782,12 @@ public final class IpSecManager {
@SystemApi
public void applyTunnelModeTransform(IpSecTunnelInterface tunnel, int direction,
IpSecTransform transform) throws IOException {
- // TODO: call IpSecService
+ try {
+ mService.applyTunnelModeTransform(
+ tunnel.getResourceId(), direction, transform.getResourceId());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
}
/**
* Construct an instance of IpSecManager within an application context.
diff --git a/core/java/android/net/IpSecTunnelInterfaceResponse.aidl b/core/java/android/net/IpSecTunnelInterfaceResponse.aidl
new file mode 100644
index 0000000000..7239221415
--- /dev/null
+++ b/core/java/android/net/IpSecTunnelInterfaceResponse.aidl
@@ -0,0 +1,20 @@
+/*
+ * Copyright (C) 2018 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;
+
+/** @hide */
+parcelable IpSecTunnelInterfaceResponse;
diff --git a/core/java/android/net/IpSecTunnelInterfaceResponse.java b/core/java/android/net/IpSecTunnelInterfaceResponse.java
new file mode 100644
index 0000000000..c23d831a44
--- /dev/null
+++ b/core/java/android/net/IpSecTunnelInterfaceResponse.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2018 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 android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * This class is used to return an IpSecTunnelInterface resource Id and and corresponding status
+ * from the IpSecService to an IpSecTunnelInterface object.
+ *
+ * @hide
+ */
+public final class IpSecTunnelInterfaceResponse implements Parcelable {
+ private static final String TAG = "IpSecTunnelInterfaceResponse";
+
+ public final int resourceId;
+ public final String interfaceName;
+ public final int status;
+ // Parcelable Methods
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel out, int flags) {
+ out.writeInt(status);
+ out.writeInt(resourceId);
+ out.writeString(interfaceName);
+ }
+
+ public IpSecTunnelInterfaceResponse(int inStatus) {
+ if (inStatus == IpSecManager.Status.OK) {
+ throw new IllegalArgumentException("Valid status implies other args must be provided");
+ }
+ status = inStatus;
+ resourceId = IpSecManager.INVALID_RESOURCE_ID;
+ interfaceName = "";
+ }
+
+ public IpSecTunnelInterfaceResponse(int inStatus, int inResourceId, String inInterfaceName) {
+ status = inStatus;
+ resourceId = inResourceId;
+ interfaceName = inInterfaceName;
+ }
+
+ private IpSecTunnelInterfaceResponse(Parcel in) {
+ status = in.readInt();
+ resourceId = in.readInt();
+ interfaceName = in.readString();
+ }
+
+ public static final Parcelable.Creator This method should only be called from Binder threads. Do not call this from within the
+ * system server as it will crash the system on failure.
+ *
+ * @return an integer key within the netId range, if successful
+ * @throws IllegalStateException if unsuccessful (all netId are currently reserved)
+ */
+ @VisibleForTesting
+ int reserveNetId() {
+ synchronized (mTunnelNetIds) {
+ for (int i = 0; i < TUN_INTF_NETID_RANGE; i++) {
+ int index = mNextTunnelNetIdIndex;
+ int netId = index + TUN_INTF_NETID_START;
+ if (++mNextTunnelNetIdIndex >= TUN_INTF_NETID_RANGE) mNextTunnelNetIdIndex = 0;
+ if (!mTunnelNetIds.get(netId)) {
+ mTunnelNetIds.put(netId, true);
+ return netId;
+ }
+ }
+ }
+ throw new IllegalStateException("No free netIds to allocate");
+ }
+
+ @VisibleForTesting
+ void releaseNetId(int netId) {
+ synchronized (mTunnelNetIds) {
+ mTunnelNetIds.delete(netId);
+ }
+ }
+
+ private final class TunnelInterfaceRecord extends OwnedResourceRecord {
+ private final String mInterfaceName;
+ private final Network mUnderlyingNetwork;
+
+ // outer addresses
+ private final String mLocalAddress;
+ private final String mRemoteAddress;
+
+ private final int mIkey;
+ private final int mOkey;
+
+ TunnelInterfaceRecord(
+ int resourceId,
+ String interfaceName,
+ Network underlyingNetwork,
+ String localAddr,
+ String remoteAddr,
+ int ikey,
+ int okey) {
+ super(resourceId);
+
+ mInterfaceName = interfaceName;
+ mUnderlyingNetwork = underlyingNetwork;
+ mLocalAddress = localAddr;
+ mRemoteAddress = remoteAddr;
+ mIkey = ikey;
+ mOkey = okey;
+ }
+
+ /** always guarded by IpSecService#this */
+ @Override
+ public void freeUnderlyingResources() {
+ // TODO: Add calls to netd
+ // Teardown VTI
+ // Delete global policies
+
+ getResourceTracker().give();
+ releaseNetId(mIkey);
+ releaseNetId(mOkey);
+ }
+
+ public String getInterfaceName() {
+ return mInterfaceName;
+ }
+
+ public Network getUnderlyingNetwork() {
+ return mUnderlyingNetwork;
+ }
+
+ /** Returns the local, outer address for the tunnelInterface */
+ public String getLocalAddress() {
+ return mLocalAddress;
+ }
+
+ /** Returns the remote, outer address for the tunnelInterface */
+ public String getRemoteAddress() {
+ return mRemoteAddress;
+ }
+
+ public int getIkey() {
+ return mIkey;
+ }
+
+ public int getOkey() {
+ return mOkey;
+ }
+
+ @Override
+ protected ResourceTracker getResourceTracker() {
+ return getUserRecord().mTunnelQuotaTracker;
+ }
+
+ @Override
+ public void invalidate() {
+ getUserRecord().removeTunnelInterfaceRecord(mResourceId);
+ }
+
+ @Override
+ public String toString() {
+ return new StringBuilder()
+ .append("{super=")
+ .append(super.toString())
+ .append(", mInterfaceName=")
+ .append(mInterfaceName)
+ .append(", mUnderlyingNetwork=")
+ .append(mUnderlyingNetwork)
+ .append(", mLocalAddress=")
+ .append(mLocalAddress)
+ .append(", mRemoteAddress=")
+ .append(mRemoteAddress)
+ .append(", mIkey=")
+ .append(mIkey)
+ .append(", mOkey=")
+ .append(mOkey)
+ .append("}")
+ .toString();
+ }
+ }
+
/**
* Tracks a UDP encap socket, and manages cleanup paths
*
@@ -1051,6 +1203,97 @@ public class IpSecService extends IIpSecService.Stub {
releaseResource(userRecord.mEncapSocketRecords, resourceId);
}
+ /**
+ * Create a tunnel interface for use in IPSec tunnel mode. The system server will cache the
+ * tunnel interface and a record of its owner so that it can and must be freed when no longer
+ * needed.
+ */
+ @Override
+ public synchronized IpSecTunnelInterfaceResponse createTunnelInterface(
+ String localAddr, String remoteAddr, Network underlyingNetwork, IBinder binder) {
+ checkNotNull(binder, "Null Binder passed to createTunnelInterface");
+ checkNotNull(underlyingNetwork, "No underlying network was specified");
+ checkInetAddress(localAddr);
+ checkInetAddress(remoteAddr);
+
+ // TODO: Check that underlying network exists, and IP addresses not assigned to a different
+ // network (b/72316676).
+
+ UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid());
+ if (!userRecord.mTunnelQuotaTracker.isAvailable()) {
+ return new IpSecTunnelInterfaceResponse(IpSecManager.Status.RESOURCE_UNAVAILABLE);
+ }
+
+ final int resourceId = mNextResourceId++;
+ final int ikey = reserveNetId();
+ 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)
+
+ userRecord.mTunnelInterfaceRecords.put(
+ resourceId,
+ new RefcountedResource