Support IPsec transform migration
This commit adds methods to support migrating tunnel mode
IpSecTransform to new source/destination addresses.
Bug: 169171001
Test: atest FrameworksNetTests (new tests added)
Change-Id: Ic177015fba5b62d3f73009633118109d3631086f
diff --git a/service-t/src/com/android/server/IpSecService.java b/service-t/src/com/android/server/IpSecService.java
index 6cee08a..ca96c06 100644
--- a/service-t/src/com/android/server/IpSecService.java
+++ b/service-t/src/com/android/server/IpSecService.java
@@ -36,6 +36,7 @@
import android.net.IpSecAlgorithm;
import android.net.IpSecConfig;
import android.net.IpSecManager;
+import android.net.IpSecMigrateInfoParcel;
import android.net.IpSecSpiResponse;
import android.net.IpSecTransform;
import android.net.IpSecTransformResponse;
@@ -590,14 +591,19 @@
}
/**
- * Tracks an SA in the kernel, and manages cleanup paths. Once a TransformRecord is
- * created, the SpiRecord that originally tracked the SAs will reliquish the
- * responsibility of freeing the underlying SA to this class via the mOwnedByTransform flag.
+ * Tracks an SA in the kernel, and manages cleanup paths. Once a TransformRecord is created, the
+ * SpiRecord that originally tracked the SAs will reliquish the responsibility of freeing the
+ * underlying SA to this class via the mOwnedByTransform flag.
+ *
+ * <p>This class is not thread-safe, and expects that that users of this class will ensure
+ * synchronization and thread safety by holding the IpSecService.this instance lock
*/
private final class TransformRecord extends OwnedResourceRecord {
private final IpSecConfig mConfig;
private final SpiRecord mSpi;
private final EncapSocketRecord mSocket;
+ private String mNewSourceAddress = null;
+ private String mNewDestinationAddress = null;
TransformRecord(
int resourceId, IpSecConfig config, SpiRecord spi, EncapSocketRecord socket) {
@@ -621,6 +627,51 @@
return mSocket;
}
+ @GuardedBy("IpSecService.this")
+ public String getNewSourceAddress() {
+ return mNewSourceAddress;
+ }
+
+ @GuardedBy("IpSecService.this")
+ public String getNewDestinationAddress() {
+ return mNewDestinationAddress;
+ }
+
+ private void verifyTunnelModeOrThrow() {
+ if (mConfig.getMode() != IpSecTransform.MODE_TUNNEL) {
+ throw new UnsupportedOperationException(
+ "Migration requested/called on non-tunnel-mode transform");
+ }
+ }
+
+ /** Start migrating this transform to new source and destination addresses */
+ @GuardedBy("IpSecService.this")
+ public void startMigration(String newSourceAddress, String newDestinationAddress) {
+ verifyTunnelModeOrThrow();
+ Objects.requireNonNull(newSourceAddress, "newSourceAddress was null");
+ Objects.requireNonNull(newDestinationAddress, "newDestinationAddress was null");
+ mNewSourceAddress = newSourceAddress;
+ mNewDestinationAddress = newDestinationAddress;
+ }
+
+ /** Finish migration and update addresses. */
+ @GuardedBy("IpSecService.this")
+ public void finishMigration() {
+ verifyTunnelModeOrThrow();
+ mConfig.setSourceAddress(mNewSourceAddress);
+ mConfig.setDestinationAddress(mNewDestinationAddress);
+ mNewSourceAddress = null;
+ mNewDestinationAddress = null;
+ }
+
+ /** Return if this transform is going to be migrated. */
+ @GuardedBy("IpSecService.this")
+ public boolean isMigrating() {
+ verifyTunnelModeOrThrow();
+
+ return mNewSourceAddress != null;
+ }
+
/** always guarded by IpSecService#this */
@Override
public void freeUnderlyingResources() {
@@ -1726,6 +1777,44 @@
}
/**
+ * Migrate an active Tunnel Mode IPsec Transform to new source/destination addresses.
+ *
+ * <p>Begins the process of migrating a transform and cache the new addresses. To complete the
+ * migration once started, callers MUST apply the same transform to the appropriate tunnel using
+ * {@link #applyTunnelModeTransform}. Otherwise, the address update will not be committed and
+ * the transform will still only process traffic between the current source and destination
+ * address. One common use case is that the control plane will start the migration process and
+ * then hand off the transform to the IPsec caller to perform the actual migration when the
+ * tunnel is ready.
+ *
+ * <p>If this method is called multiple times before {@link #applyTunnelModeTransform} is
+ * called, when the transform is applied, it will be migrated to the addresses from the last
+ * call.
+ *
+ * <p>The provided source and destination addresses MUST share the same address family, but they
+ * can have a different family from the current addresses.
+ *
+ * <p>Transform migration is only supported for tunnel mode transforms. Calling this method on
+ * other types of transforms will throw an {@code UnsupportedOperationException}.
+ */
+ @Override
+ public synchronized void migrateTransform(
+ int transformId,
+ String newSourceAddress,
+ String newDestinationAddress,
+ String callingPackage) {
+ Objects.requireNonNull(newSourceAddress, "newSourceAddress was null");
+ Objects.requireNonNull(newDestinationAddress, "newDestinationAddress was null");
+
+ enforceTunnelFeatureAndPermissions(callingPackage);
+
+ UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid());
+ TransformRecord transformInfo =
+ userRecord.mTransformRecords.getResourceOrThrow(transformId);
+ transformInfo.startMigration(newSourceAddress, newDestinationAddress);
+ }
+
+ /**
* Delete a transport mode transform that was previously allocated by + registered with the
* system server. If this is called on an inactive (or non-existent) transform, it will not
* return an error. It's safe to de-allocate transforms that may have already been deleted for
@@ -1784,12 +1873,15 @@
/**
* Apply an active tunnel mode transform to a TunnelInterface, which will apply the IPsec
- * security association as a correspondent policy to the provided interface
+ * security association as a correspondent policy to the provided interface.
+ *
+ * <p>If the transform is migrating, migrate the IPsec security association to new
+ * source/destination addresses, and mark the migration as finished.
*/
@Override
public synchronized void applyTunnelModeTransform(
- int tunnelResourceId, int direction,
- int transformResourceId, String callingPackage) throws RemoteException {
+ int tunnelResourceId, int direction, int transformResourceId, String callingPackage)
+ throws RemoteException {
enforceTunnelFeatureAndPermissions(callingPackage);
checkDirection(direction);
@@ -1868,6 +1960,24 @@
// Update SA with tunnel mark (ikey or okey based on direction)
createOrUpdateTransform(c, transformResourceId, spiRecord, socketRecord);
+
+ if (transformInfo.isMigrating()) {
+ for (int selAddrFamily : ADDRESS_FAMILIES) {
+ final IpSecMigrateInfoParcel migrateInfo =
+ new IpSecMigrateInfoParcel(
+ Binder.getCallingUid(),
+ selAddrFamily,
+ direction,
+ c.getSourceAddress(),
+ c.getDestinationAddress(),
+ transformInfo.getNewSourceAddress(),
+ transformInfo.getNewDestinationAddress(),
+ c.getXfrmInterfaceId());
+
+ mNetd.ipSecMigrate(migrateInfo);
+ }
+ transformInfo.finishMigration();
+ }
} catch (ServiceSpecificException e) {
if (e.errorCode == EINVAL) {
throw new IllegalArgumentException(e.toString());