diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java index 5ec57cd2d9f70063e6f834155b292f3c1eac7ef6..e053c53c7afd05d0bafaed7bebf8406fe0eca819 100644 --- a/services/core/java/com/android/server/connectivity/Vpn.java +++ b/services/core/java/com/android/server/connectivity/Vpn.java @@ -2586,13 +2586,13 @@ public class Vpn { void onDefaultNetworkLinkPropertiesChanged(@NonNull LinkProperties lp); - void onChildOpened( - @NonNull Network network, @NonNull ChildSessionConfiguration childConfig); + void onDefaultNetworkLost(@NonNull Network network); - void onChildTransformCreated( - @NonNull Network network, @NonNull IpSecTransform transform, int direction); + void onChildOpened(int token, @NonNull ChildSessionConfiguration childConfig); - void onSessionLost(@NonNull Network network, @Nullable Exception exception); + void onChildTransformCreated(int token, @NonNull IpSecTransform transform, int direction); + + void onSessionLost(int token, @Nullable Exception exception); } /** @@ -2639,6 +2639,13 @@ public class Vpn { /** Signal to ensure shutdown is honored even if a new Network is connected. */ 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 IkeSession mSession; @Nullable private Network mActiveNetwork; @@ -2692,22 +2699,25 @@ public class Vpn { return Objects.equals(mActiveNetwork, network) && mIsRunning; } + private boolean isActiveToken(int token) { + return (mCurrentToken == token) && mIsRunning; + } + /** * 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 * thread in order to ensure consistency of the Ikev2VpnRunner fields. */ - public void onChildOpened( - @NonNull Network network, @NonNull ChildSessionConfiguration childConfig) { - if (!isActiveNetwork(network)) { - Log.d(TAG, "onOpened called for obsolete network " + network); + public void onChildOpened(int token, @NonNull ChildSessionConfiguration childConfig) { + if (!isActiveToken(token)) { + Log.d(TAG, "onChildOpened called for obsolete token " + token); // 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 - // 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(). + // 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; } @@ -2744,7 +2754,7 @@ public class Vpn { mConfig.dnsServers.clear(); mConfig.dnsServers.addAll(dnsAddrStrings); - mConfig.underlyingNetworks = new Network[] {network}; + mConfig.underlyingNetworks = new Network[] {mActiveNetwork}; mConfig.disallowedApplications = getAppExclusionList(mPackage); @@ -2760,7 +2770,8 @@ public class Vpn { return; // Link properties are already sent. } else { // Underlying networks also set in agentConnect() - networkAgent.setUnderlyingNetworks(Collections.singletonList(network)); + networkAgent.setUnderlyingNetworks( + Collections.singletonList(mActiveNetwork)); } lp = makeLinkProperties(); // Accesses VPN instance fields; must be locked @@ -2768,8 +2779,8 @@ public class Vpn { networkAgent.sendLinkProperties(lp); } catch (Exception e) { - Log.d(TAG, "Error in ChildOpened for network " + network, e); - onSessionLost(network, e); + Log.d(TAG, "Error in ChildOpened for token " + token, e); + onSessionLost(token, e); } } @@ -2781,15 +2792,15 @@ public class Vpn { * consistency of the Ikev2VpnRunner fields. */ public void onChildTransformCreated( - @NonNull Network network, @NonNull IpSecTransform transform, int direction) { - if (!isActiveNetwork(network)) { - Log.d(TAG, "ChildTransformCreated for obsolete network " + network); + int token, @NonNull IpSecTransform transform, int direction) { + if (!isActiveToken(token)) { + Log.d(TAG, "ChildTransformCreated for obsolete token " + token); // 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 - // 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(). + // 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; } @@ -2798,8 +2809,8 @@ public class Vpn { // them alive for us mIpSecManager.applyTunnelModeTransform(mTunnelIface, direction, transform); } catch (IOException e) { - Log.d(TAG, "Transform application failed for network " + network, e); - onSessionLost(network, e); + Log.d(TAG, "Transform application failed for token " + token, e); + onSessionLost(token, e); } } @@ -2857,19 +2868,21 @@ public class Vpn { network); NetdUtils.setInterfaceUp(mNetd, mTunnelIface.getInterfaceName()); - mSession = mIkev2SessionCreator.createIkeSession( - mContext, - ikeSessionParams, - childSessionParams, - mExecutor, - new VpnIkev2Utils.IkeSessionCallbackImpl( - TAG, IkeV2VpnRunner.this, network), - new VpnIkev2Utils.ChildSessionCallbackImpl( - TAG, IkeV2VpnRunner.this, network)); - Log.d(TAG, "Ike Session started for network " + network); + final int token = ++mCurrentToken; + mSession = + mIkev2SessionCreator.createIkeSession( + mContext, + ikeSessionParams, + childSessionParams, + mExecutor, + new VpnIkev2Utils.IkeSessionCallbackImpl( + TAG, IkeV2VpnRunner.this, token), + new VpnIkev2Utils.ChildSessionCallbackImpl( + TAG, IkeV2VpnRunner.this, token)); + Log.d(TAG, "IKE session started for token " + token); } catch (Exception e) { - Log.i(TAG, "Setup failed for network " + network + ". Aborting", e); - onSessionLost(network, e); + Log.i(TAG, "Setup failed for token " + mCurrentToken + ". Aborting", e); + onSessionLost(mCurrentToken, e); } } @@ -2883,6 +2896,29 @@ public class Vpn { mUnderlyingLinkProperties = lp; } + /** + * Handles loss of the default underlying network + * + * <p>The 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 (!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; + } + + handleSessionLost(null); + } + /** Marks the state as FAILED, and disconnects. */ private void markFailedAndDisconnect(Exception exception) { synchronized (Vpn.this) { @@ -2901,18 +2937,22 @@ public class Vpn { * <p>This method MUST always be called on the mExecutor thread in order to ensure * consistency of the Ikev2VpnRunner fields. */ - public void onSessionLost(@NonNull Network network, @Nullable Exception exception) { - if (!isActiveNetwork(network)) { - Log.d(TAG, "onSessionLost() called for obsolete network " + network); + public void onSessionLost(int token, @Nullable Exception exception) { + 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, - // and the Ikev2VpnRunner has switched to it 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 - // onSessionLost() and resetIkeState(). + // 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; } + handleSessionLost(exception); + } + + private void handleSessionLost(@Nullable Exception exception) { synchronized (Vpn.this) { if (exception instanceof IkeProtocolException) { final IkeProtocolException ikeException = (IkeProtocolException) exception; @@ -3030,7 +3070,7 @@ public class Vpn { // Close all obsolete state, but keep VPN alive incase a usable network comes up. // (Mirrors VpnService behavior) - Log.d(TAG, "Resetting state for network: " + network); + Log.d(TAG, "Resetting state for token: " + mCurrentToken); synchronized (Vpn.this) { // Since this method handles non-fatal errors only, set mInterface to null to diff --git a/services/core/java/com/android/server/connectivity/VpnIkev2Utils.java b/services/core/java/com/android/server/connectivity/VpnIkev2Utils.java index e1e488db679f78530bf43812d2226c46ee85a519..3e49afac317b03b17e012bf0d5cba8060e6f27f9 100644 --- a/services/core/java/com/android/server/connectivity/VpnIkev2Utils.java +++ b/services/core/java/com/android/server/connectivity/VpnIkev2Utils.java @@ -298,35 +298,35 @@ public class VpnIkev2Utils { static class IkeSessionCallbackImpl implements IkeSessionCallback { private final String mTag; 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; mCallback = callback; - mNetwork = network; + mToken = token; } @Override public void onOpened(@NonNull IkeSessionConfiguration ikeSessionConfig) { - Log.d(mTag, "IkeOpened for network " + mNetwork); + Log.d(mTag, "IkeOpened for token " + mToken); // Nothing to do here. } @Override public void onClosed() { - Log.d(mTag, "IkeClosed for network " + mNetwork); - mCallback.onSessionLost(mNetwork, null); // Server requested session closure. Retry? + Log.d(mTag, "IkeClosed for token " + mToken); + mCallback.onSessionLost(mToken, null); // Server requested session closure. Retry? } @Override public void onClosedExceptionally(@NonNull IkeException exception) { - Log.d(mTag, "IkeClosedExceptionally for network " + mNetwork, exception); - mCallback.onSessionLost(mNetwork, exception); + Log.d(mTag, "IkeClosedExceptionally for token " + mToken, exception); + mCallback.onSessionLost(mToken, exception); } @Override 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. } } @@ -334,36 +334,36 @@ public class VpnIkev2Utils { static class ChildSessionCallbackImpl implements ChildSessionCallback { private final String mTag; 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; mCallback = callback; - mNetwork = network; + mToken = token; } @Override public void onOpened(@NonNull ChildSessionConfiguration childConfig) { - Log.d(mTag, "ChildOpened for network " + mNetwork); - mCallback.onChildOpened(mNetwork, childConfig); + Log.d(mTag, "ChildOpened for token " + mToken); + mCallback.onChildOpened(mToken, childConfig); } @Override public void onClosed() { - Log.d(mTag, "ChildClosed for network " + mNetwork); - mCallback.onSessionLost(mNetwork, null); + Log.d(mTag, "ChildClosed for token " + mToken); + mCallback.onSessionLost(mToken, null); } @Override public void onClosedExceptionally(@NonNull IkeException exception) { - Log.d(mTag, "ChildClosedExceptionally for network " + mNetwork, exception); - mCallback.onSessionLost(mNetwork, exception); + Log.d(mTag, "ChildClosedExceptionally for token " + mToken, exception); + mCallback.onSessionLost(mToken, exception); } @Override public void onIpSecTransformCreated(@NonNull IpSecTransform transform, int direction) { - Log.d(mTag, "ChildTransformCreated; Direction: " + direction + "; network " + mNetwork); - mCallback.onChildTransformCreated(mNetwork, transform, direction); + Log.d(mTag, "ChildTransformCreated; Direction: " + direction + "; token " + mToken); + mCallback.onChildTransformCreated(mToken, transform, direction); } @Override @@ -371,8 +371,7 @@ public class VpnIkev2Utils { // 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 // IKE library. - Log.d(mTag, - "ChildTransformDeleted; Direction: " + direction + "; for network " + mNetwork); + Log.d(mTag, "ChildTransformDeleted; Direction: " + direction + "; for token " + mToken); } } @@ -412,8 +411,8 @@ public class VpnIkev2Utils { @Override public void onLost(@NonNull Network network) { - Log.d(mTag, "Tearing down; lost network: " + network); - mExecutor.execute(() -> mCallback.onSessionLost(network, null)); + Log.d(mTag, "onLost called for network: " + network); + mExecutor.execute(() -> mCallback.onDefaultNetworkLost(network)); } }