Merge "Add API Surface for creating IpSec Transforms"
am: 7645fa2816 Change-Id: Ibe70a60c5c5808a7877145cccefcc1f11bfacc51
This commit is contained in:
181
core/java/android/net/IpSecAlgorithm.java
Normal file
181
core/java/android/net/IpSecAlgorithm.java
Normal file
@@ -0,0 +1,181 @@
|
||||
/*
|
||||
* 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 android.annotation.StringDef;
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
|
||||
/**
|
||||
* IpSecAlgorithm specifies a single algorithm that can be applied to an IpSec Transform. Refer to
|
||||
* RFC 4301.
|
||||
*/
|
||||
public final class IpSecAlgorithm implements Parcelable {
|
||||
|
||||
/**
|
||||
* AES-CBC Encryption/Ciphering Algorithm.
|
||||
*
|
||||
* <p>Valid lengths for this key are {128, 192, 256}.
|
||||
*/
|
||||
public static final String ALGO_CRYPT_AES_CBC = "cbc(aes)";
|
||||
|
||||
/**
|
||||
* MD5 HMAC Authentication/Integrity Algorithm. This algorithm is not recommended for use in new
|
||||
* applications and is provided for legacy compatibility with 3gpp infrastructure.
|
||||
*
|
||||
* <p>Valid truncation lengths are multiples of 8 bits from 96 to (default) 128.
|
||||
*/
|
||||
public static final String ALGO_AUTH_HMAC_MD5 = "hmac(md5)";
|
||||
|
||||
/**
|
||||
* SHA1 HMAC Authentication/Integrity Algorithm. This algorithm is not recommended for use in
|
||||
* new applications and is provided for legacy compatibility with 3gpp infrastructure.
|
||||
*
|
||||
* <p>Valid truncation lengths are multiples of 8 bits from 96 to (default) 160.
|
||||
*/
|
||||
public static final String ALGO_AUTH_HMAC_SHA1 = "hmac(sha1)";
|
||||
|
||||
/**
|
||||
* SHA256 HMAC Authentication/Integrity Algorithm.
|
||||
*
|
||||
* <p>Valid truncation lengths are multiples of 8 bits from 96 to (default) 256.
|
||||
*/
|
||||
public static final String ALGO_AUTH_HMAC_SHA256 = "hmac(sha256)";
|
||||
|
||||
/**
|
||||
* SHA384 HMAC Authentication/Integrity Algorithm.
|
||||
*
|
||||
* <p>Valid truncation lengths are multiples of 8 bits from 192 to (default) 384.
|
||||
*/
|
||||
public static final String ALGO_AUTH_HMAC_SHA384 = "hmac(sha384)";
|
||||
/**
|
||||
* SHA512 HMAC Authentication/Integrity Algorithm
|
||||
*
|
||||
* <p>Valid truncation lengths are multiples of 8 bits from 256 to (default) 512.
|
||||
*/
|
||||
public static final String ALGO_AUTH_HMAC_SHA512 = "hmac(sha512)";
|
||||
|
||||
/** @hide */
|
||||
@StringDef({
|
||||
ALGO_CRYPT_AES_CBC,
|
||||
ALGO_AUTH_HMAC_MD5,
|
||||
ALGO_AUTH_HMAC_SHA1,
|
||||
ALGO_AUTH_HMAC_SHA256,
|
||||
ALGO_AUTH_HMAC_SHA512
|
||||
})
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
public @interface AlgorithmName {}
|
||||
|
||||
private final String mName;
|
||||
private final byte[] mKey;
|
||||
private final int mTruncLenBits;
|
||||
|
||||
/**
|
||||
* Specify a IpSecAlgorithm of one of the supported types including the truncation length of the
|
||||
* algorithm
|
||||
*
|
||||
* @param algorithm type for IpSec.
|
||||
* @param key non-null Key padded to a multiple of 8 bits.
|
||||
*/
|
||||
public IpSecAlgorithm(String algorithm, byte[] key) {
|
||||
this(algorithm, key, key.length * 8);
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify a IpSecAlgorithm of one of the supported types including the truncation length of the
|
||||
* algorithm
|
||||
*
|
||||
* @param algoName precise name of the algorithm to be used.
|
||||
* @param key non-null Key padded to a multiple of 8 bits.
|
||||
* @param truncLenBits the number of bits of output hash to use; only meaningful for
|
||||
* Authentication.
|
||||
*/
|
||||
public IpSecAlgorithm(@AlgorithmName String algoName, byte[] key, int truncLenBits) {
|
||||
if (!isTruncationLengthValid(algoName, truncLenBits)) {
|
||||
throw new IllegalArgumentException("Unknown algorithm or invalid length");
|
||||
}
|
||||
mName = algoName;
|
||||
mKey = key.clone();
|
||||
mTruncLenBits = Math.min(truncLenBits, key.length * 8);
|
||||
}
|
||||
|
||||
/** Retrieve the algorithm name */
|
||||
public String getName() {
|
||||
return mName;
|
||||
}
|
||||
|
||||
/** Retrieve the key for this algorithm */
|
||||
public byte[] getKey() {
|
||||
return mKey.clone();
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the truncation length, in bits, for the key in this algo. By default this will be
|
||||
* the length in bits of the key.
|
||||
*/
|
||||
public int getTruncationLengthBits() {
|
||||
return mTruncLenBits;
|
||||
}
|
||||
|
||||
/* Parcelable Implementation */
|
||||
public int describeContents() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** Write to parcel */
|
||||
public void writeToParcel(Parcel out, int flags) {
|
||||
out.writeString(mName);
|
||||
out.writeByteArray(mKey);
|
||||
out.writeInt(mTruncLenBits);
|
||||
}
|
||||
|
||||
/** Parcelable Creator */
|
||||
public static final Parcelable.Creator<IpSecAlgorithm> CREATOR =
|
||||
new Parcelable.Creator<IpSecAlgorithm>() {
|
||||
public IpSecAlgorithm createFromParcel(Parcel in) {
|
||||
return new IpSecAlgorithm(in);
|
||||
}
|
||||
|
||||
public IpSecAlgorithm[] newArray(int size) {
|
||||
return new IpSecAlgorithm[size];
|
||||
}
|
||||
};
|
||||
|
||||
private IpSecAlgorithm(Parcel in) {
|
||||
mName = in.readString();
|
||||
mKey = in.createByteArray();
|
||||
mTruncLenBits = in.readInt();
|
||||
}
|
||||
|
||||
private static boolean isTruncationLengthValid(String algo, int truncLenBits) {
|
||||
switch (algo) {
|
||||
case ALGO_AUTH_HMAC_MD5:
|
||||
return (truncLenBits >= 96 && truncLenBits <= 128);
|
||||
case ALGO_AUTH_HMAC_SHA1:
|
||||
return (truncLenBits >= 96 && truncLenBits <= 160);
|
||||
case ALGO_AUTH_HMAC_SHA256:
|
||||
return (truncLenBits >= 96 && truncLenBits <= 256);
|
||||
case ALGO_AUTH_HMAC_SHA384:
|
||||
return (truncLenBits >= 192 && truncLenBits <= 384);
|
||||
case ALGO_AUTH_HMAC_SHA512:
|
||||
return (truncLenBits >= 256 && truncLenBits <= 512);
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
};
|
||||
20
core/java/android/net/IpSecConfig.aidl
Normal file
20
core/java/android/net/IpSecConfig.aidl
Normal file
@@ -0,0 +1,20 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
/** @hide */
|
||||
parcelable IpSecConfig;
|
||||
197
core/java/android/net/IpSecConfig.java
Normal file
197
core/java/android/net/IpSecConfig.java
Normal file
@@ -0,0 +1,197 @@
|
||||
/*
|
||||
* 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 android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
import android.util.Log;
|
||||
import java.net.InetAddress;
|
||||
import java.net.UnknownHostException;
|
||||
|
||||
/** @hide */
|
||||
public final class IpSecConfig implements Parcelable {
|
||||
private static final String TAG = IpSecConfig.class.getSimpleName();
|
||||
|
||||
//MODE_TRANSPORT or MODE_TUNNEL
|
||||
int mode;
|
||||
|
||||
// For tunnel mode
|
||||
InetAddress localAddress;
|
||||
|
||||
InetAddress remoteAddress;
|
||||
|
||||
// Limit selection by network interface
|
||||
Network network;
|
||||
|
||||
public static class Flow {
|
||||
// Minimum requirements for identifying a transform
|
||||
// SPI identifying the IPsec flow in packet processing
|
||||
// and a remote IP address
|
||||
int spi;
|
||||
|
||||
// Encryption Algorithm
|
||||
IpSecAlgorithm encryptionAlgo;
|
||||
|
||||
// Authentication Algorithm
|
||||
IpSecAlgorithm authenticationAlgo;
|
||||
}
|
||||
|
||||
Flow[] flow = new Flow[2];
|
||||
|
||||
// For tunnel mode IPv4 UDP Encapsulation
|
||||
// IpSecTransform#ENCAP_ESP_*, such as ENCAP_ESP_OVER_UDP_IKE
|
||||
int encapType;
|
||||
int encapLocalPort;
|
||||
int encapRemotePort;
|
||||
|
||||
// An optional protocol to match with the selector
|
||||
int selectorProto;
|
||||
|
||||
// A bitmask of FEATURE_* indicating which of the fields
|
||||
// of this class are valid.
|
||||
long features;
|
||||
|
||||
// An interval, in seconds between the NattKeepalive packets
|
||||
int nattKeepaliveInterval;
|
||||
|
||||
public InetAddress getLocalIp() {
|
||||
return localAddress;
|
||||
}
|
||||
|
||||
public int getSpi(int direction) {
|
||||
return flow[direction].spi;
|
||||
}
|
||||
|
||||
public InetAddress getRemoteIp() {
|
||||
return remoteAddress;
|
||||
}
|
||||
|
||||
public IpSecAlgorithm getEncryptionAlgo(int direction) {
|
||||
return flow[direction].encryptionAlgo;
|
||||
}
|
||||
|
||||
public IpSecAlgorithm getAuthenticationAlgo(int direction) {
|
||||
return flow[direction].authenticationAlgo;
|
||||
}
|
||||
|
||||
Network getNetwork() {
|
||||
return network;
|
||||
}
|
||||
|
||||
public int getEncapType() {
|
||||
return encapType;
|
||||
}
|
||||
|
||||
public int getEncapLocalPort() {
|
||||
return encapLocalPort;
|
||||
}
|
||||
|
||||
public int getEncapRemotePort() {
|
||||
return encapRemotePort;
|
||||
}
|
||||
|
||||
public int getSelectorProto() {
|
||||
return selectorProto;
|
||||
}
|
||||
|
||||
int getNattKeepaliveInterval() {
|
||||
return nattKeepaliveInterval;
|
||||
}
|
||||
|
||||
public boolean hasProperty(int featureBits) {
|
||||
return (features & featureBits) == featureBits;
|
||||
}
|
||||
|
||||
// Parcelable Methods
|
||||
|
||||
@Override
|
||||
public int describeContents() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeToParcel(Parcel out, int flags) {
|
||||
out.writeLong(features);
|
||||
// TODO: Use a byte array or other better method for storing IPs that can also include scope
|
||||
out.writeString((localAddress != null) ? localAddress.getHostAddress() : null);
|
||||
// TODO: Use a byte array or other better method for storing IPs that can also include scope
|
||||
out.writeString((remoteAddress != null) ? remoteAddress.getHostAddress() : null);
|
||||
out.writeParcelable(network, flags);
|
||||
out.writeInt(flow[IpSecTransform.DIRECTION_IN].spi);
|
||||
out.writeParcelable(flow[IpSecTransform.DIRECTION_IN].encryptionAlgo, flags);
|
||||
out.writeParcelable(flow[IpSecTransform.DIRECTION_IN].authenticationAlgo, flags);
|
||||
out.writeInt(flow[IpSecTransform.DIRECTION_OUT].spi);
|
||||
out.writeParcelable(flow[IpSecTransform.DIRECTION_OUT].encryptionAlgo, flags);
|
||||
out.writeParcelable(flow[IpSecTransform.DIRECTION_OUT].authenticationAlgo, flags);
|
||||
out.writeInt(encapType);
|
||||
out.writeInt(encapLocalPort);
|
||||
out.writeInt(encapRemotePort);
|
||||
out.writeInt(selectorProto);
|
||||
}
|
||||
|
||||
// Package Private: Used by the IpSecTransform.Builder;
|
||||
// there should be no public constructor for this object
|
||||
IpSecConfig() {
|
||||
flow[IpSecTransform.DIRECTION_IN].spi = 0;
|
||||
flow[IpSecTransform.DIRECTION_OUT].spi = 0;
|
||||
nattKeepaliveInterval = 0; //FIXME constant
|
||||
}
|
||||
|
||||
private static InetAddress readInetAddressFromParcel(Parcel in) {
|
||||
String addrString = in.readString();
|
||||
if (addrString == null) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
return InetAddress.getByName(addrString);
|
||||
} catch (UnknownHostException e) {
|
||||
Log.wtf(TAG, "Invalid IpAddress " + addrString);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private IpSecConfig(Parcel in) {
|
||||
features = in.readLong();
|
||||
localAddress = readInetAddressFromParcel(in);
|
||||
remoteAddress = readInetAddressFromParcel(in);
|
||||
network = (Network) in.readParcelable(Network.class.getClassLoader());
|
||||
flow[IpSecTransform.DIRECTION_IN].spi = in.readInt();
|
||||
flow[IpSecTransform.DIRECTION_IN].encryptionAlgo =
|
||||
(IpSecAlgorithm) in.readParcelable(IpSecAlgorithm.class.getClassLoader());
|
||||
flow[IpSecTransform.DIRECTION_IN].authenticationAlgo =
|
||||
(IpSecAlgorithm) in.readParcelable(IpSecAlgorithm.class.getClassLoader());
|
||||
flow[IpSecTransform.DIRECTION_OUT].spi = in.readInt();
|
||||
flow[IpSecTransform.DIRECTION_OUT].encryptionAlgo =
|
||||
(IpSecAlgorithm) in.readParcelable(IpSecAlgorithm.class.getClassLoader());
|
||||
flow[IpSecTransform.DIRECTION_OUT].authenticationAlgo =
|
||||
(IpSecAlgorithm) in.readParcelable(IpSecAlgorithm.class.getClassLoader());
|
||||
encapType = in.readInt();
|
||||
encapLocalPort = in.readInt();
|
||||
encapRemotePort = in.readInt();
|
||||
selectorProto = in.readInt();
|
||||
}
|
||||
|
||||
public static final Parcelable.Creator<IpSecConfig> CREATOR =
|
||||
new Parcelable.Creator<IpSecConfig>() {
|
||||
public IpSecConfig createFromParcel(Parcel in) {
|
||||
return new IpSecConfig(in);
|
||||
}
|
||||
|
||||
public IpSecConfig[] newArray(int size) {
|
||||
return new IpSecConfig[size];
|
||||
}
|
||||
};
|
||||
}
|
||||
379
core/java/android/net/IpSecManager.java
Normal file
379
core/java/android/net/IpSecManager.java
Normal file
@@ -0,0 +1,379 @@
|
||||
/*
|
||||
* 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.SystemApi;
|
||||
import android.content.Context;
|
||||
import android.os.INetworkManagementService;
|
||||
import android.os.ParcelFileDescriptor;
|
||||
import android.util.AndroidException;
|
||||
import dalvik.system.CloseGuard;
|
||||
import java.io.FileDescriptor;
|
||||
import java.io.IOException;
|
||||
import java.net.DatagramSocket;
|
||||
import java.net.InetAddress;
|
||||
import java.net.Socket;
|
||||
|
||||
/**
|
||||
* This class contains methods for managing IPsec sessions, which will perform kernel-space
|
||||
* encryption and decryption of socket or Network traffic.
|
||||
*
|
||||
* <p>An IpSecManager may be obtained by calling {@link
|
||||
* android.content.Context#getSystemService(String) Context#getSystemService(String)} with {@link
|
||||
* android.content.Context#IPSEC_SERVICE Context#IPSEC_SERVICE}
|
||||
*/
|
||||
public final class IpSecManager {
|
||||
private static final String TAG = "IpSecManager";
|
||||
|
||||
/**
|
||||
* Indicates that the combination of remote InetAddress and SPI was non-unique for a given
|
||||
* request. If encountered, selection of a new SPI is required before a transform may be
|
||||
* created. Note, this should happen very rarely if the SPI is chosen to be sufficiently random
|
||||
* or reserved using reserveSecurityParameterIndex.
|
||||
*/
|
||||
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;
|
||||
}
|
||||
|
||||
/** Retrieve the SPI that caused a collision */
|
||||
public int getSpi() {
|
||||
return mSpi;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates that the requested system resource for IPsec, such as a socket or other system
|
||||
* resource is unavailable. If this exception is thrown, try releasing allocated objects of the
|
||||
* type requested.
|
||||
*/
|
||||
public static final class ResourceUnavailableException extends AndroidException {
|
||||
|
||||
ResourceUnavailableException(String msg) {
|
||||
super(msg);
|
||||
}
|
||||
}
|
||||
|
||||
private final Context mContext;
|
||||
private final INetworkManagementService mService;
|
||||
|
||||
public static final class SecurityParameterIndex implements AutoCloseable {
|
||||
private final Context mContext;
|
||||
private final InetAddress mDestinationAddress;
|
||||
private final CloseGuard mCloseGuard = CloseGuard.get();
|
||||
private int mSpi;
|
||||
|
||||
/** Return the underlying SPI held by this object */
|
||||
public int getSpi() {
|
||||
return mSpi;
|
||||
}
|
||||
|
||||
private SecurityParameterIndex(Context context, InetAddress destinationAddress, int spi)
|
||||
throws ResourceUnavailableException, SpiUnavailableException {
|
||||
mContext = context;
|
||||
mDestinationAddress = destinationAddress;
|
||||
mSpi = spi;
|
||||
mCloseGuard.open("open");
|
||||
}
|
||||
|
||||
/**
|
||||
* Release an SPI that was previously reserved.
|
||||
*
|
||||
* <p>Release an SPI for use by other users in the system. This will fail if the SPI is
|
||||
* currently in use by an IpSecTransform.
|
||||
*
|
||||
* @param destinationAddress SPIs must be unique for each combination of SPI and destination
|
||||
* address. Thus, the destinationAddress to which the SPI will communicate must be
|
||||
* supplied.
|
||||
* @param spi the previously reserved SPI to be freed.
|
||||
*/
|
||||
@Override
|
||||
public void close() {
|
||||
mSpi = INVALID_SECURITY_PARAMETER_INDEX; // TODO: Invalid SPI
|
||||
mCloseGuard.close();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void finalize() {
|
||||
if (mCloseGuard != null) {
|
||||
mCloseGuard.warnIfOpen();
|
||||
}
|
||||
|
||||
close();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The Security Parameter Index, SPI, 0 indicates an unknown or invalid index.
|
||||
*
|
||||
* <p>No IPsec packet may contain an SPI of 0.
|
||||
*/
|
||||
public static final int INVALID_SECURITY_PARAMETER_INDEX = 0;
|
||||
|
||||
/**
|
||||
* Reserve an SPI for traffic bound towards the specified destination address.
|
||||
*
|
||||
* <p>If successful, this SPI is guaranteed available until released by a call to {@link
|
||||
* SecurityParameterIndex#close()}.
|
||||
*
|
||||
* @param destinationAddress SPIs must be unique for each combination of SPI and destination
|
||||
* address.
|
||||
* @param requestedSpi the requested SPI, or '0' to allocate a random SPI.
|
||||
* @return the reserved SecurityParameterIndex
|
||||
* @throws ResourceUnavailableException indicating that too many SPIs are currently allocated
|
||||
* for this user
|
||||
* @throws SpiUnavailableException indicating that a particular SPI cannot be reserved
|
||||
*/
|
||||
public SecurityParameterIndex reserveSecurityParameterIndex(
|
||||
InetAddress destinationAddress, int requestedSpi)
|
||||
throws SpiUnavailableException, ResourceUnavailableException {
|
||||
return new SecurityParameterIndex(mContext, destinationAddress, requestedSpi);
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply an active Transport Mode IPsec Transform to a stream socket to perform IPsec
|
||||
* encapsulation of the traffic flowing between the socket and the remote InetAddress of that
|
||||
* transform. For security reasons, attempts to send traffic to any IP address other than the
|
||||
* address associated with that transform will throw an IOException. In addition, if the
|
||||
* IpSecTransform is later deactivated, the socket will throw an IOException on any calls to
|
||||
* send() or receive() until the transform is removed from the socket by calling {@link
|
||||
* #removeTransportModeTransform(Socket, IpSecTransform)};
|
||||
*
|
||||
* @param socket a stream socket
|
||||
* @param transform an {@link IpSecTransform}, which must be an active Transport Mode transform.
|
||||
*/
|
||||
public void applyTransportModeTransform(Socket socket, IpSecTransform transform)
|
||||
throws IOException {
|
||||
applyTransportModeTransform(ParcelFileDescriptor.fromSocket(socket), transform);
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply an active Transport Mode IPsec Transform to a datagram socket to perform IPsec
|
||||
* encapsulation of the traffic flowing between the socket and the remote InetAddress of that
|
||||
* transform. For security reasons, attempts to send traffic to any IP address other than the
|
||||
* address associated with that transform will throw an IOException. In addition, if the
|
||||
* IpSecTransform is later deactivated, the socket will throw an IOException on any calls to
|
||||
* send() or receive() until the transform is removed from the socket by calling {@link
|
||||
* #removeTransportModeTransform(DatagramSocket, IpSecTransform)};
|
||||
*
|
||||
* @param socket a datagram socket
|
||||
* @param transform an {@link IpSecTransform}, which must be an active Transport Mode transform.
|
||||
*/
|
||||
public void applyTransportModeTransform(DatagramSocket socket, IpSecTransform transform)
|
||||
throws IOException {
|
||||
applyTransportModeTransform(ParcelFileDescriptor.fromDatagramSocket(socket), transform);
|
||||
}
|
||||
|
||||
/* Call down to activate a transform */
|
||||
private void applyTransportModeTransform(ParcelFileDescriptor pfd, IpSecTransform transform) {}
|
||||
|
||||
/**
|
||||
* Apply an active Tunnel Mode IPsec Transform to a network, which will tunnel all traffic to
|
||||
* and from that 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).
|
||||
* Applications should probably not use this API directly. Instead, they should use {@link
|
||||
* VpnService} to provide VPN capability in a more generic fashion.
|
||||
*
|
||||
* @param net a {@link Network} that will be tunneled via IP Sec.
|
||||
* @param transform an {@link IpSecTransform}, which must be an active Tunnel Mode transform.
|
||||
* @hide
|
||||
*/
|
||||
@SystemApi
|
||||
public void applyTunnelModeTransform(Network net, IpSecTransform transform) {}
|
||||
|
||||
/**
|
||||
* Remove a transform from a given stream socket. Once removed, traffic on the socket will not
|
||||
* be encypted. This allows sockets that have been used for IPsec to be reclaimed for
|
||||
* communication in the clear in the event socket reuse is desired. This operation will succeed
|
||||
* regardless of the underlying state of a transform. If a transform is removed, communication
|
||||
* on all sockets to which that transform was applied will fail until this method is called.
|
||||
*
|
||||
* @param socket a socket that previously had a transform applied to it.
|
||||
* @param transform the IPsec Transform that was previously applied to the given socket
|
||||
*/
|
||||
public void removeTransportModeTransform(Socket socket, IpSecTransform transform) {
|
||||
removeTransportModeTransform(ParcelFileDescriptor.fromSocket(socket), transform);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a transform from a given datagram socket. Once removed, traffic on the socket will not
|
||||
* be encypted. This allows sockets that have been used for IPsec to be reclaimed for
|
||||
* communication in the clear in the event socket reuse is desired. This operation will succeed
|
||||
* regardless of the underlying state of a transform. If a transform is removed, communication
|
||||
* on all sockets to which that transform was applied will fail until this method is called.
|
||||
*
|
||||
* @param socket a socket that previously had a transform applied to it.
|
||||
* @param transform the IPsec Transform that was previously applied to the given socket
|
||||
*/
|
||||
public void removeTransportModeTransform(DatagramSocket socket, IpSecTransform transform) {
|
||||
removeTransportModeTransform(ParcelFileDescriptor.fromDatagramSocket(socket), transform);
|
||||
}
|
||||
|
||||
/* Call down to activate a transform */
|
||||
private void removeTransportModeTransform(ParcelFileDescriptor pfd, IpSecTransform transform) {}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
* @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
|
||||
*/
|
||||
@SystemApi
|
||||
public void removeTunnelModeTransform(Network net, IpSecTransform transform) {}
|
||||
|
||||
/**
|
||||
* Class providing access to a system-provided UDP Encapsulation Socket, which may be used for
|
||||
* IKE signalling as well as for inbound and outbound UDP encapsulated IPsec traffic.
|
||||
*
|
||||
* <p>The socket provided by this class cannot be re-bound or closed via the inner
|
||||
* FileDescriptor. Instead, disposing of this socket requires a call to close().
|
||||
*/
|
||||
public static final class UdpEncapsulationSocket implements AutoCloseable {
|
||||
private final FileDescriptor mFd;
|
||||
private final Context mContext;
|
||||
private final CloseGuard mCloseGuard = CloseGuard.get();
|
||||
|
||||
private UdpEncapsulationSocket(Context context, int port)
|
||||
throws ResourceUnavailableException {
|
||||
mContext = context;
|
||||
mCloseGuard.open("constructor");
|
||||
// TODO: go down to the kernel and get a socket on the specified
|
||||
mFd = new FileDescriptor();
|
||||
}
|
||||
|
||||
private UdpEncapsulationSocket(Context context) throws ResourceUnavailableException {
|
||||
mContext = context;
|
||||
mCloseGuard.open("constructor");
|
||||
// TODO: go get a random socket on a random port
|
||||
mFd = new FileDescriptor();
|
||||
}
|
||||
|
||||
/** Access the inner UDP Encapsulation Socket */
|
||||
public FileDescriptor getSocket() {
|
||||
return mFd;
|
||||
}
|
||||
|
||||
/** Retrieve the port number of the inner encapsulation socket */
|
||||
public int getPort() {
|
||||
return 0; // TODO get the port number from the Socket;
|
||||
}
|
||||
|
||||
@Override
|
||||
/**
|
||||
* Release the resources that have been reserved for this Socket.
|
||||
*
|
||||
* <p>This method closes the underlying socket, reducing a user's allocated sockets in the
|
||||
* system. This must be done as part of cleanup following use of a socket. Failure to do so
|
||||
* will cause the socket to count against a total allocation limit for IpSec and eventually
|
||||
* fail due to resource limits.
|
||||
*
|
||||
* @param fd a file descriptor previously returned as a UDP Encapsulation socket.
|
||||
*/
|
||||
public void close() {
|
||||
// TODO: Go close the socket
|
||||
mCloseGuard.close();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void finalize() throws Throwable {
|
||||
if (mCloseGuard != null) {
|
||||
mCloseGuard.warnIfOpen();
|
||||
}
|
||||
|
||||
close();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Open a socket that is bound to a free UDP port on the system.
|
||||
*
|
||||
* <p>By binding in this manner and holding the FileDescriptor, the socket cannot be un-bound by
|
||||
* the caller. This provides safe access to a socket on a port that can later be used as a UDP
|
||||
* Encapsulation port.
|
||||
*
|
||||
* <p>This socket reservation works in conjunction with IpSecTransforms, which may re-use the
|
||||
* socket port. Explicitly opening this port is only necessary if communication is desired on
|
||||
* that port.
|
||||
*
|
||||
* @param port a local UDP port to be reserved for UDP Encapsulation. is provided, then this
|
||||
* method will bind to the specified port or fail. To retrieve the port number, call {@link
|
||||
* android.system.Os#getsockname(FileDescriptor)}.
|
||||
* @return a {@link UdpEncapsulationSocket} that is bound to the requested port for the lifetime
|
||||
* of the object.
|
||||
*/
|
||||
// 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.
|
||||
public UdpEncapsulationSocket openUdpEncapsulationSocket(int port)
|
||||
throws IOException, ResourceUnavailableException {
|
||||
// Temporary code
|
||||
return new UdpEncapsulationSocket(mContext, port);
|
||||
}
|
||||
|
||||
/**
|
||||
* Open a socket that is bound to a port selected by the system.
|
||||
*
|
||||
* <p>By binding in this manner and holding the FileDescriptor, the socket cannot be un-bound by
|
||||
* the caller. This provides safe access to a socket on a port that can later be used as a UDP
|
||||
* Encapsulation port.
|
||||
*
|
||||
* <p>This socket reservation works in conjunction with IpSecTransforms, which may re-use the
|
||||
* socket port. Explicitly opening this port is only necessary if communication is desired on
|
||||
* that port.
|
||||
*
|
||||
* @return a {@link UdpEncapsulationSocket} that is bound to an arbitrarily selected port
|
||||
*/
|
||||
// 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.
|
||||
public UdpEncapsulationSocket openUdpEncapsulationSocket()
|
||||
throws IOException, ResourceUnavailableException {
|
||||
// Temporary code
|
||||
return new UdpEncapsulationSocket(mContext);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve an instance of an IpSecManager within you application context
|
||||
*
|
||||
* @param context the application context for this manager
|
||||
* @hide
|
||||
*/
|
||||
public IpSecManager(Context context, INetworkManagementService service) {
|
||||
mContext = checkNotNull(context, "missing context");
|
||||
mService = checkNotNull(service, "missing service");
|
||||
}
|
||||
}
|
||||
471
core/java/android/net/IpSecTransform.java
Normal file
471
core/java/android/net/IpSecTransform.java
Normal file
@@ -0,0 +1,471 @@
|
||||
/*
|
||||
* 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 android.annotation.IntDef;
|
||||
import android.annotation.SystemApi;
|
||||
import android.content.Context;
|
||||
import android.system.ErrnoException;
|
||||
import android.util.Log;
|
||||
import dalvik.system.CloseGuard;
|
||||
import java.io.IOException;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.net.InetAddress;
|
||||
|
||||
/**
|
||||
* This class represents an IpSecTransform, which encapsulates both properties and state of IPsec.
|
||||
*
|
||||
* <p>IpSecTransforms must be built from an IpSecTransform.Builder, and they must persist throughout
|
||||
* the lifetime of the underlying transform. If a transform object leaves scope, the underlying
|
||||
* transform may be disabled automatically, with likely undesirable results.
|
||||
*
|
||||
* <p>An IpSecTransform may either represent a tunnel mode transform that operates on a wide array
|
||||
* of traffic or may represent a transport mode transform operating on a Socket or Sockets.
|
||||
*/
|
||||
public final class IpSecTransform implements AutoCloseable {
|
||||
private static final String TAG = "IpSecTransform";
|
||||
|
||||
/**
|
||||
* For direction-specific attributes of an IpSecTransform, indicates that an attribute applies
|
||||
* to traffic towards the host.
|
||||
*/
|
||||
public static final int DIRECTION_IN = 0;
|
||||
|
||||
/**
|
||||
* For direction-specific attributes of an IpSecTransform, indicates that an attribute applies
|
||||
* to traffic from the host.
|
||||
*/
|
||||
public static final int DIRECTION_OUT = 1;
|
||||
|
||||
/** @hide */
|
||||
@IntDef(value = {DIRECTION_IN, DIRECTION_OUT})
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
public @interface TransformDirection {}
|
||||
|
||||
/** @hide */
|
||||
private static final int MODE_TUNNEL = 0;
|
||||
|
||||
/** @hide */
|
||||
private static final int MODE_TRANSPORT = 1;
|
||||
|
||||
/** @hide */
|
||||
public static final int ENCAP_NONE = 0;
|
||||
|
||||
/**
|
||||
* IpSec traffic will be encapsulated within UDP as per <a
|
||||
* href="https://tools.ietf.org/html/rfc3948">RFC3498</a>.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public static final int ENCAP_ESPINUDP = 1;
|
||||
|
||||
/**
|
||||
* IpSec traffic will be encapsulated within a UDP header with an additional 8-byte header pad
|
||||
* (of '0'-value bytes) that prevents traffic from being interpreted as IKE or as ESP over UDP.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public static final int ENCAP_ESPINUDP_NONIKE = 2;
|
||||
|
||||
/** @hide */
|
||||
@IntDef(value = {ENCAP_NONE, ENCAP_ESPINUDP, ENCAP_ESPINUDP_NONIKE})
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
public @interface EncapType {}
|
||||
|
||||
/**
|
||||
* Sentinel for an invalid transform (means that this transform is inactive).
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public static final int INVALID_TRANSFORM_ID = -1;
|
||||
|
||||
private IpSecTransform(Context context, IpSecConfig config) {
|
||||
mContext = context;
|
||||
mConfig = config;
|
||||
mTransformId = INVALID_TRANSFORM_ID;
|
||||
}
|
||||
|
||||
private IpSecTransform activate()
|
||||
throws IOException, IpSecManager.ResourceUnavailableException,
|
||||
IpSecManager.SpiUnavailableException {
|
||||
int transformId;
|
||||
synchronized (this) {
|
||||
//try {
|
||||
transformId = INVALID_TRANSFORM_ID;
|
||||
//} catch (RemoteException e) {
|
||||
// throw e.rethrowFromSystemServer();
|
||||
//}
|
||||
|
||||
if (transformId < 0) {
|
||||
throw new ErrnoException("addTransform", -transformId).rethrowAsIOException();
|
||||
}
|
||||
|
||||
startKeepalive(mContext); // Will silently fail if not required
|
||||
mTransformId = transformId;
|
||||
Log.d(TAG, "Added Transform with Id " + transformId);
|
||||
}
|
||||
mCloseGuard.open("build");
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Deactivate an IpSecTransform and free all resources for that transform that are managed by
|
||||
* the system for this Transform.
|
||||
*
|
||||
* <p>Deactivating a transform while it is still applied to any Socket will result in sockets
|
||||
* refusing to send or receive data. This method will silently succeed if the specified
|
||||
* transform has already been removed; thus, it is always safe to attempt cleanup when a
|
||||
* transform is no longer needed.
|
||||
*/
|
||||
public void close() {
|
||||
Log.d(TAG, "Removing Transform with Id " + mTransformId);
|
||||
|
||||
// Always safe to attempt cleanup
|
||||
if (mTransformId == INVALID_TRANSFORM_ID) {
|
||||
return;
|
||||
}
|
||||
//try {
|
||||
stopKeepalive();
|
||||
//} catch (RemoteException e) {
|
||||
// transform.setTransformId(transformId);
|
||||
// throw e.rethrowFromSystemServer();
|
||||
//} finally {
|
||||
mTransformId = INVALID_TRANSFORM_ID;
|
||||
//}
|
||||
mCloseGuard.close();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void finalize() throws Throwable {
|
||||
if (mCloseGuard != null) {
|
||||
mCloseGuard.warnIfOpen();
|
||||
}
|
||||
close();
|
||||
}
|
||||
|
||||
/* Package */
|
||||
IpSecConfig getConfig() {
|
||||
return mConfig;
|
||||
}
|
||||
|
||||
private final IpSecConfig mConfig;
|
||||
private int mTransformId;
|
||||
private final Context mContext;
|
||||
private final CloseGuard mCloseGuard = CloseGuard.get();
|
||||
private ConnectivityManager.PacketKeepalive mKeepalive;
|
||||
private int mKeepaliveStatus = ConnectivityManager.PacketKeepalive.NO_KEEPALIVE;
|
||||
private Object mKeepaliveSyncLock = new Object();
|
||||
private ConnectivityManager.PacketKeepaliveCallback mKeepaliveCallback =
|
||||
new ConnectivityManager.PacketKeepaliveCallback() {
|
||||
|
||||
@Override
|
||||
public void onStarted() {
|
||||
synchronized (mKeepaliveSyncLock) {
|
||||
mKeepaliveStatus = ConnectivityManager.PacketKeepalive.SUCCESS;
|
||||
mKeepaliveSyncLock.notifyAll();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStopped() {
|
||||
synchronized (mKeepaliveSyncLock) {
|
||||
mKeepaliveStatus = ConnectivityManager.PacketKeepalive.NO_KEEPALIVE;
|
||||
mKeepaliveSyncLock.notifyAll();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(int error) {
|
||||
synchronized (mKeepaliveSyncLock) {
|
||||
mKeepaliveStatus = error;
|
||||
mKeepaliveSyncLock.notifyAll();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/* Package */
|
||||
void startKeepalive(Context c) {
|
||||
if (mConfig.getNattKeepaliveInterval() == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
ConnectivityManager cm =
|
||||
(ConnectivityManager) c.getSystemService(Context.CONNECTIVITY_SERVICE);
|
||||
|
||||
if (mKeepalive != null) {
|
||||
Log.e(TAG, "Keepalive already started for this IpSecTransform.");
|
||||
return;
|
||||
}
|
||||
|
||||
synchronized (mKeepaliveSyncLock) {
|
||||
mKeepalive =
|
||||
cm.startNattKeepalive(
|
||||
mConfig.getNetwork(),
|
||||
mConfig.getNattKeepaliveInterval(),
|
||||
mKeepaliveCallback,
|
||||
mConfig.getLocalIp(),
|
||||
mConfig.getEncapLocalPort(),
|
||||
mConfig.getRemoteIp());
|
||||
try {
|
||||
mKeepaliveSyncLock.wait(2000);
|
||||
} catch (InterruptedException e) {
|
||||
}
|
||||
}
|
||||
if (mKeepaliveStatus != ConnectivityManager.PacketKeepalive.SUCCESS) {
|
||||
throw new UnsupportedOperationException("Packet Keepalive cannot be started");
|
||||
}
|
||||
}
|
||||
|
||||
/* Package */
|
||||
void stopKeepalive() {
|
||||
if (mKeepalive == null) {
|
||||
return;
|
||||
}
|
||||
mKeepalive.stop();
|
||||
synchronized (mKeepaliveSyncLock) {
|
||||
if (mKeepaliveStatus == ConnectivityManager.PacketKeepalive.SUCCESS) {
|
||||
try {
|
||||
mKeepaliveSyncLock.wait(2000);
|
||||
} catch (InterruptedException e) {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Package */
|
||||
void setTransformId(int transformId) {
|
||||
mTransformId = transformId;
|
||||
}
|
||||
|
||||
/* Package */
|
||||
int getTransformId() {
|
||||
return mTransformId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builder object to facilitate the creation of IpSecTransform objects.
|
||||
*
|
||||
* <p>Apply additional properties to the transform and then call a build() method to return an
|
||||
* IpSecTransform object.
|
||||
*
|
||||
* @see Builder#buildTransportModeTransform(InetAddress)
|
||||
*/
|
||||
public static class Builder {
|
||||
private Context mContext;
|
||||
private IpSecConfig mConfig;
|
||||
|
||||
/**
|
||||
* Add an encryption algorithm to the transform for the given direction.
|
||||
*
|
||||
* <p>If encryption is set for a given direction without also providing an SPI for that
|
||||
* direction, creation of an IpSecTransform will fail upon calling a build() method.
|
||||
*
|
||||
* @param direction either {@link #DIRECTION_IN or #DIRECTION_OUT}
|
||||
* @param algo {@link IpSecAlgorithm} specifying the encryption to be applied.
|
||||
*/
|
||||
public IpSecTransform.Builder setEncryption(
|
||||
@TransformDirection int direction, IpSecAlgorithm algo) {
|
||||
mConfig.flow[direction].encryptionAlgo = algo;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add an authentication/integrity algorithm to the transform.
|
||||
*
|
||||
* <p>If authentication is set for a given direction without also providing an SPI for that
|
||||
* direction, creation of an IpSecTransform will fail upon calling a build() method.
|
||||
*
|
||||
* @param direction either {@link #DIRECTION_IN or #DIRECTION_OUT}
|
||||
* @param algo {@link IpSecAlgorithm} specifying the authentication to be applied.
|
||||
*/
|
||||
public IpSecTransform.Builder setAuthentication(
|
||||
@TransformDirection int direction, IpSecAlgorithm algo) {
|
||||
mConfig.flow[direction].authenticationAlgo = algo;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the SPI, which uniquely identifies a particular IPsec session from others. Because
|
||||
* IPsec operates at the IP layer, this 32-bit identifier uniquely identifies packets to a
|
||||
* given destination address.
|
||||
*
|
||||
* <p>Care should be chosen when selecting an SPI to ensure that is is as unique as
|
||||
* possible. Random number generation is a reasonable approach to selecting an SPI. For
|
||||
* outbound SPIs, they must be reserved by calling {@link
|
||||
* IpSecManager#reserveSecurityParameterIndex(InetAddress, int)}. Otherwise, Transforms will
|
||||
* fail to build.
|
||||
*
|
||||
* <p>Unless an SPI is set for a given direction, traffic in that direction will be
|
||||
* sent/received without any IPsec applied.
|
||||
*
|
||||
* @param direction either {@link #DIRECTION_IN or #DIRECTION_OUT}
|
||||
* @param spi a unique 32-bit integer to identify transformed traffic
|
||||
*/
|
||||
public IpSecTransform.Builder setSpi(@TransformDirection int direction, int spi) {
|
||||
mConfig.flow[direction].spi = spi;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the SPI, which uniquely identifies a particular IPsec session from others. Because
|
||||
* IPsec operates at the IP layer, this 32-bit identifier uniquely identifies packets to a
|
||||
* given destination address.
|
||||
*
|
||||
* <p>Care should be chosen when selecting an SPI to ensure that is is as unique as
|
||||
* possible. Random number generation is a reasonable approach to selecting an SPI. For
|
||||
* outbound SPIs, they must be reserved by calling {@link
|
||||
* IpSecManager#reserveSecurityParameterIndex(InetAddress, int)}. Otherwise, Transforms will
|
||||
* fail to activate.
|
||||
*
|
||||
* <p>Unless an SPI is set for a given direction, traffic in that direction will be
|
||||
* sent/received without any IPsec applied.
|
||||
*
|
||||
* @param direction either {@link #DIRECTION_IN or #DIRECTION_OUT}
|
||||
* @param spi a unique {@link IpSecManager.SecurityParameterIndex} to identify transformed
|
||||
* traffic
|
||||
*/
|
||||
public IpSecTransform.Builder setSpi(
|
||||
@TransformDirection int direction, IpSecManager.SecurityParameterIndex spi) {
|
||||
mConfig.flow[direction].spi = spi.getSpi();
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify the network on which this transform will emit its traffic; (otherwise it will
|
||||
* emit on the default network).
|
||||
*
|
||||
* <p>Restricts the transformed traffic to a particular {@link Network}. This is required in
|
||||
* tunnel mode.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
@SystemApi
|
||||
public IpSecTransform.Builder setUnderlyingNetwork(Network net) {
|
||||
mConfig.network = net;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add UDP encapsulation to an IPv4 transform
|
||||
*
|
||||
* <p>This option allows IPsec traffic to pass through NAT. Refer to RFC 3947 and 3948 for
|
||||
* details on how UDP should be applied to IPsec.
|
||||
*
|
||||
* @param localSocket a {@link IpSecManager.UdpEncapsulationSocket} for sending and
|
||||
* receiving encapsulating traffic.
|
||||
* @param remotePort the UDP port number of the remote that will send and receive
|
||||
* encapsulated traffic. In the case of IKE, this is likely port 4500.
|
||||
*/
|
||||
public IpSecTransform.Builder setIpv4Encapsulation(
|
||||
IpSecManager.UdpEncapsulationSocket localSocket, int remotePort) {
|
||||
// TODO: check encap type is valid.
|
||||
mConfig.encapType = ENCAP_ESPINUDP;
|
||||
mConfig.encapLocalPort = localSocket.getPort(); // TODO: plug in the encap socket
|
||||
mConfig.encapRemotePort = remotePort;
|
||||
return this;
|
||||
}
|
||||
|
||||
// TODO: Decrease the minimum keepalive to maybe 10?
|
||||
// TODO: Probably a better exception to throw for NATTKeepalive failure
|
||||
// TODO: Specify the needed NATT keepalive permission.
|
||||
/**
|
||||
* Send a NATT Keepalive packet with a given maximum interval. This will create an offloaded
|
||||
* request to do power-efficient NATT Keepalive. If NATT keepalive is requested but cannot
|
||||
* be activated, then the transform will fail to activate and throw an IOException.
|
||||
*
|
||||
* @param intervalSeconds the maximum number of seconds between keepalive packets, no less
|
||||
* than 20s and no more than 3600s.
|
||||
* @hide
|
||||
*/
|
||||
@SystemApi
|
||||
public IpSecTransform.Builder setNattKeepalive(int intervalSeconds) {
|
||||
mConfig.nattKeepaliveInterval = intervalSeconds;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build and return an active {@link IpSecTransform} object as a Transport Mode Transform.
|
||||
* Some parameters have interdependencies that are checked at build time. If a well-formed
|
||||
* transform cannot be created from the supplied parameters, this method will throw an
|
||||
* Exception.
|
||||
*
|
||||
* <p>Upon a successful return from this call, the provided IpSecTransform will be active
|
||||
* and may be applied to sockets. If too many IpSecTransform objects are active for a given
|
||||
* user this operation will fail and throw ResourceUnavailableException. To avoid these
|
||||
* exceptions, unused Transform objects must be cleaned up by calling {@link
|
||||
* IpSecTransform#close()} when they are no longer needed.
|
||||
*
|
||||
* @param remoteAddress the {@link InetAddress} that, when matched on traffic to/from this
|
||||
* socket will cause the transform to be applied.
|
||||
* <p>Note that an active transform will not impact any network traffic until it has
|
||||
* been applied to one or more Sockets. Calling this method is a necessary precondition
|
||||
* for applying it to a socket, but is not sufficient to actually apply IPsec.
|
||||
* @throws IllegalArgumentException indicating that a particular combination of transform
|
||||
* properties is invalid.
|
||||
* @throws IpSecManager.ResourceUnavailableException in the event that no more Transforms
|
||||
* may be allocated
|
||||
* @throws SpiUnavailableException if the SPI collides with an existing transform
|
||||
* (unlikely).
|
||||
* @throws ResourceUnavailableException if the current user currently has exceeded the
|
||||
* number of allowed active transforms.
|
||||
*/
|
||||
public IpSecTransform buildTransportModeTransform(InetAddress remoteAddress)
|
||||
throws IpSecManager.ResourceUnavailableException,
|
||||
IpSecManager.SpiUnavailableException, IOException {
|
||||
//FIXME: argument validation here
|
||||
//throw new IllegalArgumentException("Natt Keepalive requires UDP Encapsulation");
|
||||
mConfig.mode = MODE_TRANSPORT;
|
||||
mConfig.remoteAddress = remoteAddress;
|
||||
return new IpSecTransform(mContext, mConfig).activate();
|
||||
}
|
||||
|
||||
/**
|
||||
* Build and return an {@link IpSecTransform} object as a Tunnel Mode Transform. Some
|
||||
* parameters have interdependencies that are checked at build time.
|
||||
*
|
||||
* @param localAddress the {@link InetAddress} that provides the local endpoint for this
|
||||
* IPsec tunnel. This is almost certainly an address belonging to the {@link Network}
|
||||
* that will originate the traffic, which is set as the {@link #setUnderlyingNetwork}.
|
||||
* @param remoteAddress the {@link InetAddress} representing the remote endpoint of this
|
||||
* IPsec tunnel.
|
||||
* @throws IllegalArgumentException indicating that a particular combination of transform
|
||||
* properties is invalid.
|
||||
* @hide
|
||||
*/
|
||||
@SystemApi
|
||||
public IpSecTransform buildTunnelModeTransform(
|
||||
InetAddress localAddress, InetAddress remoteAddress) {
|
||||
//FIXME: argument validation here
|
||||
//throw new IllegalArgumentException("Natt Keepalive requires UDP Encapsulation");
|
||||
mConfig.localAddress = localAddress;
|
||||
mConfig.remoteAddress = remoteAddress;
|
||||
mConfig.mode = MODE_TUNNEL;
|
||||
return new IpSecTransform(mContext, mConfig);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new IpSecTransform.Builder to construct an IpSecTransform
|
||||
*
|
||||
* @param context current Context
|
||||
*/
|
||||
public Builder(Context context) {
|
||||
mContext = context;
|
||||
mConfig = new IpSecConfig();
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user