diff options
-rw-r--r-- | services/core/java/com/android/server/connectivity/Vpn.java | 217 | ||||
-rw-r--r-- | services/core/java/com/android/server/connectivity/VpnIkev2Utils.java | 24 |
2 files changed, 195 insertions, 46 deletions
diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java index 254a3226e6de..2ab68409a80f 100644 --- a/services/core/java/com/android/server/connectivity/Vpn.java +++ b/services/core/java/com/android/server/connectivity/Vpn.java @@ -88,7 +88,10 @@ import android.net.ipsec.ike.IkeSession; import android.net.ipsec.ike.IkeSessionCallback; import android.net.ipsec.ike.IkeSessionParams; import android.net.ipsec.ike.IkeTunnelConnectionParams; +import android.net.ipsec.ike.exceptions.IkeNetworkLostException; +import android.net.ipsec.ike.exceptions.IkeNonProtocolException; import android.net.ipsec.ike.exceptions.IkeProtocolException; +import android.net.ipsec.ike.exceptions.IkeTimeoutException; import android.os.Binder; import android.os.Build.VERSION_CODES; import android.os.Bundle; @@ -760,31 +763,36 @@ public class Vpn { // Also notify the new package if there was a provider change. final boolean shouldNotifyNewPkg = isVpnApp(packageName) && isPackageChanged; - if (setAlwaysOnPackageInternal(packageName, lockdown, lockdownAllowlist)) { - saveAlwaysOnPackage(); - // TODO(b/230548427): Remove SDK check once VPN related stuff are decoupled from - // ConnectivityServiceTest. - if (shouldNotifyOldPkg && SdkLevel.isAtLeastT()) { - // If both of shouldNotifyOldPkg & isPackageChanged are true, which means the - // always-on of old package is disabled or the old package is replaced with the new - // package. In this case, VpnProfileState should be disconnected. - sendEventToVpnManagerApp(VpnManager.CATEGORY_EVENT_ALWAYS_ON_STATE_CHANGED, - -1 /* errorClass */, -1 /* errorCode*/, oldPackage, - null /* sessionKey */, isPackageChanged ? makeDisconnectedVpnProfileState() - : makeVpnProfileStateLocked(), - null /* underlyingNetwork */, null /* nc */, null /* lp */); - } - // TODO(b/230548427): Remove SDK check once VPN related stuff are decoupled from - // ConnectivityServiceTest. - if (shouldNotifyNewPkg && SdkLevel.isAtLeastT()) { - sendEventToVpnManagerApp(VpnManager.CATEGORY_EVENT_ALWAYS_ON_STATE_CHANGED, - -1 /* errorClass */, -1 /* errorCode*/, packageName, - getSessionKeyLocked(), makeVpnProfileStateLocked(), - null /* underlyingNetwork */, null /* nc */, null /* lp */); - } + if (!setAlwaysOnPackageInternal(packageName, lockdown, lockdownAllowlist)) { + return false; + } + + saveAlwaysOnPackage(); + + // TODO(b/230548427): Remove SDK check once VPN related stuff are decoupled from + // ConnectivityServiceTest. + if (!SdkLevel.isAtLeastT()) { return true; } - return false; + + if (shouldNotifyOldPkg) { + // If both of shouldNotifyOldPkg & isPackageChanged are true, that means the + // always-on of old package is disabled or the old package is replaced with the new + // package. In this case, VpnProfileState should be disconnected. + sendEventToVpnManagerApp(VpnManager.CATEGORY_EVENT_ALWAYS_ON_STATE_CHANGED, + -1 /* errorClass */, -1 /* errorCode*/, oldPackage, + null /* sessionKey */, isPackageChanged ? makeDisconnectedVpnProfileState() + : makeVpnProfileStateLocked(), + null /* underlyingNetwork */, null /* nc */, null /* lp */); + } + + if (shouldNotifyNewPkg) { + sendEventToVpnManagerApp(VpnManager.CATEGORY_EVENT_ALWAYS_ON_STATE_CHANGED, + -1 /* errorClass */, -1 /* errorCode*/, packageName, + getSessionKeyLocked(), makeVpnProfileStateLocked(), + null /* underlyingNetwork */, null /* nc */, null /* lp */); + } + return true; } /** @@ -2530,6 +2538,21 @@ public class Vpn { } } + @Nullable + protected synchronized NetworkCapabilities getRedactedNetworkCapabilitiesOfUnderlyingNetwork( + NetworkCapabilities nc) { + if (nc == null) return null; + return mConnectivityManager.getRedactedNetworkCapabilitiesForPackage( + nc, mOwnerUID, mPackage); + } + + @Nullable + protected synchronized LinkProperties getRedactedLinkPropertiesOfUnderlyingNetwork( + LinkProperties lp) { + if (lp == null) return null; + return mConnectivityManager.getRedactedLinkPropertiesForPackage(lp, mOwnerUID, mPackage); + } + /** This class represents the common interface for all VPN runners. */ @VisibleForTesting abstract class VpnRunner extends Thread { @@ -2564,6 +2587,10 @@ public class Vpn { interface IkeV2VpnRunnerCallback { void onDefaultNetworkChanged(@NonNull Network network); + void onDefaultNetworkCapabilitiesChanged(@NonNull NetworkCapabilities nc); + + void onDefaultNetworkLinkPropertiesChanged(@NonNull LinkProperties lp); + void onChildOpened( @NonNull Network network, @NonNull ChildSessionConfiguration childConfig); @@ -2620,14 +2647,14 @@ public class Vpn { @Nullable private IpSecTunnelInterface mTunnelIface; @Nullable private IkeSession mSession; @Nullable private Network mActiveNetwork; + @Nullable private NetworkCapabilities mUnderlyingNetworkCapabilities; + @Nullable private LinkProperties mUnderlyingLinkProperties; private final String mSessionKey; IkeV2VpnRunner(@NonNull Ikev2VpnProfile profile) { super(TAG); mProfile = profile; mIpSecManager = (IpSecManager) mContext.getSystemService(Context.IPSEC_SERVICE); - // Pass mExecutor into Ikev2VpnNetworkCallback and make sure that IkeV2VpnRunnerCallback - // will be called by the mExecutor thread. mNetworkCallback = new VpnIkev2Utils.Ikev2VpnNetworkCallback(TAG, this, mExecutor); mSessionKey = UUID.randomUUID().toString(); } @@ -2849,6 +2876,16 @@ public class Vpn { } } + /** Called when the NetworkCapabilities of underlying network is changed */ + public void onDefaultNetworkCapabilitiesChanged(@NonNull NetworkCapabilities nc) { + mUnderlyingNetworkCapabilities = nc; + } + + /** Called when the LinkProperties of underlying network is changed */ + public void onDefaultNetworkLinkPropertiesChanged(@NonNull LinkProperties lp) { + mUnderlyingLinkProperties = lp; + } + /** Marks the state as FAILED, and disconnects. */ private void markFailedAndDisconnect(Exception exception) { synchronized (Vpn.this) { @@ -2879,28 +2916,120 @@ public class Vpn { return; } - if (exception instanceof IkeProtocolException) { - final IkeProtocolException ikeException = (IkeProtocolException) exception; - - switch (ikeException.getErrorType()) { - case IkeProtocolException.ERROR_TYPE_NO_PROPOSAL_CHOSEN: // Fallthrough - case IkeProtocolException.ERROR_TYPE_INVALID_KE_PAYLOAD: // Fallthrough - case IkeProtocolException.ERROR_TYPE_AUTHENTICATION_FAILED: // Fallthrough - case IkeProtocolException.ERROR_TYPE_SINGLE_PAIR_REQUIRED: // Fallthrough - case IkeProtocolException.ERROR_TYPE_FAILED_CP_REQUIRED: // Fallthrough - case IkeProtocolException.ERROR_TYPE_TS_UNACCEPTABLE: - // All the above failures are configuration errors, and are terminal - markFailedAndDisconnect(exception); - return; - // All other cases possibly recoverable. + synchronized (Vpn.this) { + if (exception instanceof IkeProtocolException) { + final IkeProtocolException ikeException = (IkeProtocolException) exception; + + switch (ikeException.getErrorType()) { + case IkeProtocolException.ERROR_TYPE_NO_PROPOSAL_CHOSEN: // Fallthrough + case IkeProtocolException.ERROR_TYPE_INVALID_KE_PAYLOAD: // Fallthrough + case IkeProtocolException.ERROR_TYPE_AUTHENTICATION_FAILED: // Fallthrough + case IkeProtocolException.ERROR_TYPE_SINGLE_PAIR_REQUIRED: // Fallthrough + case IkeProtocolException.ERROR_TYPE_FAILED_CP_REQUIRED: // Fallthrough + case IkeProtocolException.ERROR_TYPE_TS_UNACCEPTABLE: + // All the above failures are configuration errors, and are terminal + // TODO(b/230548427): Remove SDK check once VPN related stuff are + // decoupled from ConnectivityServiceTest. + if (SdkLevel.isAtLeastT()) { + sendEventToVpnManagerApp(VpnManager.CATEGORY_EVENT_IKE_ERROR, + VpnManager.ERROR_CLASS_NOT_RECOVERABLE, + ikeException.getErrorType(), + getPackage(), mSessionKey, makeVpnProfileStateLocked(), + mActiveNetwork, + getRedactedNetworkCapabilitiesOfUnderlyingNetwork( + mUnderlyingNetworkCapabilities), + getRedactedLinkPropertiesOfUnderlyingNetwork( + mUnderlyingLinkProperties)); + } + markFailedAndDisconnect(exception); + return; + // All other cases possibly recoverable. + default: + // All the above failures are configuration errors, and are terminal + // TODO(b/230548427): Remove SDK check once VPN related stuff are + // decoupled from ConnectivityServiceTest. + if (SdkLevel.isAtLeastT()) { + sendEventToVpnManagerApp(VpnManager.CATEGORY_EVENT_IKE_ERROR, + VpnManager.ERROR_CLASS_RECOVERABLE, + ikeException.getErrorType(), + getPackage(), mSessionKey, makeVpnProfileStateLocked(), + mActiveNetwork, + getRedactedNetworkCapabilitiesOfUnderlyingNetwork( + mUnderlyingNetworkCapabilities), + getRedactedLinkPropertiesOfUnderlyingNetwork( + mUnderlyingLinkProperties)); + } + } + } else if (exception instanceof IllegalArgumentException) { + // Failed to build IKE/ChildSessionParams; fatal profile configuration error + markFailedAndDisconnect(exception); + return; + } else if (exception instanceof IkeNetworkLostException) { + // TODO(b/230548427): Remove SDK check once VPN related stuff are + // decoupled from ConnectivityServiceTest. + if (SdkLevel.isAtLeastT()) { + sendEventToVpnManagerApp(VpnManager.CATEGORY_EVENT_NETWORK_ERROR, + VpnManager.ERROR_CLASS_RECOVERABLE, + VpnManager.ERROR_CODE_NETWORK_LOST, + getPackage(), mSessionKey, makeVpnProfileStateLocked(), + mActiveNetwork, + getRedactedNetworkCapabilitiesOfUnderlyingNetwork( + mUnderlyingNetworkCapabilities), + getRedactedLinkPropertiesOfUnderlyingNetwork( + mUnderlyingLinkProperties)); + } + } else if (exception instanceof IkeNonProtocolException) { + if (exception.getCause() instanceof UnknownHostException) { + // TODO(b/230548427): Remove SDK check once VPN related stuff are + // decoupled from ConnectivityServiceTest. + if (SdkLevel.isAtLeastT()) { + sendEventToVpnManagerApp(VpnManager.CATEGORY_EVENT_NETWORK_ERROR, + VpnManager.ERROR_CLASS_RECOVERABLE, + VpnManager.ERROR_CODE_NETWORK_UNKNOWN_HOST, + getPackage(), mSessionKey, makeVpnProfileStateLocked(), + mActiveNetwork, + getRedactedNetworkCapabilitiesOfUnderlyingNetwork( + mUnderlyingNetworkCapabilities), + getRedactedLinkPropertiesOfUnderlyingNetwork( + mUnderlyingLinkProperties)); + } + } else if (exception.getCause() instanceof IkeTimeoutException) { + // TODO(b/230548427): Remove SDK check once VPN related stuff are + // decoupled from ConnectivityServiceTest. + if (SdkLevel.isAtLeastT()) { + sendEventToVpnManagerApp(VpnManager.CATEGORY_EVENT_NETWORK_ERROR, + VpnManager.ERROR_CLASS_RECOVERABLE, + VpnManager.ERROR_CODE_NETWORK_PROTOCOL_TIMEOUT, + getPackage(), mSessionKey, makeVpnProfileStateLocked(), + mActiveNetwork, + getRedactedNetworkCapabilitiesOfUnderlyingNetwork( + mUnderlyingNetworkCapabilities), + getRedactedLinkPropertiesOfUnderlyingNetwork( + mUnderlyingLinkProperties)); + } + } else if (exception.getCause() instanceof IOException) { + // TODO(b/230548427): Remove SDK check once VPN related stuff are + // decoupled from ConnectivityServiceTest. + if (SdkLevel.isAtLeastT()) { + sendEventToVpnManagerApp(VpnManager.CATEGORY_EVENT_NETWORK_ERROR, + VpnManager.ERROR_CLASS_RECOVERABLE, + VpnManager.ERROR_CODE_NETWORK_IO, + getPackage(), mSessionKey, makeVpnProfileStateLocked(), + mActiveNetwork, + getRedactedNetworkCapabilitiesOfUnderlyingNetwork( + mUnderlyingNetworkCapabilities), + getRedactedLinkPropertiesOfUnderlyingNetwork( + mUnderlyingLinkProperties)); + } + } + } else if (exception != null) { + Log.wtf(TAG, "onSessionLost: exception = " + exception); } - } else if (exception instanceof IllegalArgumentException) { - // Failed to build IKE/ChildSessionParams; fatal profile configuration error - markFailedAndDisconnect(exception); - return; } mActiveNetwork = null; + mUnderlyingNetworkCapabilities = null; + mUnderlyingLinkProperties = null; // Close all obsolete state, but keep VPN alive incase a usable network comes up. // (Mirrors VpnService behavior) @@ -2965,6 +3094,8 @@ public class Vpn { */ private void disconnectVpnRunner() { mActiveNetwork = null; + mUnderlyingNetworkCapabilities = null; + mUnderlyingLinkProperties = null; mIsRunning = false; resetIkeState(); diff --git a/services/core/java/com/android/server/connectivity/VpnIkev2Utils.java b/services/core/java/com/android/server/connectivity/VpnIkev2Utils.java index 6982d6095689..17058282d947 100644 --- a/services/core/java/com/android/server/connectivity/VpnIkev2Utils.java +++ b/services/core/java/com/android/server/connectivity/VpnIkev2Utils.java @@ -50,7 +50,9 @@ import android.net.InetAddresses; import android.net.IpPrefix; import android.net.IpSecAlgorithm; import android.net.IpSecTransform; +import android.net.LinkProperties; import android.net.Network; +import android.net.NetworkCapabilities; import android.net.RouteInfo; import android.net.eap.EapSessionConfig; import android.net.ipsec.ike.ChildSaProposal; @@ -86,7 +88,7 @@ import java.util.Arrays; import java.util.Collection; import java.util.HashSet; import java.util.List; -import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executor; /** * Utility class to build and convert IKEv2/IPsec parameters. @@ -377,10 +379,10 @@ public class VpnIkev2Utils { static class Ikev2VpnNetworkCallback extends NetworkCallback { private final String mTag; private final Vpn.IkeV2VpnRunnerCallback mCallback; - private final ExecutorService mExecutor; + private final Executor mExecutor; Ikev2VpnNetworkCallback(String tag, Vpn.IkeV2VpnRunnerCallback callback, - ExecutorService executor) { + Executor executor) { mTag = tag; mCallback = callback; mExecutor = executor; @@ -393,6 +395,22 @@ public class VpnIkev2Utils { } @Override + public void onCapabilitiesChanged(@NonNull Network network, + @NonNull NetworkCapabilities networkCapabilities) { + Log.d(mTag, "NC changed for net " + network + " : " + networkCapabilities); + mExecutor.execute( + () -> mCallback.onDefaultNetworkCapabilitiesChanged(networkCapabilities)); + } + + @Override + public void onLinkPropertiesChanged(@NonNull Network network, + @NonNull LinkProperties linkProperties) { + Log.d(mTag, "LP changed for net " + network + " : " + linkProperties); + mExecutor.execute( + () -> mCallback.onDefaultNetworkLinkPropertiesChanged(linkProperties)); + } + + @Override public void onLost(@NonNull Network network) { Log.d(mTag, "Tearing down; lost network: " + network); mExecutor.execute(() -> mCallback.onSessionLost(network, null)); |