Merge changes I1470e1fa,I52250144,I03b12ae6,Id08cb64c
* changes:
gn2bp: remove hardcoded jni_registration denylist
gn2bp: add javacflag to skip GEN_JNI stub generation
gn2bp: use Android libgtest_prod_headers
gn2bp: depend on :current_android_jar for JNI generation
diff --git a/framework-t/src/android/net/IIpSecService.aidl b/framework-t/src/android/net/IIpSecService.aidl
index 933256a..88ffd0e 100644
--- a/framework-t/src/android/net/IIpSecService.aidl
+++ b/framework-t/src/android/net/IIpSecService.aidl
@@ -66,6 +66,12 @@
IpSecTransformResponse createTransform(
in IpSecConfig c, in IBinder binder, in String callingPackage);
+ void migrateTransform(
+ int transformId,
+ in String newSourceAddress,
+ in String newDestinationAddress,
+ in String callingPackage);
+
void deleteTransform(int transformId);
void applyTransportModeTransform(
diff --git a/framework-t/src/android/net/IpSecManager.java b/framework-t/src/android/net/IpSecManager.java
index 9cceac2..1c83e09 100644
--- a/framework-t/src/android/net/IpSecManager.java
+++ b/framework-t/src/android/net/IpSecManager.java
@@ -37,6 +37,7 @@
import android.util.Log;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.modules.utils.build.SdkLevel;
import dalvik.system.CloseGuard;
@@ -65,6 +66,24 @@
private static final String TAG = "IpSecManager";
/**
+ * Feature flag to declare the kernel support of updating IPsec SAs.
+ *
+ * <p>Feature for {@link #getSystemAvailableFeatures} and {@link #hasSystemFeature}: The device
+ * has the requisite kernel support for migrating IPsec tunnels to new source/destination
+ * addresses.
+ *
+ * <p>This feature implies that the device supports XFRM Migration (CONFIG_XFRM_MIGRATE) and has
+ * the kernel fixes to allow XFRM Migration correctly
+ *
+ * @see android.content.pm.PackageManager#FEATURE_IPSEC_TUNNEL_MIGRATION
+ * @hide
+ */
+ // Redefine this flag here so that IPsec code shipped in a mainline module can build on old
+ // platforms before FEATURE_IPSEC_TUNNEL_MIGRATION API is released.
+ public static final String FEATURE_IPSEC_TUNNEL_MIGRATION =
+ "android.software.ipsec_tunnel_migration";
+
+ /**
* Used when applying a transform to direct traffic through an {@link IpSecTransform}
* towards the host.
*
@@ -988,6 +1007,59 @@
}
/**
+ * 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 IpSecManager#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
+ * IpSecManager#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}.
+ *
+ * @see IpSecTunnelInterface#setUnderlyingNetwork
+ * @param transform a tunnel mode {@link IpSecTransform}
+ * @param newSourceAddress the new source address
+ * @param newDestinationAddress the new destination address
+ * @hide
+ */
+ @RequiresFeature(FEATURE_IPSEC_TUNNEL_MIGRATION)
+ @RequiresPermission(android.Manifest.permission.MANAGE_IPSEC_TUNNELS)
+ public void startMigration(
+ @NonNull IpSecTransform transform,
+ @NonNull InetAddress newSourceAddress,
+ @NonNull InetAddress newDestinationAddress) {
+ if (!SdkLevel.isAtLeastU()) {
+ throw new UnsupportedOperationException(
+ "Transform migration only supported for Android 14+");
+ }
+
+ Objects.requireNonNull(transform, "transform was null");
+ Objects.requireNonNull(newSourceAddress, "newSourceAddress was null");
+ Objects.requireNonNull(newDestinationAddress, "newDestinationAddress was null");
+
+ try {
+ mService.migrateTransform(
+ transform.getResourceId(),
+ newSourceAddress.getHostAddress(),
+ newDestinationAddress.getHostAddress(),
+ mContext.getOpPackageName());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* @hide
*/
public IpSecTransformResponse createTransform(IpSecConfig config, IBinder binder,
diff --git a/service-t/src/com/android/server/IpSecService.java b/service-t/src/com/android/server/IpSecService.java
index 6cee08a..9e71eb3 100644
--- a/service-t/src/com/android/server/IpSecService.java
+++ b/service-t/src/com/android/server/IpSecService.java
@@ -17,6 +17,7 @@
package com.android.server;
import static android.Manifest.permission.DUMP;
+import static android.net.IpSecManager.FEATURE_IPSEC_TUNNEL_MIGRATION;
import static android.net.IpSecManager.INVALID_RESOURCE_ID;
import static android.system.OsConstants.AF_INET;
import static android.system.OsConstants.AF_INET6;
@@ -36,6 +37,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 +592,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 +628,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() {
@@ -1630,6 +1682,14 @@
android.Manifest.permission.MANAGE_IPSEC_TUNNELS, "IpSecService");
}
+ private void enforceMigrateFeature() {
+ if (!mContext.getPackageManager().hasSystemFeature(FEATURE_IPSEC_TUNNEL_MIGRATION)) {
+ throw new UnsupportedOperationException(
+ "IPsec Tunnel migration requires"
+ + " PackageManager.FEATURE_IPSEC_TUNNEL_MIGRATION");
+ }
+ }
+
private void createOrUpdateTransform(
IpSecConfig c, int resourceId, SpiRecord spiRecord, EncapSocketRecord socketRecord)
throws RemoteException {
@@ -1726,6 +1786,45 @@
}
/**
+ * 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);
+ enforceMigrateFeature();
+
+ 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 +1883,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 +1970,32 @@
// Update SA with tunnel mark (ikey or okey based on direction)
createOrUpdateTransform(c, transformResourceId, spiRecord, socketRecord);
+
+ if (transformInfo.isMigrating()) {
+ if (!mContext.getPackageManager()
+ .hasSystemFeature(FEATURE_IPSEC_TUNNEL_MIGRATION)) {
+ Log.wtf(
+ TAG,
+ "Attempted to migrate a transform without"
+ + " FEATURE_IPSEC_TUNNEL_MIGRATION");
+ }
+
+ 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());
diff --git a/service/src/com/android/server/connectivity/DscpPolicyTracker.java b/service/src/com/android/server/connectivity/DscpPolicyTracker.java
index 2bfad10..8d566b6 100644
--- a/service/src/com/android/server/connectivity/DscpPolicyTracker.java
+++ b/service/src/com/android/server/connectivity/DscpPolicyTracker.java
@@ -66,8 +66,8 @@
private Set<String> mAttachedIfaces;
- private final BpfMap<Struct.U32, DscpPolicyValue> mBpfDscpIpv4Policies;
- private final BpfMap<Struct.U32, DscpPolicyValue> mBpfDscpIpv6Policies;
+ private final BpfMap<Struct.S32, DscpPolicyValue> mBpfDscpIpv4Policies;
+ private final BpfMap<Struct.S32, DscpPolicyValue> mBpfDscpIpv6Policies;
// The actual policy rules used by the BPF code to process packets
// are in mBpfDscpIpv4Policies and mBpfDscpIpv4Policies. Both of
@@ -85,10 +85,10 @@
public DscpPolicyTracker() throws ErrnoException {
mAttachedIfaces = new HashSet<String>();
mIfaceIndexToPolicyIdBpfMapIndex = new HashMap<Integer, SparseIntArray>();
- mBpfDscpIpv4Policies = new BpfMap<Struct.U32, DscpPolicyValue>(IPV4_POLICY_MAP_PATH,
- BpfMap.BPF_F_RDWR, Struct.U32.class, DscpPolicyValue.class);
- mBpfDscpIpv6Policies = new BpfMap<Struct.U32, DscpPolicyValue>(IPV6_POLICY_MAP_PATH,
- BpfMap.BPF_F_RDWR, Struct.U32.class, DscpPolicyValue.class);
+ mBpfDscpIpv4Policies = new BpfMap<Struct.S32, DscpPolicyValue>(IPV4_POLICY_MAP_PATH,
+ BpfMap.BPF_F_RDWR, Struct.S32.class, DscpPolicyValue.class);
+ mBpfDscpIpv6Policies = new BpfMap<Struct.S32, DscpPolicyValue>(IPV6_POLICY_MAP_PATH,
+ BpfMap.BPF_F_RDWR, Struct.S32.class, DscpPolicyValue.class);
}
private boolean isUnusedIndex(int index) {
@@ -181,7 +181,7 @@
// are both null or if they are both instances of Inet4Address.
if (matchesIpv4(policy)) {
mBpfDscpIpv4Policies.insertOrReplaceEntry(
- new Struct.U32(addIndex),
+ new Struct.S32(addIndex),
new DscpPolicyValue(policy.getSourceAddress(),
policy.getDestinationAddress(), ifIndex,
policy.getSourcePort(), policy.getDestinationPortRange(),
@@ -192,7 +192,7 @@
// are both null or if they are both instances of Inet6Address.
if (matchesIpv6(policy)) {
mBpfDscpIpv6Policies.insertOrReplaceEntry(
- new Struct.U32(addIndex),
+ new Struct.S32(addIndex),
new DscpPolicyValue(policy.getSourceAddress(),
policy.getDestinationAddress(), ifIndex,
policy.getSourcePort(), policy.getDestinationPortRange(),
@@ -258,8 +258,8 @@
boolean sendCallback) {
int status = DSCP_POLICY_STATUS_POLICY_NOT_FOUND;
try {
- mBpfDscpIpv4Policies.replaceEntry(new Struct.U32(index), DscpPolicyValue.NONE);
- mBpfDscpIpv6Policies.replaceEntry(new Struct.U32(index), DscpPolicyValue.NONE);
+ mBpfDscpIpv4Policies.replaceEntry(new Struct.S32(index), DscpPolicyValue.NONE);
+ mBpfDscpIpv6Policies.replaceEntry(new Struct.S32(index), DscpPolicyValue.NONE);
status = DSCP_POLICY_STATUS_DELETED;
} catch (ErrnoException e) {
Log.e(TAG, "Failed to delete policy from map: ", e);
diff --git a/tests/unit/java/android/net/IpSecTransformTest.java b/tests/unit/java/android/net/IpSecTransformTest.java
index c1bd719..ec59064 100644
--- a/tests/unit/java/android/net/IpSecTransformTest.java
+++ b/tests/unit/java/android/net/IpSecTransformTest.java
@@ -18,22 +18,92 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.fail;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.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.os.Build;
+import android.test.mock.MockContext;
import androidx.test.filters.SmallTest;
+import com.android.server.IpSecService;
import com.android.testutils.DevSdkIgnoreRule;
import com.android.testutils.DevSdkIgnoreRunner;
+import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
+import java.net.InetAddress;
+
/** Unit tests for {@link IpSecTransform}. */
@SmallTest
@RunWith(DevSdkIgnoreRunner.class)
@DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.S_V2)
public class IpSecTransformTest {
+ @Rule public final DevSdkIgnoreRule ignoreRule = new DevSdkIgnoreRule();
+
+ private static final int DROID_SPI = 0xD1201D;
+ private static final int TEST_RESOURCE_ID = 0x1234;
+
+ private static final InetAddress SRC_ADDRESS = InetAddresses.parseNumericAddress("192.0.2.200");
+ private static final InetAddress DST_ADDRESS = InetAddresses.parseNumericAddress("192.0.2.201");
+ private static final InetAddress SRC_ADDRESS_V6 =
+ InetAddresses.parseNumericAddress("2001:db8::200");
+ private static final InetAddress DST_ADDRESS_V6 =
+ InetAddresses.parseNumericAddress("2001:db8::201");
+
+ private MockContext mMockContext;
+ private IpSecService mMockIpSecService;
+ private IpSecManager mIpSecManager;
+
+ @Before
+ public void setUp() throws Exception {
+ mMockIpSecService = mock(IpSecService.class);
+ mIpSecManager = new IpSecManager(mock(Context.class) /* unused */, mMockIpSecService);
+
+ // Set up mMockContext since IpSecTransform needs an IpSecManager instance and a non-null
+ // package name to create transform
+ mMockContext =
+ new MockContext() {
+ @Override
+ public String getSystemServiceName(Class<?> serviceClass) {
+ if (serviceClass.equals(IpSecManager.class)) {
+ return Context.IPSEC_SERVICE;
+ }
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public Object getSystemService(String name) {
+ if (name.equals(Context.IPSEC_SERVICE)) {
+ return mIpSecManager;
+ }
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public String getOpPackageName() {
+ return "fooPackage";
+ }
+ };
+
+ final IpSecSpiResponse spiResp =
+ new IpSecSpiResponse(IpSecManager.Status.OK, TEST_RESOURCE_ID, DROID_SPI);
+ when(mMockIpSecService.allocateSecurityParameterIndex(any(), anyInt(), any()))
+ .thenReturn(spiResp);
+
+ final IpSecTransformResponse transformResp =
+ new IpSecTransformResponse(IpSecManager.Status.OK, TEST_RESOURCE_ID);
+ when(mMockIpSecService.createTransform(any(), any(), any())).thenReturn(transformResp);
+ }
@Test
public void testCreateTransformCopiesConfig() {
@@ -64,4 +134,32 @@
assertEquals(config1, config2);
}
+
+ private IpSecTransform buildTestTransform() throws Exception {
+ final IpSecManager.SecurityParameterIndex spi =
+ mIpSecManager.allocateSecurityParameterIndex(DST_ADDRESS);
+ return new IpSecTransform.Builder(mMockContext).buildTunnelModeTransform(SRC_ADDRESS, spi);
+ }
+
+ @Test
+ @DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.TIRAMISU)
+ public void testStartMigration() throws Exception {
+ mIpSecManager.startMigration(buildTestTransform(), SRC_ADDRESS_V6, DST_ADDRESS_V6);
+ verify(mMockIpSecService)
+ .migrateTransform(
+ anyInt(),
+ eq(SRC_ADDRESS_V6.getHostAddress()),
+ eq(DST_ADDRESS_V6.getHostAddress()),
+ any());
+ }
+
+ @Test
+ @DevSdkIgnoreRule.IgnoreAfter(Build.VERSION_CODES.TIRAMISU)
+ public void testStartMigrationOnSdkBeforeU() throws Exception {
+ try {
+ mIpSecManager.startMigration(buildTestTransform(), SRC_ADDRESS_V6, DST_ADDRESS_V6);
+ fail("Expect to fail since migration is not supported before U");
+ } catch (UnsupportedOperationException expected) {
+ }
+ }
}
diff --git a/tests/unit/java/com/android/server/IpSecServiceParameterizedTest.java b/tests/unit/java/com/android/server/IpSecServiceParameterizedTest.java
index 624071a..1618a62 100644
--- a/tests/unit/java/com/android/server/IpSecServiceParameterizedTest.java
+++ b/tests/unit/java/com/android/server/IpSecServiceParameterizedTest.java
@@ -23,6 +23,7 @@
import static android.net.IpSecManager.DIRECTION_FWD;
import static android.net.IpSecManager.DIRECTION_IN;
import static android.net.IpSecManager.DIRECTION_OUT;
+import static android.net.IpSecManager.FEATURE_IPSEC_TUNNEL_MIGRATION;
import static android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK;
import static android.system.OsConstants.AF_INET;
import static android.system.OsConstants.AF_INET6;
@@ -30,11 +31,16 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.fail;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.argThat;
+import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Matchers.anyInt;
import static org.mockito.Matchers.anyString;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -49,6 +55,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;
@@ -130,6 +137,9 @@
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F
};
+ private static final String NEW_SRC_ADDRESS = "2001:db8:2::1";
+ private static final String NEW_DST_ADDRESS = "2001:db8:2::2";
+
AppOpsManager mMockAppOps = mock(AppOpsManager.class);
ConnectivityManager mMockConnectivityMgr = mock(ConnectivityManager.class);
@@ -369,8 +379,8 @@
.ipSecAddSecurityAssociation(
eq(mUid),
eq(config.getMode()),
- eq(config.getSourceAddress()),
- eq(config.getDestinationAddress()),
+ eq(mSourceAddr),
+ eq(mDestinationAddr),
eq((config.getNetwork() != null) ? config.getNetwork().netId : 0),
eq(TEST_SPI),
eq(0),
@@ -910,9 +920,60 @@
}
}
+ @Test
+ @DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.TIRAMISU)
+ public void testApplyAndMigrateTunnelModeTransformOutbound() throws Exception {
+ verifyApplyAndMigrateTunnelModeTransformCommon(false, DIRECTION_OUT);
+ }
+
+ @Test
+ @DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.TIRAMISU)
+ public void testApplyAndMigrateTunnelModeTransformOutboundReleasedSpi() throws Exception {
+ verifyApplyAndMigrateTunnelModeTransformCommon(true, DIRECTION_OUT);
+ }
+
+ @Test
+ @DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.TIRAMISU)
+ public void testApplyAndMigrateTunnelModeTransformInbound() throws Exception {
+ verifyApplyAndMigrateTunnelModeTransformCommon(false, DIRECTION_IN);
+ }
+
+ @Test
+ @DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.TIRAMISU)
+ public void testApplyAndMigrateTunnelModeTransformInboundReleasedSpi() throws Exception {
+ verifyApplyAndMigrateTunnelModeTransformCommon(true, DIRECTION_IN);
+ }
+
+ @Test
+ @DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.TIRAMISU)
+ public void testApplyAndMigrateTunnelModeTransformForward() throws Exception {
+ verifyApplyAndMigrateTunnelModeTransformCommon(false, DIRECTION_FWD);
+ }
+
+ @Test
+ @DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.TIRAMISU)
+ public void testApplyAndMigrateTunnelModeTransformForwardReleasedSpi() throws Exception {
+ verifyApplyAndMigrateTunnelModeTransformCommon(true, DIRECTION_FWD);
+ }
+
public void verifyApplyTunnelModeTransformCommon(boolean closeSpiBeforeApply, int direction)
throws Exception {
- IpSecConfig ipSecConfig = new IpSecConfig();
+ verifyApplyTunnelModeTransformCommon(
+ new IpSecConfig(), closeSpiBeforeApply, false /* isMigrating */, direction);
+ }
+
+ public void verifyApplyAndMigrateTunnelModeTransformCommon(
+ boolean closeSpiBeforeApply, int direction) throws Exception {
+ verifyApplyTunnelModeTransformCommon(
+ new IpSecConfig(), closeSpiBeforeApply, true /* isMigrating */, direction);
+ }
+
+ public int verifyApplyTunnelModeTransformCommon(
+ IpSecConfig ipSecConfig,
+ boolean closeSpiBeforeApply,
+ boolean isMigrating,
+ int direction)
+ throws Exception {
ipSecConfig.setMode(IpSecTransform.MODE_TUNNEL);
addDefaultSpisAndRemoteAddrToIpSecConfig(ipSecConfig);
addAuthAndCryptToIpSecConfig(ipSecConfig);
@@ -928,6 +989,12 @@
int transformResourceId = createTransformResp.resourceId;
int tunnelResourceId = createTunnelResp.resourceId;
+
+ if (isMigrating) {
+ mIpSecService.migrateTransform(
+ transformResourceId, NEW_SRC_ADDRESS, NEW_DST_ADDRESS, BLESSED_PACKAGE);
+ }
+
mIpSecService.applyTunnelModeTransform(
tunnelResourceId, direction, transformResourceId, BLESSED_PACKAGE);
@@ -947,8 +1014,16 @@
ipSecConfig.setXfrmInterfaceId(tunnelResourceId);
verifyTransformNetdCalledForCreatingSA(ipSecConfig, createTransformResp);
- }
+ if (isMigrating) {
+ verify(mMockNetd, times(ADDRESS_FAMILIES.length))
+ .ipSecMigrate(any(IpSecMigrateInfoParcel.class));
+ } else {
+ verify(mMockNetd, never()).ipSecMigrate(any());
+ }
+
+ return tunnelResourceId;
+ }
@Test
public void testApplyTunnelModeTransformWithClosedSpi() throws Exception {
@@ -1023,7 +1098,7 @@
}
@Test
- public void testFeatureFlagVerification() throws Exception {
+ public void testFeatureFlagIpSecTunnelsVerification() throws Exception {
when(mMockPkgMgr.hasSystemFeature(eq(PackageManager.FEATURE_IPSEC_TUNNELS)))
.thenReturn(false);
@@ -1035,4 +1110,17 @@
} catch (UnsupportedOperationException expected) {
}
}
+
+ @Test
+ @DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.TIRAMISU)
+ public void testFeatureFlagIpSecTunnelMigrationVerification() throws Exception {
+ when(mMockPkgMgr.hasSystemFeature(eq(FEATURE_IPSEC_TUNNEL_MIGRATION))).thenReturn(false);
+
+ try {
+ mIpSecService.migrateTransform(
+ 1 /* transformId */, NEW_SRC_ADDRESS, NEW_DST_ADDRESS, BLESSED_PACKAGE);
+ fail("Expected UnsupportedOperationException for disabled feature");
+ } catch (UnsupportedOperationException expected) {
+ }
+ }
}