summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author chiachangwang <chiachangwang@google.com> 2022-08-02 14:13:33 +0000
committer chiachangwang <chiachangwang@google.com> 2022-10-26 03:08:32 +0000
commit49d781a7ff41be569aa716c9e049d77bf5625b9a (patch)
tree8874ab13fa0db6d9685d2bb41367b934dcff55b2
parent1291b83a2fb8ae8a095d50730f75013151f6ce3f (diff)
Reset Ike session if stall is not recovered by MOBIKE
Reset IKE session when data stall happens in VPN network. When a data stall is suspected, MOBIKE will be triggered to try to recover the network. If MOBIKE does not help, more aggressive recover step is required. Thus, resetting the ike session if the situation is not recovered for a while. Also rate-limit the timer to prevent the session being reset for every fixed time. Bug: 238692379 Test: atest FrameworksNetTests Change-Id: If72ee8a2ecdf4ec9038b78b98055ee21b7fa47da
-rw-r--r--services/core/java/com/android/server/connectivity/Vpn.java103
1 files changed, 95 insertions, 8 deletions
diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java
index 6795b6b4158d..45b0f0a6d04a 100644
--- a/services/core/java/com/android/server/connectivity/Vpn.java
+++ b/services/core/java/com/android/server/connectivity/Vpn.java
@@ -79,6 +79,7 @@ import android.net.NetworkScore;
import android.net.RouteInfo;
import android.net.UidRangeParcel;
import android.net.UnderlyingNetworkInfo;
+import android.net.Uri;
import android.net.VpnManager;
import android.net.VpnProfileState;
import android.net.VpnService;
@@ -226,6 +227,16 @@ public class Vpn {
private static final int VPN_DEFAULT_SCORE = 101;
/**
+ * The reset session timer for data stall. If a session has not successfully revalidated after
+ * the delay, the session will be torn down and restarted in an attempt to recover. Delay
+ * counter is reset on successful validation only.
+ *
+ * <p>If retries have exceeded the length of this array, the last entry in the array will be
+ * used as a repeating interval.
+ */
+ private static final long[] DATA_STALL_RESET_DELAYS_SEC = {30L, 60L, 120L, 240L, 480L, 960L};
+
+ /**
* The initial token value of IKE session.
*/
private static final int STARTING_TOKEN = -1;
@@ -271,6 +282,7 @@ public class Vpn {
private final UserManager mUserManager;
private final VpnProfileStore mVpnProfileStore;
+ protected boolean mDataStallSuspected = false;
@VisibleForTesting
VpnProfileStore getVpnProfileStore() {
@@ -522,12 +534,30 @@ public class Vpn {
@NonNull LinkProperties lp,
@NonNull NetworkScore score,
@NonNull NetworkAgentConfig config,
- @Nullable NetworkProvider provider) {
+ @Nullable NetworkProvider provider,
+ @Nullable ValidationStatusCallback callback) {
return new VpnNetworkAgentWrapper(
- context, looper, logTag, nc, lp, score, config, provider);
+ context, looper, logTag, nc, lp, score, config, provider, callback);
+ }
+
+ /**
+ * Get the length of time to wait before resetting the ike session when a data stall is
+ * suspected.
+ */
+ public long getDataStallResetSessionSeconds(int count) {
+ if (count >= DATA_STALL_RESET_DELAYS_SEC.length) {
+ return DATA_STALL_RESET_DELAYS_SEC[DATA_STALL_RESET_DELAYS_SEC.length - 1];
+ } else {
+ return DATA_STALL_RESET_DELAYS_SEC[count];
+ }
}
}
+ @VisibleForTesting
+ interface ValidationStatusCallback {
+ void onValidationStatus(int status);
+ }
+
public Vpn(Looper looper, Context context, INetworkManagementService netService, INetd netd,
@UserIdInt int userId, VpnProfileStore vpnProfileStore) {
this(looper, context, new Dependencies(), netService, netd, userId, vpnProfileStore,
@@ -1460,6 +1490,11 @@ public class Vpn {
@GuardedBy("this")
private void agentConnect() {
+ agentConnect(null /* validationCallback */);
+ }
+
+ @GuardedBy("this")
+ private void agentConnect(@Nullable ValidationStatusCallback validationCallback) {
LinkProperties lp = makeLinkProperties();
// VPN either provide a default route (IPv4 or IPv6 or both), or they are a split tunnel
@@ -1507,7 +1542,7 @@ public class Vpn {
mNetworkAgent = mDeps.newNetworkAgent(mContext, mLooper, NETWORKTYPE /* logtag */,
mNetworkCapabilities, lp,
new NetworkScore.Builder().setLegacyInt(VPN_DEFAULT_SCORE).build(),
- networkAgentConfig, mNetworkProvider);
+ networkAgentConfig, mNetworkProvider, validationCallback);
final long token = Binder.clearCallingIdentity();
try {
mNetworkAgent.register();
@@ -2723,7 +2758,7 @@ public class Vpn {
@Nullable private ScheduledFuture<?> mScheduledHandleNetworkLostFuture;
@Nullable private ScheduledFuture<?> mScheduledHandleRetryIkeSessionFuture;
-
+ @Nullable private ScheduledFuture<?> mScheduledHandleDataStallFuture;
/** Signal to ensure shutdown is honored even if a new Network is connected. */
private boolean mIsRunning = true;
@@ -2750,6 +2785,14 @@ public class Vpn {
private boolean mMobikeEnabled = false;
/**
+ * The number of attempts to reset the IKE session since the last successful connection.
+ *
+ * <p>This variable controls the retry delay, and is reset when the VPN pass network
+ * validation.
+ */
+ private int mDataStallRetryCount = 0;
+
+ /**
* The number of attempts since the last successful connection.
*
* <p>This variable controls the retry delay, and is reset when a new IKE session is
@@ -2931,7 +2974,7 @@ public class Vpn {
if (isSettingsVpnLocked()) {
prepareStatusIntent();
}
- agentConnect();
+ agentConnect(this::onValidationStatus);
return; // Link properties are already sent.
} else {
// Underlying networks also set in agentConnect()
@@ -3200,18 +3243,52 @@ public class Vpn {
// Ignore stale runner.
if (mVpnRunner != Vpn.IkeV2VpnRunner.this) return;
- // Handle the report only for current VPN network.
+ // Handle the report only for current VPN network. If data stall is already
+ // reported, ignoring the other reports. It means that the stall is not
+ // recovered by MOBIKE and should be on the way to reset the ike session.
if (mNetworkAgent != null
- && mNetworkAgent.getNetwork().equals(report.getNetwork())) {
+ && mNetworkAgent.getNetwork().equals(report.getNetwork())
+ && !mDataStallSuspected) {
Log.d(TAG, "Data stall suspected");
// Trigger MOBIKE.
maybeMigrateIkeSession(mActiveNetwork);
+ mDataStallSuspected = true;
}
}
}
}
+ public void onValidationStatus(int status) {
+ if (status == NetworkAgent.VALIDATION_STATUS_VALID) {
+ // No data stall now. Reset it.
+ mExecutor.execute(() -> {
+ mDataStallSuspected = false;
+ mDataStallRetryCount = 0;
+ if (mScheduledHandleDataStallFuture != null) {
+ Log.d(TAG, "Recovered from stall. Cancel pending reset action.");
+ mScheduledHandleDataStallFuture.cancel(false /* mayInterruptIfRunning */);
+ mScheduledHandleDataStallFuture = null;
+ }
+ });
+ } else {
+ // Skip other invalid status if the scheduled recovery exists.
+ if (mScheduledHandleDataStallFuture != null) return;
+
+ mScheduledHandleDataStallFuture = mExecutor.schedule(() -> {
+ if (mDataStallSuspected) {
+ Log.d(TAG, "Reset session to recover stalled network");
+ // This will reset old state if it exists.
+ startIkeSession(mActiveNetwork);
+ }
+
+ // Reset mScheduledHandleDataStallFuture since it's already run on executor
+ // thread.
+ mScheduledHandleDataStallFuture = null;
+ }, mDeps.getDataStallResetSessionSeconds(mDataStallRetryCount++), TimeUnit.SECONDS);
+ }
+ }
+
/**
* Handles loss of the default underlying network
*
@@ -4339,6 +4416,7 @@ public class Vpn {
// un-finalized.
@VisibleForTesting
public static class VpnNetworkAgentWrapper extends NetworkAgent {
+ private final ValidationStatusCallback mCallback;
/** Create an VpnNetworkAgentWrapper */
public VpnNetworkAgentWrapper(
@NonNull Context context,
@@ -4348,8 +4426,10 @@ public class Vpn {
@NonNull LinkProperties lp,
@NonNull NetworkScore score,
@NonNull NetworkAgentConfig config,
- @Nullable NetworkProvider provider) {
+ @Nullable NetworkProvider provider,
+ @Nullable ValidationStatusCallback callback) {
super(context, looper, logTag, nc, lp, score, config, provider);
+ mCallback = callback;
}
/** Update the LinkProperties */
@@ -4371,6 +4451,13 @@ public class Vpn {
public void onNetworkUnwanted() {
// We are user controlled, not driven by NetworkRequest.
}
+
+ @Override
+ public void onValidationStatus(int status, Uri redirectUri) {
+ if (mCallback != null) {
+ mCallback.onValidationStatus(status);
+ }
+ }
}
/**