| /* | |
| * Copyright (C) 2017 The Android Open Source Project | |
| * | |
| * Licensed under the Apache License, Version 2.0 (the "License"); | |
| * you may not use this file except in compliance with the License. | |
| * You may obtain a copy of the License at | |
| * | |
| * http://www.apache.org/licenses/LICENSE-2.0 | |
| * | |
| * Unless required by applicable law or agreed to in writing, software | |
| * distributed under the License is distributed on an "AS IS" BASIS, | |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| * See the License for the specific language governing permissions and | |
| * limitations under the License. | |
| */ | |
| package android.net; | |
| import static com.android.internal.util.Preconditions.checkNotNull; | |
| import android.annotation.IntDef; | |
| import android.annotation.NonNull; | |
| import android.annotation.RequiresPermission; | |
| import android.annotation.SystemService; | |
| import android.annotation.TestApi; | |
| import android.content.Context; | |
| import android.os.Binder; | |
| import android.os.ParcelFileDescriptor; | |
| import android.os.RemoteException; | |
| import android.util.AndroidException; | |
| import android.util.Log; | |
| import com.android.internal.annotations.VisibleForTesting; | |
| import dalvik.system.CloseGuard; | |
| import java.io.FileDescriptor; | |
| import java.io.IOException; | |
| import java.lang.annotation.Retention; | |
| import java.lang.annotation.RetentionPolicy; | |
| import java.net.DatagramSocket; | |
| import java.net.InetAddress; | |
| import java.net.Socket; | |
| /** | |
| * This class contains methods for managing IPsec sessions. Once configured, the kernel will apply | |
| * confidentiality (encryption) and integrity (authentication) to IP traffic. | |
| * | |
| * <p>Note that not all aspects of IPsec are permitted by this API. Applications may create | |
| * transport mode security associations and apply them to individual sockets. Applications looking | |
| * to create a VPN should use {@link VpnService}. | |
| * | |
| * @see <a href="https://tools.ietf.org/html/rfc4301">RFC 4301, Security Architecture for the | |
| * Internet Protocol</a> | |
| */ | |
| @SystemService(Context.IPSEC_SERVICE) | |
| public final class IpSecManager { | |
| private static final String TAG = "IpSecManager"; | |
| /** | |
| * Used when applying a transform to direct traffic through an {@link IpSecTransform} | |
| * towards the host. | |
| * | |
| * <p>See {@link #applyTransportModeTransform(Socket, int, IpSecTransform)}. | |
| */ | |
| public static final int DIRECTION_IN = 0; | |
| /** | |
| * Used when applying a transform to direct traffic through an {@link IpSecTransform} | |
| * away from the host. | |
| * | |
| * <p>See {@link #applyTransportModeTransform(Socket, int, IpSecTransform)}. | |
| */ | |
| public static final int DIRECTION_OUT = 1; | |
| /** @hide */ | |
| @IntDef(value = {DIRECTION_IN, DIRECTION_OUT}) | |
| @Retention(RetentionPolicy.SOURCE) | |
| public @interface PolicyDirection {} | |
| /** | |
| * The Security Parameter Index (SPI) 0 indicates an unknown or invalid index. | |
| * | |
| * <p>No IPsec packet may contain an SPI of 0. | |
| * | |
| * @hide | |
| */ | |
| @TestApi public static final int INVALID_SECURITY_PARAMETER_INDEX = 0; | |
| /** @hide */ | |
| public interface Status { | |
| public static final int OK = 0; | |
| public static final int RESOURCE_UNAVAILABLE = 1; | |
| public static final int SPI_UNAVAILABLE = 2; | |
| } | |
| /** @hide */ | |
| public static final int INVALID_RESOURCE_ID = -1; | |
| /** | |
| * Thrown to indicate that a requested SPI is in use. | |
| * | |
| * <p>The combination of remote {@code InetAddress} and SPI must be unique across all apps on | |
| * one device. If this error is encountered, a new SPI is required before a transform may be | |
| * created. This error can be avoided by calling {@link | |
| * IpSecManager#allocateSecurityParameterIndex}. | |
| */ | |
| public static final class SpiUnavailableException extends AndroidException { | |
| private final int mSpi; | |
| /** | |
| * Construct an exception indicating that a transform with the given SPI is already in use | |
| * or otherwise unavailable. | |
| * | |
| * @param msg description indicating the colliding SPI | |
| * @param spi the SPI that could not be used due to a collision | |
| */ | |
| SpiUnavailableException(String msg, int spi) { | |
| super(msg + " (spi: " + spi + ")"); | |
| mSpi = spi; | |
| } | |
| /** Get the SPI that caused a collision. */ | |
| public int getSpi() { | |
| return mSpi; | |
| } | |
| } | |
| /** | |
| * Thrown to indicate that an IPsec resource is unavailable. | |
| * | |
| * <p>This could apply to resources such as sockets, {@link SecurityParameterIndex}, {@link | |
| * IpSecTransform}, or other system resources. If this exception is thrown, users should release | |
| * allocated objects of the type requested. | |
| */ | |
| public static final class ResourceUnavailableException extends AndroidException { | |
| ResourceUnavailableException(String msg) { | |
| super(msg); | |
| } | |
| } | |
| private final Context mContext; | |
| private final IIpSecService mService; | |
| /** | |
| * This class represents a reserved SPI. | |
| * | |
| * <p>Objects of this type are used to track reserved security parameter indices. They can be | |
| * obtained by calling {@link IpSecManager#allocateSecurityParameterIndex} and must be released | |
| * by calling {@link #close()} when they are no longer needed. | |
| */ | |
| public static final class SecurityParameterIndex implements AutoCloseable { | |
| private final IIpSecService mService; | |
| private final InetAddress mDestinationAddress; | |
| private final CloseGuard mCloseGuard = CloseGuard.get(); | |
| private int mSpi = INVALID_SECURITY_PARAMETER_INDEX; | |
| private int mResourceId = INVALID_RESOURCE_ID; | |
| /** Get the underlying SPI held by this object. */ | |
| public int getSpi() { | |
| return mSpi; | |
| } | |
| /** | |
| * Release an SPI that was previously reserved. | |
| * | |
| * <p>Release an SPI for use by other users in the system. If a SecurityParameterIndex is | |
| * applied to an IpSecTransform, it will become unusable for future transforms but should | |
| * still be closed to ensure system resources are released. | |
| */ | |
| @Override | |
| public void close() { | |
| try { | |
| mService.releaseSecurityParameterIndex(mResourceId); | |
| mResourceId = INVALID_RESOURCE_ID; | |
| } catch (RemoteException e) { | |
| throw e.rethrowFromSystemServer(); | |
| } | |
| mCloseGuard.close(); | |
| } | |
| /** Check that the SPI was closed properly. */ | |
| @Override | |
| protected void finalize() throws Throwable { | |
| if (mCloseGuard != null) { | |
| mCloseGuard.warnIfOpen(); | |
| } | |
| close(); | |
| } | |
| private SecurityParameterIndex( | |
| @NonNull IIpSecService service, InetAddress destinationAddress, int spi) | |
| throws ResourceUnavailableException, SpiUnavailableException { | |
| mService = service; | |
| mDestinationAddress = destinationAddress; | |
| try { | |
| IpSecSpiResponse result = | |
| mService.allocateSecurityParameterIndex( | |
| destinationAddress.getHostAddress(), spi, new Binder()); | |
| if (result == null) { | |
| throw new NullPointerException("Received null response from IpSecService"); | |
| } | |
| int status = result.status; | |
| switch (status) { | |
| case Status.OK: | |
| break; | |
| case Status.RESOURCE_UNAVAILABLE: | |
| throw new ResourceUnavailableException( | |
| "No more SPIs may be allocated by this requester."); | |
| case Status.SPI_UNAVAILABLE: | |
| throw new SpiUnavailableException("Requested SPI is unavailable", spi); | |
| default: | |
| throw new RuntimeException( | |
| "Unknown status returned by IpSecService: " + status); | |
| } | |
| mSpi = result.spi; | |
| mResourceId = result.resourceId; | |
| if (mSpi == INVALID_SECURITY_PARAMETER_INDEX) { | |
| throw new RuntimeException("Invalid SPI returned by IpSecService: " + status); | |
| } | |
| if (mResourceId == INVALID_RESOURCE_ID) { | |
| throw new RuntimeException( | |
| "Invalid Resource ID returned by IpSecService: " + status); | |
| } | |
| } catch (RemoteException e) { | |
| throw e.rethrowFromSystemServer(); | |
| } | |
| mCloseGuard.open("open"); | |
| } | |
| /** @hide */ | |
| @VisibleForTesting | |
| public int getResourceId() { | |
| return mResourceId; | |
| } | |
| } | |
| /** | |
| * Reserve a random SPI for traffic bound to or from the specified destination address. | |
| * | |
| * <p>If successful, this SPI is guaranteed available until released by a call to {@link | |
| * SecurityParameterIndex#close()}. | |
| * | |
| * @param destinationAddress the destination address for traffic bearing the requested SPI. | |
| * For inbound traffic, the destination should be an address currently assigned on-device. | |
| * @return the reserved SecurityParameterIndex | |
| * @throws {@link #ResourceUnavailableException} indicating that too many SPIs are | |
| * currently allocated for this user | |
| */ | |
| @NonNull | |
| public SecurityParameterIndex allocateSecurityParameterIndex( | |
| @NonNull InetAddress destinationAddress) throws ResourceUnavailableException { | |
| try { | |
| return new SecurityParameterIndex( | |
| mService, | |
| destinationAddress, | |
| IpSecManager.INVALID_SECURITY_PARAMETER_INDEX); | |
| } catch (SpiUnavailableException unlikely) { | |
| throw new ResourceUnavailableException("No SPIs available"); | |
| } | |
| } | |
| /** | |
| * Reserve the requested SPI for traffic bound to or from the specified destination address. | |
| * | |
| * <p>If successful, this SPI is guaranteed available until released by a call to {@link | |
| * SecurityParameterIndex#close()}. | |
| * | |
| * @param destinationAddress the destination address for traffic bearing the requested SPI. | |
| * For inbound traffic, the destination should be an address currently assigned on-device. | |
| * @param requestedSpi the requested SPI, or '0' to allocate a random SPI. The range 1-255 is | |
| * reserved and may not be used. See RFC 4303 Section 2.1. | |
| * @return the reserved SecurityParameterIndex | |
| * @throws {@link #ResourceUnavailableException} indicating that too many SPIs are | |
| * currently allocated for this user | |
| * @throws {@link #SpiUnavailableException} indicating that the requested SPI could not be | |
| * reserved | |
| */ | |
| @NonNull | |
| public SecurityParameterIndex allocateSecurityParameterIndex( | |
| @NonNull InetAddress destinationAddress, int requestedSpi) | |
| throws SpiUnavailableException, ResourceUnavailableException { | |
| if (requestedSpi == IpSecManager.INVALID_SECURITY_PARAMETER_INDEX) { | |
| throw new IllegalArgumentException("Requested SPI must be a valid (non-zero) SPI"); | |
| } | |
| return new SecurityParameterIndex(mService, destinationAddress, requestedSpi); | |
| } | |
| /** | |
| * Apply an IPsec transform to a stream socket. | |
| * | |
| * <p>This applies transport mode encapsulation to the given socket. Once applied, I/O on the | |
| * socket will be encapsulated according to the parameters of the {@code IpSecTransform}. When | |
| * the transform is removed from the socket by calling {@link #removeTransportModeTransforms}, | |
| * unprotected traffic can resume on that socket. | |
| * | |
| * <p>For security reasons, the destination address of any traffic on the socket must match the | |
| * remote {@code InetAddress} of the {@code IpSecTransform}. Attempts to send traffic to any | |
| * other IP address will result in an IOException. In addition, reads and writes on the socket | |
| * will throw IOException if the user deactivates the transform (by calling {@link | |
| * IpSecTransform#close()}) without calling {@link #removeTransportModeTransforms}. | |
| * | |
| * <p>Note that when applied to TCP sockets, calling {@link IpSecTransform#close()} on an | |
| * applied transform before completion of graceful shutdown may result in the shutdown sequence | |
| * failing to complete. As such, applications requiring graceful shutdown MUST close the socket | |
| * prior to deactivating the applied transform. Socket closure may be performed asynchronously | |
| * (in batches), so the returning of a close function does not guarantee shutdown of a socket. | |
| * Setting an SO_LINGER timeout results in socket closure being performed synchronously, and is | |
| * sufficient to ensure shutdown. | |
| * | |
| * Specifically, if the transform is deactivated (by calling {@link IpSecTransform#close()}), | |
| * prior to the socket being closed, the standard [FIN - FIN/ACK - ACK], or the reset [RST] | |
| * packets are dropped due to the lack of a valid Transform. Similarly, if a socket without the | |
| * SO_LINGER option set is closed, the delayed/batched FIN packets may be dropped. | |
| * | |
| * <h4>Rekey Procedure</h4> | |
| * | |
| * <p>When applying a new tranform to a socket in the outbound direction, the previous transform | |
| * will be removed and the new transform will take effect immediately, sending all traffic on | |
| * the new transform; however, when applying a transform in the inbound direction, traffic | |
| * on the old transform will continue to be decrypted and delivered until that transform is | |
| * deallocated by calling {@link IpSecTransform#close()}. This overlap allows lossless rekey | |
| * procedures where both transforms are valid until both endpoints are using the new transform | |
| * and all in-flight packets have been received. | |
| * | |
| * @param socket a stream socket | |
| * @param direction the direction in which the transform should be applied | |
| * @param transform a transport mode {@code IpSecTransform} | |
| * @throws IOException indicating that the transform could not be applied | |
| */ | |
| public void applyTransportModeTransform(@NonNull Socket socket, | |
| @PolicyDirection int direction, @NonNull IpSecTransform transform) throws IOException { | |
| // Ensure creation of FD. See b/77548890 for more details. | |
| socket.getSoLinger(); | |
| applyTransportModeTransform(socket.getFileDescriptor$(), direction, transform); | |
| } | |
| /** | |
| * Apply an IPsec transform to a datagram socket. | |
| * | |
| * <p>This applies transport mode encapsulation to the given socket. Once applied, I/O on the | |
| * socket will be encapsulated according to the parameters of the {@code IpSecTransform}. When | |
| * the transform is removed from the socket by calling {@link #removeTransportModeTransforms}, | |
| * unprotected traffic can resume on that socket. | |
| * | |
| * <p>For security reasons, the destination address of any traffic on the socket must match the | |
| * remote {@code InetAddress} of the {@code IpSecTransform}. Attempts to send traffic to any | |
| * other IP address will result in an IOException. In addition, reads and writes on the socket | |
| * will throw IOException if the user deactivates the transform (by calling {@link | |
| * IpSecTransform#close()}) without calling {@link #removeTransportModeTransforms}. | |
| * | |
| * <h4>Rekey Procedure</h4> | |
| * | |
| * <p>When applying a new tranform to a socket in the outbound direction, the previous transform | |
| * will be removed and the new transform will take effect immediately, sending all traffic on | |
| * the new transform; however, when applying a transform in the inbound direction, traffic | |
| * on the old transform will continue to be decrypted and delivered until that transform is | |
| * deallocated by calling {@link IpSecTransform#close()}. This overlap allows lossless rekey | |
| * procedures where both transforms are valid until both endpoints are using the new transform | |
| * and all in-flight packets have been received. | |
| * | |
| * @param socket a datagram socket | |
| * @param direction the direction in which the transform should be applied | |
| * @param transform a transport mode {@code IpSecTransform} | |
| * @throws IOException indicating that the transform could not be applied | |
| */ | |
| public void applyTransportModeTransform(@NonNull DatagramSocket socket, | |
| @PolicyDirection int direction, @NonNull IpSecTransform transform) throws IOException { | |
| applyTransportModeTransform(socket.getFileDescriptor$(), direction, transform); | |
| } | |
| /** | |
| * Apply an IPsec transform to a socket. | |
| * | |
| * <p>This applies transport mode encapsulation to the given socket. Once applied, I/O on the | |
| * socket will be encapsulated according to the parameters of the {@code IpSecTransform}. When | |
| * the transform is removed from the socket by calling {@link #removeTransportModeTransforms}, | |
| * unprotected traffic can resume on that socket. | |
| * | |
| * <p>For security reasons, the destination address of any traffic on the socket must match the | |
| * remote {@code InetAddress} of the {@code IpSecTransform}. Attempts to send traffic to any | |
| * other IP address will result in an IOException. In addition, reads and writes on the socket | |
| * will throw IOException if the user deactivates the transform (by calling {@link | |
| * IpSecTransform#close()}) without calling {@link #removeTransportModeTransforms}. | |
| * | |
| * <p>Note that when applied to TCP sockets, calling {@link IpSecTransform#close()} on an | |
| * applied transform before completion of graceful shutdown may result in the shutdown sequence | |
| * failing to complete. As such, applications requiring graceful shutdown MUST close the socket | |
| * prior to deactivating the applied transform. Socket closure may be performed asynchronously | |
| * (in batches), so the returning of a close function does not guarantee shutdown of a socket. | |
| * Setting an SO_LINGER timeout results in socket closure being performed synchronously, and is | |
| * sufficient to ensure shutdown. | |
| * | |
| * Specifically, if the transform is deactivated (by calling {@link IpSecTransform#close()}), | |
| * prior to the socket being closed, the standard [FIN - FIN/ACK - ACK], or the reset [RST] | |
| * packets are dropped due to the lack of a valid Transform. Similarly, if a socket without the | |
| * SO_LINGER option set is closed, the delayed/batched FIN packets may be dropped. | |
| * | |
| * <h4>Rekey Procedure</h4> | |
| * | |
| * <p>When applying a new tranform to a socket in the outbound direction, the previous transform | |
| * will be removed and the new transform will take effect immediately, sending all traffic on | |
| * the new transform; however, when applying a transform in the inbound direction, traffic | |
| * on the old transform will continue to be decrypted and delivered until that transform is | |
| * deallocated by calling {@link IpSecTransform#close()}. This overlap allows lossless rekey | |
| * procedures where both transforms are valid until both endpoints are using the new transform | |
| * and all in-flight packets have been received. | |
| * | |
| * @param socket a socket file descriptor | |
| * @param direction the direction in which the transform should be applied | |
| * @param transform a transport mode {@code IpSecTransform} | |
| * @throws IOException indicating that the transform could not be applied | |
| */ | |
| public void applyTransportModeTransform(@NonNull FileDescriptor socket, | |
| @PolicyDirection int direction, @NonNull IpSecTransform transform) throws IOException { | |
| // We dup() the FileDescriptor here because if we don't, then the ParcelFileDescriptor() | |
| // constructor takes control and closes the user's FD when we exit the method. | |
| try (ParcelFileDescriptor pfd = ParcelFileDescriptor.dup(socket)) { | |
| mService.applyTransportModeTransform(pfd, direction, transform.getResourceId()); | |
| } catch (RemoteException e) { | |
| throw e.rethrowFromSystemServer(); | |
| } | |
| } | |
| /** | |
| * Remove an IPsec transform from a stream socket. | |
| * | |
| * <p>Once removed, traffic on the socket will not be encrypted. Removing transforms from a | |
| * socket allows the socket to be reused for communication in the clear. | |
| * | |
| * <p>If an {@code IpSecTransform} object applied to this socket was deallocated by calling | |
| * {@link IpSecTransform#close()}, then communication on the socket will fail until this method | |
| * is called. | |
| * | |
| * @param socket a socket that previously had a transform applied to it | |
| * @throws IOException indicating that the transform could not be removed from the socket | |
| */ | |
| public void removeTransportModeTransforms(@NonNull Socket socket) throws IOException { | |
| // Ensure creation of FD. See b/77548890 for more details. | |
| socket.getSoLinger(); | |
| removeTransportModeTransforms(socket.getFileDescriptor$()); | |
| } | |
| /** | |
| * Remove an IPsec transform from a datagram socket. | |
| * | |
| * <p>Once removed, traffic on the socket will not be encrypted. Removing transforms from a | |
| * socket allows the socket to be reused for communication in the clear. | |
| * | |
| * <p>If an {@code IpSecTransform} object applied to this socket was deallocated by calling | |
| * {@link IpSecTransform#close()}, then communication on the socket will fail until this method | |
| * is called. | |
| * | |
| * @param socket a socket that previously had a transform applied to it | |
| * @throws IOException indicating that the transform could not be removed from the socket | |
| */ | |
| public void removeTransportModeTransforms(@NonNull DatagramSocket socket) throws IOException { | |
| removeTransportModeTransforms(socket.getFileDescriptor$()); | |
| } | |
| /** | |
| * Remove an IPsec transform from a socket. | |
| * | |
| * <p>Once removed, traffic on the socket will not be encrypted. Removing transforms from a | |
| * socket allows the socket to be reused for communication in the clear. | |
| * | |
| * <p>If an {@code IpSecTransform} object applied to this socket was deallocated by calling | |
| * {@link IpSecTransform#close()}, then communication on the socket will fail until this method | |
| * is called. | |
| * | |
| * @param socket a socket that previously had a transform applied to it | |
| * @throws IOException indicating that the transform could not be removed from the socket | |
| */ | |
| public void removeTransportModeTransforms(@NonNull FileDescriptor socket) throws IOException { | |
| try (ParcelFileDescriptor pfd = ParcelFileDescriptor.dup(socket)) { | |
| mService.removeTransportModeTransforms(pfd); | |
| } catch (RemoteException e) { | |
| throw e.rethrowFromSystemServer(); | |
| } | |
| } | |
| /** | |
| * Remove a Tunnel Mode IPsec Transform from a {@link Network}. This must be used as part of | |
| * cleanup if a tunneled Network experiences a change in default route. The Network will drop | |
| * all traffic that cannot be routed to the Tunnel's outbound interface. If that interface is | |
| * lost, all traffic will drop. | |
| * | |
| * <p>TODO: Update javadoc for tunnel mode APIs at the same time the APIs are re-worked. | |
| * | |
| * @param net a network that currently has transform applied to it. | |
| * @param transform a Tunnel Mode IPsec Transform that has been previously applied to the given | |
| * network | |
| * @hide | |
| */ | |
| public void removeTunnelModeTransform(Network net, IpSecTransform transform) {} | |
| /** | |
| * This class provides access to a UDP encapsulation Socket. | |
| * | |
| * <p>{@code UdpEncapsulationSocket} wraps a system-provided datagram socket intended for IKEv2 | |
| * signalling and UDP encapsulated IPsec traffic. Instances can be obtained by calling {@link | |
| * IpSecManager#openUdpEncapsulationSocket}. The provided socket cannot be re-bound by the | |
| * caller. The caller should not close the {@code FileDescriptor} returned by {@link | |
| * #getFileDescriptor}, but should use {@link #close} instead. | |
| * | |
| * <p>Allowing the user to close or unbind a UDP encapsulation socket could impact the traffic | |
| * of the next user who binds to that port. To prevent this scenario, these sockets are held | |
| * open by the system so that they may only be closed by calling {@link #close} or when the user | |
| * process exits. | |
| */ | |
| public static final class UdpEncapsulationSocket implements AutoCloseable { | |
| private final ParcelFileDescriptor mPfd; | |
| private final IIpSecService mService; | |
| private int mResourceId = INVALID_RESOURCE_ID; | |
| private final int mPort; | |
| private final CloseGuard mCloseGuard = CloseGuard.get(); | |
| private UdpEncapsulationSocket(@NonNull IIpSecService service, int port) | |
| throws ResourceUnavailableException, IOException { | |
| mService = service; | |
| try { | |
| IpSecUdpEncapResponse result = | |
| mService.openUdpEncapsulationSocket(port, new Binder()); | |
| switch (result.status) { | |
| case Status.OK: | |
| break; | |
| case Status.RESOURCE_UNAVAILABLE: | |
| throw new ResourceUnavailableException( | |
| "No more Sockets may be allocated by this requester."); | |
| default: | |
| throw new RuntimeException( | |
| "Unknown status returned by IpSecService: " + result.status); | |
| } | |
| mResourceId = result.resourceId; | |
| mPort = result.port; | |
| mPfd = result.fileDescriptor; | |
| } catch (RemoteException e) { | |
| throw e.rethrowFromSystemServer(); | |
| } | |
| mCloseGuard.open("constructor"); | |
| } | |
| /** Get the encapsulation socket's file descriptor. */ | |
| public FileDescriptor getFileDescriptor() { | |
| if (mPfd == null) { | |
| return null; | |
| } | |
| return mPfd.getFileDescriptor(); | |
| } | |
| /** Get the bound port of the wrapped socket. */ | |
| public int getPort() { | |
| return mPort; | |
| } | |
| /** | |
| * Close this socket. | |
| * | |
| * <p>This closes the wrapped socket. Open encapsulation sockets count against a user's | |
| * resource limits, and forgetting to close them eventually will result in {@link | |
| * ResourceUnavailableException} being thrown. | |
| */ | |
| @Override | |
| public void close() throws IOException { | |
| try { | |
| mService.closeUdpEncapsulationSocket(mResourceId); | |
| mResourceId = INVALID_RESOURCE_ID; | |
| } catch (RemoteException e) { | |
| throw e.rethrowFromSystemServer(); | |
| } | |
| try { | |
| mPfd.close(); | |
| } catch (IOException e) { | |
| Log.e(TAG, "Failed to close UDP Encapsulation Socket with Port= " + mPort); | |
| throw e; | |
| } | |
| mCloseGuard.close(); | |
| } | |
| /** Check that the socket was closed properly. */ | |
| @Override | |
| protected void finalize() throws Throwable { | |
| if (mCloseGuard != null) { | |
| mCloseGuard.warnIfOpen(); | |
| } | |
| close(); | |
| } | |
| /** @hide */ | |
| @VisibleForTesting | |
| public int getResourceId() { | |
| return mResourceId; | |
| } | |
| }; | |
| /** | |
| * Open a socket for UDP encapsulation and bind to the given port. | |
| * | |
| * <p>See {@link UdpEncapsulationSocket} for the proper way to close the returned socket. | |
| * | |
| * @param port a local UDP port | |
| * @return a socket that is bound to the given port | |
| * @throws IOException indicating that the socket could not be opened or bound | |
| * @throws ResourceUnavailableException indicating that too many encapsulation sockets are open | |
| */ | |
| // Returning a socket in this fashion that has been created and bound by the system | |
| // is the only safe way to ensure that a socket is both accessible to the user and | |
| // safely usable for Encapsulation without allowing a user to possibly unbind from/close | |
| // the port, which could potentially impact the traffic of the next user who binds to that | |
| // socket. | |
| @NonNull | |
| public UdpEncapsulationSocket openUdpEncapsulationSocket(int port) | |
| throws IOException, ResourceUnavailableException { | |
| /* | |
| * Most range checking is done in the service, but this version of the constructor expects | |
| * a valid port number, and zero cannot be checked after being passed to the service. | |
| */ | |
| if (port == 0) { | |
| throw new IllegalArgumentException("Specified port must be a valid port number!"); | |
| } | |
| return new UdpEncapsulationSocket(mService, port); | |
| } | |
| /** | |
| * Open a socket for UDP encapsulation. | |
| * | |
| * <p>See {@link UdpEncapsulationSocket} for the proper way to close the returned socket. | |
| * | |
| * <p>The local port of the returned socket can be obtained by calling {@link | |
| * UdpEncapsulationSocket#getPort()}. | |
| * | |
| * @return a socket that is bound to a local port | |
| * @throws IOException indicating that the socket could not be opened or bound | |
| * @throws ResourceUnavailableException indicating that too many encapsulation sockets are open | |
| */ | |
| // Returning a socket in this fashion that has been created and bound by the system | |
| // is the only safe way to ensure that a socket is both accessible to the user and | |
| // safely usable for Encapsulation without allowing a user to possibly unbind from/close | |
| // the port, which could potentially impact the traffic of the next user who binds to that | |
| // socket. | |
| @NonNull | |
| public UdpEncapsulationSocket openUdpEncapsulationSocket() | |
| throws IOException, ResourceUnavailableException { | |
| return new UdpEncapsulationSocket(mService, 0); | |
| } | |
| /** | |
| * This class represents an IpSecTunnelInterface | |
| * | |
| * <p>IpSecTunnelInterface objects track tunnel interfaces that serve as | |
| * local endpoints for IPsec tunnels. | |
| * | |
| * <p>Creating an IpSecTunnelInterface creates a device to which IpSecTransforms may be | |
| * applied to provide IPsec security to packets sent through the tunnel. While a tunnel | |
| * cannot be used in standalone mode within Android, the higher layers may use the tunnel | |
| * to create Network objects which are accessible to the Android system. | |
| * @hide | |
| */ | |
| public static final class IpSecTunnelInterface implements AutoCloseable { | |
| private final String mOpPackageName; | |
| private final IIpSecService mService; | |
| private final InetAddress mRemoteAddress; | |
| private final InetAddress mLocalAddress; | |
| private final Network mUnderlyingNetwork; | |
| private final CloseGuard mCloseGuard = CloseGuard.get(); | |
| private String mInterfaceName; | |
| private int mResourceId = INVALID_RESOURCE_ID; | |
| /** Get the underlying SPI held by this object. */ | |
| @NonNull | |
| public String getInterfaceName() { | |
| return mInterfaceName; | |
| } | |
| /** | |
| * Add an address to the IpSecTunnelInterface | |
| * | |
| * <p>Add an address which may be used as the local inner address for | |
| * tunneled traffic. | |
| * | |
| * @param address the local address for traffic inside the tunnel | |
| * @param prefixLen length of the InetAddress prefix | |
| * @hide | |
| */ | |
| @RequiresPermission(android.Manifest.permission.MANAGE_IPSEC_TUNNELS) | |
| public void addAddress(@NonNull InetAddress address, int prefixLen) throws IOException { | |
| try { | |
| mService.addAddressToTunnelInterface( | |
| mResourceId, new LinkAddress(address, prefixLen), mOpPackageName); | |
| } catch (RemoteException e) { | |
| throw e.rethrowFromSystemServer(); | |
| } | |
| } | |
| /** | |
| * Remove an address from the IpSecTunnelInterface | |
| * | |
| * <p>Remove an address which was previously added to the IpSecTunnelInterface | |
| * | |
| * @param address to be removed | |
| * @param prefixLen length of the InetAddress prefix | |
| * @hide | |
| */ | |
| @RequiresPermission(android.Manifest.permission.MANAGE_IPSEC_TUNNELS) | |
| public void removeAddress(@NonNull InetAddress address, int prefixLen) throws IOException { | |
| try { | |
| mService.removeAddressFromTunnelInterface( | |
| mResourceId, new LinkAddress(address, prefixLen), mOpPackageName); | |
| } catch (RemoteException e) { | |
| throw e.rethrowFromSystemServer(); | |
| } | |
| } | |
| private IpSecTunnelInterface(@NonNull Context ctx, @NonNull IIpSecService service, | |
| @NonNull InetAddress localAddress, @NonNull InetAddress remoteAddress, | |
| @NonNull Network underlyingNetwork) | |
| throws ResourceUnavailableException, IOException { | |
| mOpPackageName = ctx.getOpPackageName(); | |
| mService = service; | |
| mLocalAddress = localAddress; | |
| mRemoteAddress = remoteAddress; | |
| mUnderlyingNetwork = underlyingNetwork; | |
| try { | |
| IpSecTunnelInterfaceResponse result = | |
| mService.createTunnelInterface( | |
| localAddress.getHostAddress(), | |
| remoteAddress.getHostAddress(), | |
| underlyingNetwork, | |
| new Binder(), | |
| mOpPackageName); | |
| switch (result.status) { | |
| case Status.OK: | |
| break; | |
| case Status.RESOURCE_UNAVAILABLE: | |
| throw new ResourceUnavailableException( | |
| "No more tunnel interfaces may be allocated by this requester."); | |
| default: | |
| throw new RuntimeException( | |
| "Unknown status returned by IpSecService: " + result.status); | |
| } | |
| mResourceId = result.resourceId; | |
| mInterfaceName = result.interfaceName; | |
| } catch (RemoteException e) { | |
| throw e.rethrowFromSystemServer(); | |
| } | |
| mCloseGuard.open("constructor"); | |
| } | |
| /** | |
| * Delete an IpSecTunnelInterface | |
| * | |
| * <p>Calling close will deallocate the IpSecTunnelInterface and all of its system | |
| * resources. Any packets bound for this interface either inbound or outbound will | |
| * all be lost. | |
| */ | |
| @Override | |
| public void close() { | |
| try { | |
| mService.deleteTunnelInterface(mResourceId, mOpPackageName); | |
| mResourceId = INVALID_RESOURCE_ID; | |
| } catch (RemoteException e) { | |
| throw e.rethrowFromSystemServer(); | |
| } | |
| mCloseGuard.close(); | |
| } | |
| /** Check that the Interface was closed properly. */ | |
| @Override | |
| protected void finalize() throws Throwable { | |
| if (mCloseGuard != null) { | |
| mCloseGuard.warnIfOpen(); | |
| } | |
| close(); | |
| } | |
| /** @hide */ | |
| @VisibleForTesting | |
| public int getResourceId() { | |
| return mResourceId; | |
| } | |
| } | |
| /** | |
| * Create a new IpSecTunnelInterface as a local endpoint for tunneled IPsec traffic. | |
| * | |
| * <p>An application that creates tunnels is responsible for cleaning up the tunnel when the | |
| * underlying network goes away, and the onLost() callback is received. | |
| * | |
| * @param localAddress The local addres of the tunnel | |
| * @param remoteAddress The local addres of the tunnel | |
| * @param underlyingNetwork the {@link Network} that will carry traffic for this tunnel. | |
| * This network should almost certainly be a network such as WiFi with an L2 address. | |
| * @return a new {@link IpSecManager#IpSecTunnelInterface} with the specified properties | |
| * @throws IOException indicating that the socket could not be opened or bound | |
| * @throws ResourceUnavailableException indicating that too many encapsulation sockets are open | |
| * @hide | |
| */ | |
| @NonNull | |
| @RequiresPermission(android.Manifest.permission.MANAGE_IPSEC_TUNNELS) | |
| public IpSecTunnelInterface createIpSecTunnelInterface(@NonNull InetAddress localAddress, | |
| @NonNull InetAddress remoteAddress, @NonNull Network underlyingNetwork) | |
| throws ResourceUnavailableException, IOException { | |
| return new IpSecTunnelInterface( | |
| mContext, mService, localAddress, remoteAddress, underlyingNetwork); | |
| } | |
| /** | |
| * Apply an active Tunnel Mode IPsec Transform to a {@link IpSecTunnelInterface}, which will | |
| * tunnel all traffic for the given direction through the underlying network's interface with | |
| * IPsec (applies an outer IP header and IPsec Header to all traffic, and expects an additional | |
| * IP header and IPsec Header on all inbound traffic). | |
| * <p>Applications should probably not use this API directly. | |
| * | |
| * | |
| * @param tunnel The {@link IpSecManager#IpSecTunnelInterface} that will use the supplied | |
| * transform. | |
| * @param direction the direction, {@link DIRECTION_OUT} or {@link #DIRECTION_IN} in which | |
| * the transform will be used. | |
| * @param transform an {@link IpSecTransform} created in tunnel mode | |
| * @throws IOException indicating that the transform could not be applied due to a lower | |
| * layer failure. | |
| * @hide | |
| */ | |
| @RequiresPermission(android.Manifest.permission.MANAGE_IPSEC_TUNNELS) | |
| public void applyTunnelModeTransform(@NonNull IpSecTunnelInterface tunnel, | |
| @PolicyDirection int direction, @NonNull IpSecTransform transform) throws IOException { | |
| try { | |
| mService.applyTunnelModeTransform( | |
| tunnel.getResourceId(), direction, | |
| transform.getResourceId(), mContext.getOpPackageName()); | |
| } catch (RemoteException e) { | |
| throw e.rethrowFromSystemServer(); | |
| } | |
| } | |
| /** | |
| * Construct an instance of IpSecManager within an application context. | |
| * | |
| * @param context the application context for this manager | |
| * @hide | |
| */ | |
| public IpSecManager(Context ctx, IIpSecService service) { | |
| mContext = ctx; | |
| mService = checkNotNull(service, "missing service"); | |
| } | |
| } |