diff --git a/core/java/android/net/IIpSecService.aidl b/core/java/android/net/IIpSecService.aidl index d6774d47b4..b5a2a16c7f 100644 --- a/core/java/android/net/IIpSecService.aidl +++ b/core/java/android/net/IIpSecService.aidl @@ -72,4 +72,8 @@ interface IIpSecService int tunnelResourceId, int direction, int transformResourceId, in String callingPackage); void removeTransportModeTransforms(in ParcelFileDescriptor socket); + + int lockEncapSocketForNattKeepalive(int encapSocketResourceId, int requesterUid); + + void releaseNattKeepalive(int nattKeepaliveResourceId, int ownerUid); } diff --git a/services/core/java/com/android/server/IpSecService.java b/services/core/java/com/android/server/IpSecService.java index 2055b64483..c1f52552ea 100644 --- a/services/core/java/com/android/server/IpSecService.java +++ b/services/core/java/com/android/server/IpSecService.java @@ -28,8 +28,10 @@ import static android.system.OsConstants.SOCK_DGRAM; import static com.android.internal.util.Preconditions.checkNotNull; import android.annotation.NonNull; +import android.annotation.Nullable; import android.app.AppOpsManager; import android.content.Context; +import android.content.pm.PackageManager; import android.net.IIpSecService; import android.net.INetd; import android.net.IpSecAlgorithm; @@ -42,6 +44,7 @@ import android.net.IpSecTunnelInterfaceResponse; import android.net.IpSecUdpEncapResponse; import android.net.LinkAddress; import android.net.Network; +import android.net.NetworkStack; import android.net.NetworkUtils; import android.net.TrafficStats; import android.net.util.NetdService; @@ -74,6 +77,8 @@ import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.UnknownHostException; import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; import java.util.List; /** @@ -174,6 +179,14 @@ public class IpSecService extends IIpSecService.Stub { void freeUnderlyingResources() throws RemoteException; } + /** + * Sentinel value placeholder for a real binder in RefcountedResources. + * + *
Used for cases where there the allocating party is a system service, and thus is expected
+ * to track the resource lifecycles instead of IpSecService.
+ */
+ private static final Binder DUMMY_BINDER = new Binder();
+
/**
* RefcountedResource manages references and dependencies in an exclusively acyclic graph.
*
@@ -188,24 +201,42 @@ public class IpSecService extends IIpSecService.Stub {
*/
@VisibleForTesting
public class RefcountedResource This class ensures that while a NATT-keepalive is active, the UDP encap socket that it is
+ * supporting will stay open until the NATT-keepalive is finished. NATT-keepalive offload
+ * lifecycles will be managed by ConnectivityService, which will validate that the UDP Encap
+ * socket is owned by the requester, and take a reference to it via this NattKeepaliveRecord
+ *
+ * It shall be the responsibility of the caller to ensure that instances of an EncapSocket do
+ * not spawn multiple instances of NATT keepalives (and thereby register duplicate records)
+ */
+ private final class NattKeepaliveRecord extends OwnedResourceRecord {
+ NattKeepaliveRecord(int resourceId) {
+ super(resourceId);
+ }
+
+ @Override
+ @GuardedBy("IpSecService.this")
+ public void freeUnderlyingResources() {
+ Log.d(TAG, "Natt Keepalive released: " + mResourceId);
+
+ getResourceTracker().give();
+ }
+
+ @Override
+ protected ResourceTracker getResourceTracker() {
+ return getUserRecord().mNattKeepaliveQuotaTracker;
+ }
+
+ @Override
+ public void invalidate() {
+ getUserRecord().removeNattKeepaliveRecord(mResourceId);
+ }
+
+ @Override
+ public String toString() {
+ return new StringBuilder()
+ .append("{super=")
+ .append(super.toString())
+ .append("}")
+ .toString();
+ }
+ }
+
/**
* Constructs a new IpSecService instance
*
@@ -1818,6 +1911,57 @@ public class IpSecService extends IIpSecService.Stub {
}
}
+ private void verifyNetworkStackCaller() {
+ if (mContext.checkCallingOrSelfPermission(NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK)
+ != PackageManager.PERMISSION_GRANTED
+ && mContext.checkCallingOrSelfPermission(android.Manifest.permission.NETWORK_STACK)
+ != PackageManager.PERMISSION_GRANTED) {
+ throw new SecurityException(
+ "Requires permission NETWORK_STACK or MAINLINE_NETWORK_STACK");
+ }
+ }
+
+ /**
+ * Validates that a provided UID owns the encapSocket, and creates a NATT keepalive record
+ *
+ * For system server use only. Caller must have NETWORK_STACK permission
+ *
+ * @param encapSocketResourceId resource identifier of the encap socket record
+ * @param ownerUid the UID of the caller. Used to verify ownership.
+ * @return
+ */
+ public synchronized int lockEncapSocketForNattKeepalive(
+ int encapSocketResourceId, int ownerUid) {
+ verifyNetworkStackCaller();
+
+ // Verify ownership. Will throw IllegalArgumentException if the UID specified does not
+ // own the specified UDP encapsulation socket
+ UserRecord userRecord = mUserResourceTracker.getUserRecord(ownerUid);
+ RefcountedResource