Merge changes from topic "cherrypicker-L33200000955045139:N22000001272520181" into tm-qpr-dev
* changes: Add a retry mechanism when error is recoverable Enable MOBIKE in IKEv2 VPN Use token to identify IKE Session
This commit is contained in:
committed by
Android (Google) Code Review
commit
0a431804fa
@@ -86,6 +86,8 @@ import android.net.ipsec.ike.ChildSessionConfiguration;
|
|||||||
import android.net.ipsec.ike.ChildSessionParams;
|
import android.net.ipsec.ike.ChildSessionParams;
|
||||||
import android.net.ipsec.ike.IkeSession;
|
import android.net.ipsec.ike.IkeSession;
|
||||||
import android.net.ipsec.ike.IkeSessionCallback;
|
import android.net.ipsec.ike.IkeSessionCallback;
|
||||||
|
import android.net.ipsec.ike.IkeSessionConfiguration;
|
||||||
|
import android.net.ipsec.ike.IkeSessionConnectionInfo;
|
||||||
import android.net.ipsec.ike.IkeSessionParams;
|
import android.net.ipsec.ike.IkeSessionParams;
|
||||||
import android.net.ipsec.ike.IkeTunnelConnectionParams;
|
import android.net.ipsec.ike.IkeTunnelConnectionParams;
|
||||||
import android.net.ipsec.ike.exceptions.IkeNetworkLostException;
|
import android.net.ipsec.ike.exceptions.IkeNetworkLostException;
|
||||||
@@ -168,9 +170,10 @@ import java.util.UUID;
|
|||||||
import java.util.concurrent.CompletableFuture;
|
import java.util.concurrent.CompletableFuture;
|
||||||
import java.util.concurrent.ExecutionException;
|
import java.util.concurrent.ExecutionException;
|
||||||
import java.util.concurrent.Executor;
|
import java.util.concurrent.Executor;
|
||||||
import java.util.concurrent.ExecutorService;
|
|
||||||
import java.util.concurrent.Executors;
|
|
||||||
import java.util.concurrent.RejectedExecutionException;
|
import java.util.concurrent.RejectedExecutionException;
|
||||||
|
import java.util.concurrent.ScheduledFuture;
|
||||||
|
import java.util.concurrent.ScheduledThreadPoolExecutor;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -190,11 +193,19 @@ public class Vpn {
|
|||||||
private static final long VPN_LAUNCH_IDLE_ALLOWLIST_DURATION_MS = 60 * 1000;
|
private static final long VPN_LAUNCH_IDLE_ALLOWLIST_DURATION_MS = 60 * 1000;
|
||||||
|
|
||||||
// Length of time (in milliseconds) that an app registered for VpnManager events is placed on
|
// Length of time (in milliseconds) that an app registered for VpnManager events is placed on
|
||||||
// the device idle allowlist each time the a VpnManager event is fired.
|
// the device idle allowlist each time the VpnManager event is fired.
|
||||||
private static final long VPN_MANAGER_EVENT_ALLOWLIST_DURATION_MS = 30 * 1000;
|
private static final long VPN_MANAGER_EVENT_ALLOWLIST_DURATION_MS = 30 * 1000;
|
||||||
|
|
||||||
private static final String LOCKDOWN_ALLOWLIST_SETTING_NAME =
|
private static final String LOCKDOWN_ALLOWLIST_SETTING_NAME =
|
||||||
Settings.Secure.ALWAYS_ON_VPN_LOCKDOWN_WHITELIST;
|
Settings.Secure.ALWAYS_ON_VPN_LOCKDOWN_WHITELIST;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The retries for consecutive failures.
|
||||||
|
*
|
||||||
|
* <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[] IKEV2_VPN_RETRY_DELAYS_SEC = {1L, 2L, 5L, 30L, 60L, 300L, 900L};
|
||||||
/**
|
/**
|
||||||
* Largest profile size allowable for Platform VPNs.
|
* Largest profile size allowable for Platform VPNs.
|
||||||
*
|
*
|
||||||
@@ -473,6 +484,20 @@ public class Vpn {
|
|||||||
"Cannot set tunnel's fd as blocking=" + blocking, e);
|
"Cannot set tunnel's fd as blocking=" + blocking, e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the next retry delay
|
||||||
|
*
|
||||||
|
* <p>If retries have exceeded the IKEV2_VPN_RETRY_DELAYS_SEC, the last entry in
|
||||||
|
* the array will be used as a repeating interval.
|
||||||
|
*/
|
||||||
|
public long getNextRetryDelaySeconds(int retryCount) {
|
||||||
|
if (retryCount >= IKEV2_VPN_RETRY_DELAYS_SEC.length) {
|
||||||
|
return IKEV2_VPN_RETRY_DELAYS_SEC[IKEV2_VPN_RETRY_DELAYS_SEC.length - 1];
|
||||||
|
} else {
|
||||||
|
return IKEV2_VPN_RETRY_DELAYS_SEC[retryCount];
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public Vpn(Looper looper, Context context, INetworkManagementService netService, INetd netd,
|
public Vpn(Looper looper, Context context, INetworkManagementService netService, INetd netd,
|
||||||
@@ -2605,13 +2630,23 @@ public class Vpn {
|
|||||||
|
|
||||||
void onDefaultNetworkLinkPropertiesChanged(@NonNull LinkProperties lp);
|
void onDefaultNetworkLinkPropertiesChanged(@NonNull LinkProperties lp);
|
||||||
|
|
||||||
void onChildOpened(
|
void onDefaultNetworkLost(@NonNull Network network);
|
||||||
@NonNull Network network, @NonNull ChildSessionConfiguration childConfig);
|
|
||||||
|
|
||||||
void onChildTransformCreated(
|
void onIkeOpened(int token, @NonNull IkeSessionConfiguration ikeConfiguration);
|
||||||
@NonNull Network network, @NonNull IpSecTransform transform, int direction);
|
|
||||||
|
|
||||||
void onSessionLost(@NonNull Network network, @Nullable Exception exception);
|
void onIkeConnectionInfoChanged(
|
||||||
|
int token, @NonNull IkeSessionConnectionInfo ikeConnectionInfo);
|
||||||
|
|
||||||
|
void onChildOpened(int token, @NonNull ChildSessionConfiguration childConfig);
|
||||||
|
|
||||||
|
void onChildTransformCreated(int token, @NonNull IpSecTransform transform, int direction);
|
||||||
|
|
||||||
|
void onChildMigrated(
|
||||||
|
int token,
|
||||||
|
@NonNull IpSecTransform inTransform,
|
||||||
|
@NonNull IpSecTransform outTransform);
|
||||||
|
|
||||||
|
void onSessionLost(int token, @Nullable Exception exception);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -2642,6 +2677,10 @@ public class Vpn {
|
|||||||
class IkeV2VpnRunner extends VpnRunner implements IkeV2VpnRunnerCallback {
|
class IkeV2VpnRunner extends VpnRunner implements IkeV2VpnRunnerCallback {
|
||||||
@NonNull private static final String TAG = "IkeV2VpnRunner";
|
@NonNull private static final String TAG = "IkeV2VpnRunner";
|
||||||
|
|
||||||
|
// 5 seconds grace period before tearing down the IKE Session in case new default network
|
||||||
|
// will come up
|
||||||
|
private static final long NETWORK_LOST_TIMEOUT_MS = 5000L;
|
||||||
|
|
||||||
@NonNull private final IpSecManager mIpSecManager;
|
@NonNull private final IpSecManager mIpSecManager;
|
||||||
@NonNull private final Ikev2VpnProfile mProfile;
|
@NonNull private final Ikev2VpnProfile mProfile;
|
||||||
@NonNull private final ConnectivityManager.NetworkCallback mNetworkCallback;
|
@NonNull private final ConnectivityManager.NetworkCallback mNetworkCallback;
|
||||||
@@ -2653,24 +2692,60 @@ public class Vpn {
|
|||||||
* of the mutable Ikev2VpnRunner fields. The Ikev2VpnRunner is built mostly lock-free by
|
* of the mutable Ikev2VpnRunner fields. The Ikev2VpnRunner is built mostly lock-free by
|
||||||
* virtue of everything being serialized on this executor.
|
* virtue of everything being serialized on this executor.
|
||||||
*/
|
*/
|
||||||
@NonNull private final ExecutorService mExecutor = Executors.newSingleThreadExecutor();
|
@NonNull
|
||||||
|
private final ScheduledThreadPoolExecutor mExecutor = new ScheduledThreadPoolExecutor(1);
|
||||||
|
|
||||||
|
@Nullable private ScheduledFuture<?> mScheduledHandleNetworkLostTimeout;
|
||||||
|
@Nullable private ScheduledFuture<?> mScheduledHandleRetryIkeSessionTimeout;
|
||||||
|
|
||||||
/** Signal to ensure shutdown is honored even if a new Network is connected. */
|
/** Signal to ensure shutdown is honored even if a new Network is connected. */
|
||||||
private boolean mIsRunning = true;
|
private boolean mIsRunning = true;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The token used by the primary/current/active IKE session.
|
||||||
|
*
|
||||||
|
* <p>This token MUST be updated when the VPN switches to use a new IKE session.
|
||||||
|
*/
|
||||||
|
private int mCurrentToken = -1;
|
||||||
|
|
||||||
@Nullable private IpSecTunnelInterface mTunnelIface;
|
@Nullable private IpSecTunnelInterface mTunnelIface;
|
||||||
@Nullable private IkeSession mSession;
|
|
||||||
@Nullable private Network mActiveNetwork;
|
@Nullable private Network mActiveNetwork;
|
||||||
@Nullable private NetworkCapabilities mUnderlyingNetworkCapabilities;
|
@Nullable private NetworkCapabilities mUnderlyingNetworkCapabilities;
|
||||||
@Nullable private LinkProperties mUnderlyingLinkProperties;
|
@Nullable private LinkProperties mUnderlyingLinkProperties;
|
||||||
private final String mSessionKey;
|
private final String mSessionKey;
|
||||||
|
|
||||||
|
@Nullable private IkeSession mSession;
|
||||||
|
@Nullable private IkeSessionConnectionInfo mIkeConnectionInfo;
|
||||||
|
|
||||||
|
// mMobikeEnabled can only be updated after IKE AUTH is finished.
|
||||||
|
private boolean mMobikeEnabled = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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
|
||||||
|
* opened or when there is a new default network.
|
||||||
|
*/
|
||||||
|
private int mRetryCount = 0;
|
||||||
|
|
||||||
IkeV2VpnRunner(@NonNull Ikev2VpnProfile profile) {
|
IkeV2VpnRunner(@NonNull Ikev2VpnProfile profile) {
|
||||||
super(TAG);
|
super(TAG);
|
||||||
mProfile = profile;
|
mProfile = profile;
|
||||||
mIpSecManager = (IpSecManager) mContext.getSystemService(Context.IPSEC_SERVICE);
|
mIpSecManager = (IpSecManager) mContext.getSystemService(Context.IPSEC_SERVICE);
|
||||||
mNetworkCallback = new VpnIkev2Utils.Ikev2VpnNetworkCallback(TAG, this, mExecutor);
|
mNetworkCallback = new VpnIkev2Utils.Ikev2VpnNetworkCallback(TAG, this, mExecutor);
|
||||||
mSessionKey = UUID.randomUUID().toString();
|
mSessionKey = UUID.randomUUID().toString();
|
||||||
|
|
||||||
|
// Set the policy so that cancelled tasks will be removed from the work queue
|
||||||
|
mExecutor.setRemoveOnCancelPolicy(true);
|
||||||
|
|
||||||
|
// Set the policy so that all delayed tasks will not be executed
|
||||||
|
mExecutor.setExecuteExistingDelayedTasksAfterShutdownPolicy(false);
|
||||||
|
|
||||||
|
// To avoid hitting RejectedExecutionException upon shutdown of the mExecutor */
|
||||||
|
mExecutor.setRejectedExecutionHandler(
|
||||||
|
(r, executor) -> {
|
||||||
|
Log.d(TAG, "Runnable " + r + " rejected by the mExecutor");
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -2709,22 +2784,64 @@ public class Vpn {
|
|||||||
return Objects.equals(mActiveNetwork, network) && mIsRunning;
|
return Objects.equals(mActiveNetwork, network) && mIsRunning;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean isActiveToken(int token) {
|
||||||
|
return (mCurrentToken == token) && mIsRunning;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when an IKE session has been opened
|
||||||
|
*
|
||||||
|
* <p>This method is only ever called once per IkeSession, and MUST run on the mExecutor
|
||||||
|
* thread in order to ensure consistency of the Ikev2VpnRunner fields.
|
||||||
|
*/
|
||||||
|
public void onIkeOpened(int token, @NonNull IkeSessionConfiguration ikeConfiguration) {
|
||||||
|
if (!isActiveToken(token)) {
|
||||||
|
Log.d(TAG, "onIkeOpened called for obsolete token " + token);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
mMobikeEnabled =
|
||||||
|
ikeConfiguration.isIkeExtensionEnabled(
|
||||||
|
IkeSessionConfiguration.EXTENSION_TYPE_MOBIKE);
|
||||||
|
onIkeConnectionInfoChanged(token, ikeConfiguration.getIkeSessionConnectionInfo());
|
||||||
|
mRetryCount = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when an IKE session's {@link IkeSessionConnectionInfo} is available or updated
|
||||||
|
*
|
||||||
|
* <p>This callback is usually fired when an IKE session has been opened or migrated.
|
||||||
|
*
|
||||||
|
* <p>This method is called multiple times over the lifetime of an IkeSession, and MUST run
|
||||||
|
* on the mExecutor thread in order to ensure consistency of the Ikev2VpnRunner fields.
|
||||||
|
*/
|
||||||
|
public void onIkeConnectionInfoChanged(
|
||||||
|
int token, @NonNull IkeSessionConnectionInfo ikeConnectionInfo) {
|
||||||
|
if (!isActiveToken(token)) {
|
||||||
|
Log.d(TAG, "onIkeConnectionInfoChanged called for obsolete token " + token);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// The update on VPN and the IPsec tunnel will be done when migration is fully complete
|
||||||
|
// in onChildMigrated
|
||||||
|
mIkeConnectionInfo = ikeConnectionInfo;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called when an IKE Child session has been opened, signalling completion of the startup.
|
* Called when an IKE Child session has been opened, signalling completion of the startup.
|
||||||
*
|
*
|
||||||
* <p>This method is only ever called once per IkeSession, and MUST run on the mExecutor
|
* <p>This method is only ever called once per IkeSession, and MUST run on the mExecutor
|
||||||
* thread in order to ensure consistency of the Ikev2VpnRunner fields.
|
* thread in order to ensure consistency of the Ikev2VpnRunner fields.
|
||||||
*/
|
*/
|
||||||
public void onChildOpened(
|
public void onChildOpened(int token, @NonNull ChildSessionConfiguration childConfig) {
|
||||||
@NonNull Network network, @NonNull ChildSessionConfiguration childConfig) {
|
if (!isActiveToken(token)) {
|
||||||
if (!isActiveNetwork(network)) {
|
Log.d(TAG, "onChildOpened called for obsolete token " + token);
|
||||||
Log.d(TAG, "onOpened called for obsolete network " + network);
|
|
||||||
|
|
||||||
// Do nothing; this signals that either: (1) a new/better Network was found,
|
// Do nothing; this signals that either: (1) a new/better Network was found,
|
||||||
// and the Ikev2VpnRunner has switched to it in onDefaultNetworkChanged, or (2) this
|
// and the Ikev2VpnRunner has switched to it by restarting a new IKE session in
|
||||||
// IKE session was already shut down (exited, or an error was encountered somewhere
|
// onDefaultNetworkChanged, or (2) this IKE session was already shut down (exited,
|
||||||
// else). In both cases, all resources and sessions are torn down via
|
// or an error was encountered somewhere else). In both cases, all resources and
|
||||||
// resetIkeState().
|
// sessions are torn down via resetIkeState().
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2743,6 +2860,11 @@ public class Vpn {
|
|||||||
dnsAddrStrings.add(addr.getHostAddress());
|
dnsAddrStrings.add(addr.getHostAddress());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The actual network of this IKE session has been set up with is
|
||||||
|
// mIkeConnectionInfo.getNetwork() instead of mActiveNetwork because
|
||||||
|
// mActiveNetwork might have been updated after the setup was triggered.
|
||||||
|
final Network network = mIkeConnectionInfo.getNetwork();
|
||||||
|
|
||||||
final NetworkAgent networkAgent;
|
final NetworkAgent networkAgent;
|
||||||
final LinkProperties lp;
|
final LinkProperties lp;
|
||||||
|
|
||||||
@@ -2785,8 +2907,8 @@ public class Vpn {
|
|||||||
|
|
||||||
networkAgent.sendLinkProperties(lp);
|
networkAgent.sendLinkProperties(lp);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
Log.d(TAG, "Error in ChildOpened for network " + network, e);
|
Log.d(TAG, "Error in ChildOpened for token " + token, e);
|
||||||
onSessionLost(network, e);
|
onSessionLost(token, e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2794,19 +2916,19 @@ public class Vpn {
|
|||||||
* Called when an IPsec transform has been created, and should be applied.
|
* Called when an IPsec transform has been created, and should be applied.
|
||||||
*
|
*
|
||||||
* <p>This method is called multiple times over the lifetime of an IkeSession (or default
|
* <p>This method is called multiple times over the lifetime of an IkeSession (or default
|
||||||
* network), and is MUST always be called on the mExecutor thread in order to ensure
|
* network), and MUST always be called on the mExecutor thread in order to ensure
|
||||||
* consistency of the Ikev2VpnRunner fields.
|
* consistency of the Ikev2VpnRunner fields.
|
||||||
*/
|
*/
|
||||||
public void onChildTransformCreated(
|
public void onChildTransformCreated(
|
||||||
@NonNull Network network, @NonNull IpSecTransform transform, int direction) {
|
int token, @NonNull IpSecTransform transform, int direction) {
|
||||||
if (!isActiveNetwork(network)) {
|
if (!isActiveToken(token)) {
|
||||||
Log.d(TAG, "ChildTransformCreated for obsolete network " + network);
|
Log.d(TAG, "ChildTransformCreated for obsolete token " + token);
|
||||||
|
|
||||||
// Do nothing; this signals that either: (1) a new/better Network was found,
|
// Do nothing; this signals that either: (1) a new/better Network was found,
|
||||||
// and the Ikev2VpnRunner has switched to it in onDefaultNetworkChanged, or (2) this
|
// and the Ikev2VpnRunner has switched to it by restarting a new IKE session in
|
||||||
// IKE session was already shut down (exited, or an error was encountered somewhere
|
// onDefaultNetworkChanged, or (2) this IKE session was already shut down (exited,
|
||||||
// else). In both cases, all resources and sessions are torn down via
|
// or an error was encountered somewhere else). In both cases, all resources and
|
||||||
// resetIkeState().
|
// sessions are torn down via resetIkeState().
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2815,36 +2937,127 @@ public class Vpn {
|
|||||||
// them alive for us
|
// them alive for us
|
||||||
mIpSecManager.applyTunnelModeTransform(mTunnelIface, direction, transform);
|
mIpSecManager.applyTunnelModeTransform(mTunnelIface, direction, transform);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
Log.d(TAG, "Transform application failed for network " + network, e);
|
Log.d(TAG, "Transform application failed for token " + token, e);
|
||||||
onSessionLost(network, e);
|
onSessionLost(token, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when an IPsec transform has been created, and should be re-applied.
|
||||||
|
*
|
||||||
|
* <p>This method is called multiple times over the lifetime of an IkeSession (or default
|
||||||
|
* network), and MUST always be called on the mExecutor thread in order to ensure
|
||||||
|
* consistency of the Ikev2VpnRunner fields.
|
||||||
|
*/
|
||||||
|
public void onChildMigrated(
|
||||||
|
int token,
|
||||||
|
@NonNull IpSecTransform inTransform,
|
||||||
|
@NonNull IpSecTransform outTransform) {
|
||||||
|
if (!isActiveToken(token)) {
|
||||||
|
Log.d(TAG, "onChildMigrated for obsolete token " + token);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// The actual network of this IKE session has migrated to is
|
||||||
|
// mIkeConnectionInfo.getNetwork() instead of mActiveNetwork because mActiveNetwork
|
||||||
|
// might have been updated after the migration was triggered.
|
||||||
|
final Network network = mIkeConnectionInfo.getNetwork();
|
||||||
|
|
||||||
|
try {
|
||||||
|
synchronized (Vpn.this) {
|
||||||
|
mConfig.underlyingNetworks = new Network[] {network};
|
||||||
|
mNetworkCapabilities =
|
||||||
|
new NetworkCapabilities.Builder(mNetworkCapabilities)
|
||||||
|
.setUnderlyingNetworks(Collections.singletonList(network))
|
||||||
|
.build();
|
||||||
|
mNetworkAgent.setUnderlyingNetworks(Collections.singletonList(network));
|
||||||
|
}
|
||||||
|
|
||||||
|
mTunnelIface.setUnderlyingNetwork(network);
|
||||||
|
|
||||||
|
// Transforms do not need to be persisted; the IkeSession will keep them alive for
|
||||||
|
// us
|
||||||
|
mIpSecManager.applyTunnelModeTransform(
|
||||||
|
mTunnelIface, IpSecManager.DIRECTION_IN, inTransform);
|
||||||
|
mIpSecManager.applyTunnelModeTransform(
|
||||||
|
mTunnelIface, IpSecManager.DIRECTION_OUT, outTransform);
|
||||||
|
} catch (IOException e) {
|
||||||
|
Log.d(TAG, "Transform application failed for token " + token, e);
|
||||||
|
onSessionLost(token, e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called when a new default network is connected.
|
* Called when a new default network is connected.
|
||||||
*
|
*
|
||||||
* <p>The Ikev2VpnRunner will unconditionally switch to the new network, killing the old IKE
|
* <p>The Ikev2VpnRunner will unconditionally switch to the new network. If the IKE session
|
||||||
* state in the process, and starting a new IkeSession instance.
|
* has mobility, Ikev2VpnRunner will migrate the existing IkeSession to the new network.
|
||||||
|
* Otherwise, Ikev2VpnRunner will kill the old IKE state, and start a new IkeSession
|
||||||
|
* instance.
|
||||||
*
|
*
|
||||||
* <p>This method MUST always be called on the mExecutor thread in order to ensure
|
* <p>This method MUST always be called on the mExecutor thread in order to ensure
|
||||||
* consistency of the Ikev2VpnRunner fields.
|
* consistency of the Ikev2VpnRunner fields.
|
||||||
*/
|
*/
|
||||||
public void onDefaultNetworkChanged(@NonNull Network network) {
|
public void onDefaultNetworkChanged(@NonNull Network network) {
|
||||||
Log.d(TAG, "Starting IKEv2/IPsec session on new network: " + network);
|
Log.d(TAG, "onDefaultNetworkChanged: " + network);
|
||||||
|
|
||||||
|
// If there is a new default network brought up, cancel the retry task to prevent
|
||||||
|
// establishing an unnecessary IKE session.
|
||||||
|
cancelRetryNewIkeSessionFuture();
|
||||||
|
|
||||||
|
// If there is a new default network brought up, cancel the obsolete reset and retry
|
||||||
|
// task.
|
||||||
|
cancelHandleNetworkLostTimeout();
|
||||||
|
|
||||||
|
if (!mIsRunning) {
|
||||||
|
Log.d(TAG, "onDefaultNetworkChanged after exit");
|
||||||
|
return; // VPN has been shut down.
|
||||||
|
}
|
||||||
|
|
||||||
|
mActiveNetwork = network;
|
||||||
|
mRetryCount = 0;
|
||||||
|
|
||||||
|
startOrMigrateIkeSession(network);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Start a new IKE session.
|
||||||
|
*
|
||||||
|
* <p>This method MUST always be called on the mExecutor thread in order to ensure
|
||||||
|
* consistency of the Ikev2VpnRunner fields.
|
||||||
|
*
|
||||||
|
* @param underlyingNetwork if the value is {@code null}, which means there is no active
|
||||||
|
* network can be used, do nothing and return immediately. Otherwise, use the
|
||||||
|
* given network to start a new IKE session.
|
||||||
|
*/
|
||||||
|
private void startOrMigrateIkeSession(@Nullable Network underlyingNetwork) {
|
||||||
|
if (underlyingNetwork == null) {
|
||||||
|
Log.d(TAG, "There is no active network for starting an IKE session");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (!mIsRunning) {
|
if (mSession != null && mMobikeEnabled) {
|
||||||
Log.d(TAG, "onDefaultNetworkChanged after exit");
|
// IKE session can schedule a migration event only when IKE AUTH is finished
|
||||||
return; // VPN has been shut down.
|
// and mMobikeEnabled is true.
|
||||||
|
Log.d(
|
||||||
|
TAG,
|
||||||
|
"Migrate IKE Session with token "
|
||||||
|
+ mCurrentToken
|
||||||
|
+ " to network "
|
||||||
|
+ underlyingNetwork);
|
||||||
|
mSession.setNetwork(underlyingNetwork);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Log.d(TAG, "Start new IKE session on network " + underlyingNetwork);
|
||||||
|
|
||||||
// Clear mInterface to prevent Ikev2VpnRunner being cleared when
|
// Clear mInterface to prevent Ikev2VpnRunner being cleared when
|
||||||
// interfaceRemoved() is called.
|
// interfaceRemoved() is called.
|
||||||
mInterface = null;
|
mInterface = null;
|
||||||
// Without MOBIKE, we have no way to seamlessly migrate. Close on old
|
// Without MOBIKE, we have no way to seamlessly migrate. Close on old
|
||||||
// (non-default) network, and start the new one.
|
// (non-default) network, and start the new one.
|
||||||
resetIkeState();
|
resetIkeState();
|
||||||
mActiveNetwork = network;
|
|
||||||
|
|
||||||
// Get Ike options from IkeTunnelConnectionParams if it's available in the
|
// Get Ike options from IkeTunnelConnectionParams if it's available in the
|
||||||
// profile.
|
// profile.
|
||||||
@@ -2854,12 +3067,12 @@ public class Vpn {
|
|||||||
final ChildSessionParams childSessionParams;
|
final ChildSessionParams childSessionParams;
|
||||||
if (ikeTunConnParams != null) {
|
if (ikeTunConnParams != null) {
|
||||||
final IkeSessionParams.Builder builder = new IkeSessionParams.Builder(
|
final IkeSessionParams.Builder builder = new IkeSessionParams.Builder(
|
||||||
ikeTunConnParams.getIkeSessionParams()).setNetwork(network);
|
ikeTunConnParams.getIkeSessionParams()).setNetwork(underlyingNetwork);
|
||||||
ikeSessionParams = builder.build();
|
ikeSessionParams = builder.build();
|
||||||
childSessionParams = ikeTunConnParams.getTunnelModeChildSessionParams();
|
childSessionParams = ikeTunConnParams.getTunnelModeChildSessionParams();
|
||||||
} else {
|
} else {
|
||||||
ikeSessionParams = VpnIkev2Utils.buildIkeSessionParams(
|
ikeSessionParams = VpnIkev2Utils.buildIkeSessionParams(
|
||||||
mContext, mProfile, network);
|
mContext, mProfile, underlyingNetwork);
|
||||||
childSessionParams = VpnIkev2Utils.buildChildSessionParams(
|
childSessionParams = VpnIkev2Utils.buildChildSessionParams(
|
||||||
mProfile.getAllowedAlgorithms());
|
mProfile.getAllowedAlgorithms());
|
||||||
}
|
}
|
||||||
@@ -2867,29 +3080,50 @@ public class Vpn {
|
|||||||
// TODO: Remove the need for adding two unused addresses with
|
// TODO: Remove the need for adding two unused addresses with
|
||||||
// IPsec tunnels.
|
// IPsec tunnels.
|
||||||
final InetAddress address = InetAddress.getLocalHost();
|
final InetAddress address = InetAddress.getLocalHost();
|
||||||
|
|
||||||
|
// When onChildOpened is called and transforms are applied, it is
|
||||||
|
// guaranteed that the underlying network is still "network", because the
|
||||||
|
// all the network switch events will be deferred before onChildOpened is
|
||||||
|
// called. Thus it is safe to build a mTunnelIface before IKE setup.
|
||||||
mTunnelIface =
|
mTunnelIface =
|
||||||
mIpSecManager.createIpSecTunnelInterface(
|
mIpSecManager.createIpSecTunnelInterface(
|
||||||
address /* unused */,
|
address /* unused */, address /* unused */, underlyingNetwork);
|
||||||
address /* unused */,
|
|
||||||
network);
|
|
||||||
NetdUtils.setInterfaceUp(mNetd, mTunnelIface.getInterfaceName());
|
NetdUtils.setInterfaceUp(mNetd, mTunnelIface.getInterfaceName());
|
||||||
|
|
||||||
mSession = mIkev2SessionCreator.createIkeSession(
|
final int token = ++mCurrentToken;
|
||||||
mContext,
|
mSession =
|
||||||
ikeSessionParams,
|
mIkev2SessionCreator.createIkeSession(
|
||||||
childSessionParams,
|
mContext,
|
||||||
mExecutor,
|
ikeSessionParams,
|
||||||
new VpnIkev2Utils.IkeSessionCallbackImpl(
|
childSessionParams,
|
||||||
TAG, IkeV2VpnRunner.this, network),
|
mExecutor,
|
||||||
new VpnIkev2Utils.ChildSessionCallbackImpl(
|
new VpnIkev2Utils.IkeSessionCallbackImpl(
|
||||||
TAG, IkeV2VpnRunner.this, network));
|
TAG, IkeV2VpnRunner.this, token),
|
||||||
Log.d(TAG, "Ike Session started for network " + network);
|
new VpnIkev2Utils.ChildSessionCallbackImpl(
|
||||||
|
TAG, IkeV2VpnRunner.this, token));
|
||||||
|
Log.d(TAG, "IKE session started for token " + token);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
Log.i(TAG, "Setup failed for network " + network + ". Aborting", e);
|
Log.i(TAG, "Setup failed for token " + mCurrentToken + ". Aborting", e);
|
||||||
onSessionLost(network, e);
|
onSessionLost(mCurrentToken, e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void scheduleRetryNewIkeSession() {
|
||||||
|
final long retryDelay = mDeps.getNextRetryDelaySeconds(mRetryCount++);
|
||||||
|
Log.d(TAG, "Retry new IKE session after " + retryDelay + " seconds.");
|
||||||
|
// If the default network is lost during the retry delay, the mActiveNetwork will be
|
||||||
|
// null, and the new IKE session won't be established until there is a new default
|
||||||
|
// network bringing up.
|
||||||
|
mScheduledHandleRetryIkeSessionTimeout =
|
||||||
|
mExecutor.schedule(() -> {
|
||||||
|
startOrMigrateIkeSession(mActiveNetwork);
|
||||||
|
|
||||||
|
// Reset mScheduledHandleRetryIkeSessionTimeout since it's already run on
|
||||||
|
// executor thread.
|
||||||
|
mScheduledHandleRetryIkeSessionTimeout = null;
|
||||||
|
}, retryDelay, TimeUnit.SECONDS);
|
||||||
|
}
|
||||||
|
|
||||||
/** Called when the NetworkCapabilities of underlying network is changed */
|
/** Called when the NetworkCapabilities of underlying network is changed */
|
||||||
public void onDefaultNetworkCapabilitiesChanged(@NonNull NetworkCapabilities nc) {
|
public void onDefaultNetworkCapabilitiesChanged(@NonNull NetworkCapabilities nc) {
|
||||||
mUnderlyingNetworkCapabilities = nc;
|
mUnderlyingNetworkCapabilities = nc;
|
||||||
@@ -2900,6 +3134,99 @@ public class Vpn {
|
|||||||
mUnderlyingLinkProperties = lp;
|
mUnderlyingLinkProperties = lp;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles loss of the default underlying network
|
||||||
|
*
|
||||||
|
* <p>If the IKE Session has mobility, Ikev2VpnRunner will schedule a teardown event with a
|
||||||
|
* delay so that the IKE Session can migrate if a new network is available soon. Otherwise,
|
||||||
|
* Ikev2VpnRunner will kill the IKE session and reset the VPN.
|
||||||
|
*
|
||||||
|
* <p>This method MUST always be called on the mExecutor thread in order to ensure
|
||||||
|
* consistency of the Ikev2VpnRunner fields.
|
||||||
|
*/
|
||||||
|
public void onDefaultNetworkLost(@NonNull Network network) {
|
||||||
|
// If the default network is torn down, there is no need to call
|
||||||
|
// startOrMigrateIkeSession() since it will always check if there is an active network
|
||||||
|
// can be used or not.
|
||||||
|
cancelRetryNewIkeSessionFuture();
|
||||||
|
|
||||||
|
if (!isActiveNetwork(network)) {
|
||||||
|
Log.d(TAG, "onDefaultNetworkLost called for obsolete network " + network);
|
||||||
|
|
||||||
|
// Do nothing; this signals that either: (1) a new/better Network was found,
|
||||||
|
// and the Ikev2VpnRunner has switched to it by restarting a new IKE session in
|
||||||
|
// onDefaultNetworkChanged, or (2) this IKE session was already shut down (exited,
|
||||||
|
// or an error was encountered somewhere else). In both cases, all resources and
|
||||||
|
// sessions are torn down via resetIkeState().
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
mActiveNetwork = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mScheduledHandleNetworkLostTimeout != null
|
||||||
|
&& !mScheduledHandleNetworkLostTimeout.isCancelled()
|
||||||
|
&& !mScheduledHandleNetworkLostTimeout.isDone()) {
|
||||||
|
final IllegalStateException exception =
|
||||||
|
new IllegalStateException(
|
||||||
|
"Found a pending mScheduledHandleNetworkLostTimeout");
|
||||||
|
Log.i(
|
||||||
|
TAG,
|
||||||
|
"Unexpected error in onDefaultNetworkLost. Tear down session",
|
||||||
|
exception);
|
||||||
|
handleSessionLost(exception, network);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mSession != null && mMobikeEnabled) {
|
||||||
|
Log.d(
|
||||||
|
TAG,
|
||||||
|
"IKE Session has mobility. Delay handleSessionLost for losing network "
|
||||||
|
+ network
|
||||||
|
+ " on session with token "
|
||||||
|
+ mCurrentToken);
|
||||||
|
|
||||||
|
// Delay the teardown in case a new network will be available soon. For example,
|
||||||
|
// during handover between two WiFi networks, Android will disconnect from the
|
||||||
|
// first WiFi and then connects to the second WiFi.
|
||||||
|
mScheduledHandleNetworkLostTimeout =
|
||||||
|
mExecutor.schedule(
|
||||||
|
() -> {
|
||||||
|
handleSessionLost(null, network);
|
||||||
|
},
|
||||||
|
NETWORK_LOST_TIMEOUT_MS,
|
||||||
|
TimeUnit.MILLISECONDS);
|
||||||
|
} else {
|
||||||
|
Log.d(TAG, "Call handleSessionLost for losing network " + network);
|
||||||
|
handleSessionLost(null, network);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void cancelHandleNetworkLostTimeout() {
|
||||||
|
if (mScheduledHandleNetworkLostTimeout != null
|
||||||
|
&& !mScheduledHandleNetworkLostTimeout.isDone()) {
|
||||||
|
// It does not matter what to put in #cancel(boolean), because it is impossible
|
||||||
|
// that the task tracked by mScheduledHandleNetworkLostTimeout is
|
||||||
|
// in-progress since both that task and onDefaultNetworkChanged are submitted to
|
||||||
|
// mExecutor who has only one thread.
|
||||||
|
Log.d(TAG, "Cancel the task for handling network lost timeout");
|
||||||
|
mScheduledHandleNetworkLostTimeout.cancel(false /* mayInterruptIfRunning */);
|
||||||
|
mScheduledHandleNetworkLostTimeout = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void cancelRetryNewIkeSessionFuture() {
|
||||||
|
if (mScheduledHandleRetryIkeSessionTimeout != null
|
||||||
|
&& !mScheduledHandleRetryIkeSessionTimeout.isDone()) {
|
||||||
|
// It does not matter what to put in #cancel(boolean), because it is impossible
|
||||||
|
// that the task tracked by mScheduledHandleRetryIkeSessionTimeout is
|
||||||
|
// in-progress since both that task and onDefaultNetworkChanged are submitted to
|
||||||
|
// mExecutor who has only one thread.
|
||||||
|
Log.d(TAG, "Cancel the task for handling new ike session timeout");
|
||||||
|
mScheduledHandleRetryIkeSessionTimeout.cancel(false /* mayInterruptIfRunning */);
|
||||||
|
mScheduledHandleRetryIkeSessionTimeout = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/** Marks the state as FAILED, and disconnects. */
|
/** Marks the state as FAILED, and disconnects. */
|
||||||
private void markFailedAndDisconnect(Exception exception) {
|
private void markFailedAndDisconnect(Exception exception) {
|
||||||
synchronized (Vpn.this) {
|
synchronized (Vpn.this) {
|
||||||
@@ -2918,18 +3245,28 @@ public class Vpn {
|
|||||||
* <p>This method MUST always be called on the mExecutor thread in order to ensure
|
* <p>This method MUST always be called on the mExecutor thread in order to ensure
|
||||||
* consistency of the Ikev2VpnRunner fields.
|
* consistency of the Ikev2VpnRunner fields.
|
||||||
*/
|
*/
|
||||||
public void onSessionLost(@NonNull Network network, @Nullable Exception exception) {
|
public void onSessionLost(int token, @Nullable Exception exception) {
|
||||||
if (!isActiveNetwork(network)) {
|
Log.d(TAG, "onSessionLost() called for token " + token);
|
||||||
Log.d(TAG, "onSessionLost() called for obsolete network " + network);
|
|
||||||
|
if (!isActiveToken(token)) {
|
||||||
|
Log.d(TAG, "onSessionLost() called for obsolete token " + token);
|
||||||
|
|
||||||
// Do nothing; this signals that either: (1) a new/better Network was found,
|
// Do nothing; this signals that either: (1) a new/better Network was found,
|
||||||
// and the Ikev2VpnRunner has switched to it in onDefaultNetworkChanged, or (2) this
|
// and the Ikev2VpnRunner has switched to it by restarting a new IKE session in
|
||||||
// IKE session was already shut down (exited, or an error was encountered somewhere
|
// onDefaultNetworkChanged, or (2) this IKE session was already shut down (exited,
|
||||||
// else). In both cases, all resources and sessions are torn down via
|
// or an error was encountered somewhere else). In both cases, all resources and
|
||||||
// onSessionLost() and resetIkeState().
|
// sessions are torn down via resetIkeState().
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
handleSessionLost(exception, mActiveNetwork);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void handleSessionLost(@Nullable Exception exception, @Nullable Network network) {
|
||||||
|
// Cancel mScheduledHandleNetworkLostTimeout if the session it is going to terminate is
|
||||||
|
// already terminated due to other failures.
|
||||||
|
cancelHandleNetworkLostTimeout();
|
||||||
|
|
||||||
synchronized (Vpn.this) {
|
synchronized (Vpn.this) {
|
||||||
if (exception instanceof IkeProtocolException) {
|
if (exception instanceof IkeProtocolException) {
|
||||||
final IkeProtocolException ikeException = (IkeProtocolException) exception;
|
final IkeProtocolException ikeException = (IkeProtocolException) exception;
|
||||||
@@ -2949,7 +3286,7 @@ public class Vpn {
|
|||||||
VpnManager.ERROR_CLASS_NOT_RECOVERABLE,
|
VpnManager.ERROR_CLASS_NOT_RECOVERABLE,
|
||||||
ikeException.getErrorType(),
|
ikeException.getErrorType(),
|
||||||
getPackage(), mSessionKey, makeVpnProfileStateLocked(),
|
getPackage(), mSessionKey, makeVpnProfileStateLocked(),
|
||||||
mActiveNetwork,
|
network,
|
||||||
getRedactedNetworkCapabilitiesOfUnderlyingNetwork(
|
getRedactedNetworkCapabilitiesOfUnderlyingNetwork(
|
||||||
mUnderlyingNetworkCapabilities),
|
mUnderlyingNetworkCapabilities),
|
||||||
getRedactedLinkPropertiesOfUnderlyingNetwork(
|
getRedactedLinkPropertiesOfUnderlyingNetwork(
|
||||||
@@ -2967,7 +3304,7 @@ public class Vpn {
|
|||||||
VpnManager.ERROR_CLASS_RECOVERABLE,
|
VpnManager.ERROR_CLASS_RECOVERABLE,
|
||||||
ikeException.getErrorType(),
|
ikeException.getErrorType(),
|
||||||
getPackage(), mSessionKey, makeVpnProfileStateLocked(),
|
getPackage(), mSessionKey, makeVpnProfileStateLocked(),
|
||||||
mActiveNetwork,
|
network,
|
||||||
getRedactedNetworkCapabilitiesOfUnderlyingNetwork(
|
getRedactedNetworkCapabilitiesOfUnderlyingNetwork(
|
||||||
mUnderlyingNetworkCapabilities),
|
mUnderlyingNetworkCapabilities),
|
||||||
getRedactedLinkPropertiesOfUnderlyingNetwork(
|
getRedactedLinkPropertiesOfUnderlyingNetwork(
|
||||||
@@ -2986,7 +3323,7 @@ public class Vpn {
|
|||||||
VpnManager.ERROR_CLASS_RECOVERABLE,
|
VpnManager.ERROR_CLASS_RECOVERABLE,
|
||||||
VpnManager.ERROR_CODE_NETWORK_LOST,
|
VpnManager.ERROR_CODE_NETWORK_LOST,
|
||||||
getPackage(), mSessionKey, makeVpnProfileStateLocked(),
|
getPackage(), mSessionKey, makeVpnProfileStateLocked(),
|
||||||
mActiveNetwork,
|
network,
|
||||||
getRedactedNetworkCapabilitiesOfUnderlyingNetwork(
|
getRedactedNetworkCapabilitiesOfUnderlyingNetwork(
|
||||||
mUnderlyingNetworkCapabilities),
|
mUnderlyingNetworkCapabilities),
|
||||||
getRedactedLinkPropertiesOfUnderlyingNetwork(
|
getRedactedLinkPropertiesOfUnderlyingNetwork(
|
||||||
@@ -3001,7 +3338,7 @@ public class Vpn {
|
|||||||
VpnManager.ERROR_CLASS_RECOVERABLE,
|
VpnManager.ERROR_CLASS_RECOVERABLE,
|
||||||
VpnManager.ERROR_CODE_NETWORK_UNKNOWN_HOST,
|
VpnManager.ERROR_CODE_NETWORK_UNKNOWN_HOST,
|
||||||
getPackage(), mSessionKey, makeVpnProfileStateLocked(),
|
getPackage(), mSessionKey, makeVpnProfileStateLocked(),
|
||||||
mActiveNetwork,
|
network,
|
||||||
getRedactedNetworkCapabilitiesOfUnderlyingNetwork(
|
getRedactedNetworkCapabilitiesOfUnderlyingNetwork(
|
||||||
mUnderlyingNetworkCapabilities),
|
mUnderlyingNetworkCapabilities),
|
||||||
getRedactedLinkPropertiesOfUnderlyingNetwork(
|
getRedactedLinkPropertiesOfUnderlyingNetwork(
|
||||||
@@ -3015,7 +3352,7 @@ public class Vpn {
|
|||||||
VpnManager.ERROR_CLASS_RECOVERABLE,
|
VpnManager.ERROR_CLASS_RECOVERABLE,
|
||||||
VpnManager.ERROR_CODE_NETWORK_PROTOCOL_TIMEOUT,
|
VpnManager.ERROR_CODE_NETWORK_PROTOCOL_TIMEOUT,
|
||||||
getPackage(), mSessionKey, makeVpnProfileStateLocked(),
|
getPackage(), mSessionKey, makeVpnProfileStateLocked(),
|
||||||
mActiveNetwork,
|
network,
|
||||||
getRedactedNetworkCapabilitiesOfUnderlyingNetwork(
|
getRedactedNetworkCapabilitiesOfUnderlyingNetwork(
|
||||||
mUnderlyingNetworkCapabilities),
|
mUnderlyingNetworkCapabilities),
|
||||||
getRedactedLinkPropertiesOfUnderlyingNetwork(
|
getRedactedLinkPropertiesOfUnderlyingNetwork(
|
||||||
@@ -3029,7 +3366,7 @@ public class Vpn {
|
|||||||
VpnManager.ERROR_CLASS_RECOVERABLE,
|
VpnManager.ERROR_CLASS_RECOVERABLE,
|
||||||
VpnManager.ERROR_CODE_NETWORK_IO,
|
VpnManager.ERROR_CODE_NETWORK_IO,
|
||||||
getPackage(), mSessionKey, makeVpnProfileStateLocked(),
|
getPackage(), mSessionKey, makeVpnProfileStateLocked(),
|
||||||
mActiveNetwork,
|
network,
|
||||||
getRedactedNetworkCapabilitiesOfUnderlyingNetwork(
|
getRedactedNetworkCapabilitiesOfUnderlyingNetwork(
|
||||||
mUnderlyingNetworkCapabilities),
|
mUnderlyingNetworkCapabilities),
|
||||||
getRedactedLinkPropertiesOfUnderlyingNetwork(
|
getRedactedLinkPropertiesOfUnderlyingNetwork(
|
||||||
@@ -3039,15 +3376,16 @@ public class Vpn {
|
|||||||
} else if (exception != null) {
|
} else if (exception != null) {
|
||||||
Log.wtf(TAG, "onSessionLost: exception = " + exception);
|
Log.wtf(TAG, "onSessionLost: exception = " + exception);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
scheduleRetryNewIkeSession();
|
||||||
}
|
}
|
||||||
|
|
||||||
mActiveNetwork = null;
|
|
||||||
mUnderlyingNetworkCapabilities = null;
|
mUnderlyingNetworkCapabilities = null;
|
||||||
mUnderlyingLinkProperties = null;
|
mUnderlyingLinkProperties = null;
|
||||||
|
|
||||||
// Close all obsolete state, but keep VPN alive incase a usable network comes up.
|
// Close all obsolete state, but keep VPN alive incase a usable network comes up.
|
||||||
// (Mirrors VpnService behavior)
|
// (Mirrors VpnService behavior)
|
||||||
Log.d(TAG, "Resetting state for network: " + network);
|
Log.d(TAG, "Resetting state for token: " + mCurrentToken);
|
||||||
|
|
||||||
synchronized (Vpn.this) {
|
synchronized (Vpn.this) {
|
||||||
// Since this method handles non-fatal errors only, set mInterface to null to
|
// Since this method handles non-fatal errors only, set mInterface to null to
|
||||||
@@ -3092,6 +3430,8 @@ public class Vpn {
|
|||||||
mSession.kill(); // Kill here to make sure all resources are released immediately
|
mSession.kill(); // Kill here to make sure all resources are released immediately
|
||||||
mSession = null;
|
mSession = null;
|
||||||
}
|
}
|
||||||
|
mIkeConnectionInfo = null;
|
||||||
|
mMobikeEnabled = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -68,6 +68,7 @@ import android.net.ipsec.ike.IkeRfc822AddrIdentification;
|
|||||||
import android.net.ipsec.ike.IkeSaProposal;
|
import android.net.ipsec.ike.IkeSaProposal;
|
||||||
import android.net.ipsec.ike.IkeSessionCallback;
|
import android.net.ipsec.ike.IkeSessionCallback;
|
||||||
import android.net.ipsec.ike.IkeSessionConfiguration;
|
import android.net.ipsec.ike.IkeSessionConfiguration;
|
||||||
|
import android.net.ipsec.ike.IkeSessionConnectionInfo;
|
||||||
import android.net.ipsec.ike.IkeSessionParams;
|
import android.net.ipsec.ike.IkeSessionParams;
|
||||||
import android.net.ipsec.ike.IkeTrafficSelector;
|
import android.net.ipsec.ike.IkeTrafficSelector;
|
||||||
import android.net.ipsec.ike.TunnelModeChildSessionParams;
|
import android.net.ipsec.ike.TunnelModeChildSessionParams;
|
||||||
@@ -107,6 +108,7 @@ public class VpnIkev2Utils {
|
|||||||
new IkeSessionParams.Builder(context)
|
new IkeSessionParams.Builder(context)
|
||||||
.setServerHostname(profile.getServerAddr())
|
.setServerHostname(profile.getServerAddr())
|
||||||
.setNetwork(network)
|
.setNetwork(network)
|
||||||
|
.addIkeOption(IkeSessionParams.IKE_OPTION_MOBIKE)
|
||||||
.setLocalIdentification(localId)
|
.setLocalIdentification(localId)
|
||||||
.setRemoteIdentification(remoteId);
|
.setRemoteIdentification(remoteId);
|
||||||
setIkeAuth(profile, ikeOptionsBuilder);
|
setIkeAuth(profile, ikeOptionsBuilder);
|
||||||
@@ -298,72 +300,79 @@ public class VpnIkev2Utils {
|
|||||||
static class IkeSessionCallbackImpl implements IkeSessionCallback {
|
static class IkeSessionCallbackImpl implements IkeSessionCallback {
|
||||||
private final String mTag;
|
private final String mTag;
|
||||||
private final Vpn.IkeV2VpnRunnerCallback mCallback;
|
private final Vpn.IkeV2VpnRunnerCallback mCallback;
|
||||||
private final Network mNetwork;
|
private final int mToken;
|
||||||
|
|
||||||
IkeSessionCallbackImpl(String tag, Vpn.IkeV2VpnRunnerCallback callback, Network network) {
|
IkeSessionCallbackImpl(String tag, Vpn.IkeV2VpnRunnerCallback callback, int token) {
|
||||||
mTag = tag;
|
mTag = tag;
|
||||||
mCallback = callback;
|
mCallback = callback;
|
||||||
mNetwork = network;
|
mToken = token;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onOpened(@NonNull IkeSessionConfiguration ikeSessionConfig) {
|
public void onOpened(@NonNull IkeSessionConfiguration ikeSessionConfig) {
|
||||||
Log.d(mTag, "IkeOpened for network " + mNetwork);
|
Log.d(mTag, "IkeOpened for token " + mToken);
|
||||||
// Nothing to do here.
|
mCallback.onIkeOpened(mToken, ikeSessionConfig);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onClosed() {
|
public void onClosed() {
|
||||||
Log.d(mTag, "IkeClosed for network " + mNetwork);
|
Log.d(mTag, "IkeClosed for token " + mToken);
|
||||||
mCallback.onSessionLost(mNetwork, null); // Server requested session closure. Retry?
|
mCallback.onSessionLost(mToken, null); // Server requested session closure. Retry?
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onClosedExceptionally(@NonNull IkeException exception) {
|
public void onClosedExceptionally(@NonNull IkeException exception) {
|
||||||
Log.d(mTag, "IkeClosedExceptionally for network " + mNetwork, exception);
|
Log.d(mTag, "IkeClosedExceptionally for token " + mToken, exception);
|
||||||
mCallback.onSessionLost(mNetwork, exception);
|
mCallback.onSessionLost(mToken, exception);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onError(@NonNull IkeProtocolException exception) {
|
public void onError(@NonNull IkeProtocolException exception) {
|
||||||
Log.d(mTag, "IkeError for network " + mNetwork, exception);
|
Log.d(mTag, "IkeError for token " + mToken, exception);
|
||||||
// Non-fatal, log and continue.
|
// Non-fatal, log and continue.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onIkeSessionConnectionInfoChanged(
|
||||||
|
@NonNull IkeSessionConnectionInfo connectionInfo) {
|
||||||
|
Log.d(mTag, "onIkeSessionConnectionInfoChanged for token " + mToken);
|
||||||
|
mCallback.onIkeConnectionInfoChanged(mToken, connectionInfo);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static class ChildSessionCallbackImpl implements ChildSessionCallback {
|
static class ChildSessionCallbackImpl implements ChildSessionCallback {
|
||||||
private final String mTag;
|
private final String mTag;
|
||||||
private final Vpn.IkeV2VpnRunnerCallback mCallback;
|
private final Vpn.IkeV2VpnRunnerCallback mCallback;
|
||||||
private final Network mNetwork;
|
private final int mToken;
|
||||||
|
|
||||||
ChildSessionCallbackImpl(String tag, Vpn.IkeV2VpnRunnerCallback callback, Network network) {
|
ChildSessionCallbackImpl(String tag, Vpn.IkeV2VpnRunnerCallback callback, int token) {
|
||||||
mTag = tag;
|
mTag = tag;
|
||||||
mCallback = callback;
|
mCallback = callback;
|
||||||
mNetwork = network;
|
mToken = token;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onOpened(@NonNull ChildSessionConfiguration childConfig) {
|
public void onOpened(@NonNull ChildSessionConfiguration childConfig) {
|
||||||
Log.d(mTag, "ChildOpened for network " + mNetwork);
|
Log.d(mTag, "ChildOpened for token " + mToken);
|
||||||
mCallback.onChildOpened(mNetwork, childConfig);
|
mCallback.onChildOpened(mToken, childConfig);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onClosed() {
|
public void onClosed() {
|
||||||
Log.d(mTag, "ChildClosed for network " + mNetwork);
|
Log.d(mTag, "ChildClosed for token " + mToken);
|
||||||
mCallback.onSessionLost(mNetwork, null);
|
mCallback.onSessionLost(mToken, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onClosedExceptionally(@NonNull IkeException exception) {
|
public void onClosedExceptionally(@NonNull IkeException exception) {
|
||||||
Log.d(mTag, "ChildClosedExceptionally for network " + mNetwork, exception);
|
Log.d(mTag, "ChildClosedExceptionally for token " + mToken, exception);
|
||||||
mCallback.onSessionLost(mNetwork, exception);
|
mCallback.onSessionLost(mToken, exception);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onIpSecTransformCreated(@NonNull IpSecTransform transform, int direction) {
|
public void onIpSecTransformCreated(@NonNull IpSecTransform transform, int direction) {
|
||||||
Log.d(mTag, "ChildTransformCreated; Direction: " + direction + "; network " + mNetwork);
|
Log.d(mTag, "ChildTransformCreated; Direction: " + direction + "; token " + mToken);
|
||||||
mCallback.onChildTransformCreated(mNetwork, transform, direction);
|
mCallback.onChildTransformCreated(mToken, transform, direction);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -371,8 +380,15 @@ public class VpnIkev2Utils {
|
|||||||
// Nothing to be done; no references to the IpSecTransform are held by the
|
// Nothing to be done; no references to the IpSecTransform are held by the
|
||||||
// Ikev2VpnRunner (or this callback class), and this transform will be closed by the
|
// Ikev2VpnRunner (or this callback class), and this transform will be closed by the
|
||||||
// IKE library.
|
// IKE library.
|
||||||
Log.d(mTag,
|
Log.d(mTag, "ChildTransformDeleted; Direction: " + direction + "; for token " + mToken);
|
||||||
"ChildTransformDeleted; Direction: " + direction + "; for network " + mNetwork);
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onIpSecTransformsMigrated(
|
||||||
|
@NonNull IpSecTransform inIpSecTransform,
|
||||||
|
@NonNull IpSecTransform outIpSecTransform) {
|
||||||
|
Log.d(mTag, "ChildTransformsMigrated; token " + mToken);
|
||||||
|
mCallback.onChildMigrated(mToken, inIpSecTransform, outIpSecTransform);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -390,7 +406,7 @@ public class VpnIkev2Utils {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onAvailable(@NonNull Network network) {
|
public void onAvailable(@NonNull Network network) {
|
||||||
Log.d(mTag, "Starting IKEv2/IPsec session on new network: " + network);
|
Log.d(mTag, "onAvailable called for network: " + network);
|
||||||
mExecutor.execute(() -> mCallback.onDefaultNetworkChanged(network));
|
mExecutor.execute(() -> mCallback.onDefaultNetworkChanged(network));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -412,8 +428,8 @@ public class VpnIkev2Utils {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onLost(@NonNull Network network) {
|
public void onLost(@NonNull Network network) {
|
||||||
Log.d(mTag, "Tearing down; lost network: " + network);
|
Log.d(mTag, "onLost called for network: " + network);
|
||||||
mExecutor.execute(() -> mCallback.onSessionLost(network, null));
|
mExecutor.execute(() -> mCallback.onDefaultNetworkLost(network));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user