Make Transforms Unidirectional am: 5a19b9500d
am: f4fc2714c2 Change-Id: Ie3ca37e14d977fd255a7791ae3332ddda29ab37e
This commit is contained in:
@@ -31,7 +31,7 @@ import android.os.ParcelFileDescriptor;
|
|||||||
interface IIpSecService
|
interface IIpSecService
|
||||||
{
|
{
|
||||||
IpSecSpiResponse allocateSecurityParameterIndex(
|
IpSecSpiResponse allocateSecurityParameterIndex(
|
||||||
int direction, in String remoteAddress, int requestedSpi, in IBinder binder);
|
in String destinationAddress, int requestedSpi, in IBinder binder);
|
||||||
|
|
||||||
void releaseSecurityParameterIndex(int resourceId);
|
void releaseSecurityParameterIndex(int resourceId);
|
||||||
|
|
||||||
@@ -43,7 +43,7 @@ interface IIpSecService
|
|||||||
|
|
||||||
void deleteTransportModeTransform(int transformId);
|
void deleteTransportModeTransform(int transformId);
|
||||||
|
|
||||||
void applyTransportModeTransform(in ParcelFileDescriptor socket, int transformId);
|
void applyTransportModeTransform(in ParcelFileDescriptor socket, int direction, int transformId);
|
||||||
|
|
||||||
void removeTransportModeTransform(in ParcelFileDescriptor socket, int transformId);
|
void removeTransportModeTransforms(in ParcelFileDescriptor socket, int transformId);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -256,13 +256,19 @@ public final class IpSecAlgorithm implements Parcelable {
|
|||||||
return getName().equals(AUTH_CRYPT_AES_GCM);
|
return getName().equals(AUTH_CRYPT_AES_GCM);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Because encryption keys are sensitive and userdebug builds are used by large user pools
|
||||||
|
// such as beta testers, we only allow sensitive info such as keys on eng builds.
|
||||||
|
private static boolean isUnsafeBuild() {
|
||||||
|
return Build.IS_DEBUGGABLE && Build.IS_ENG;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return new StringBuilder()
|
return new StringBuilder()
|
||||||
.append("{mName=")
|
.append("{mName=")
|
||||||
.append(mName)
|
.append(mName)
|
||||||
.append(", mKey=")
|
.append(", mKey=")
|
||||||
.append(Build.IS_DEBUGGABLE ? HexDump.toHexString(mKey) : "<hidden>")
|
.append(isUnsafeBuild() ? HexDump.toHexString(mKey) : "<hidden>")
|
||||||
.append(", mTruncLenBits=")
|
.append(", mTruncLenBits=")
|
||||||
.append(mTruncLenBits)
|
.append(mTruncLenBits)
|
||||||
.append("}")
|
.append("}")
|
||||||
|
|||||||
@@ -32,59 +32,29 @@ public final class IpSecConfig implements Parcelable {
|
|||||||
// MODE_TRANSPORT or MODE_TUNNEL
|
// MODE_TRANSPORT or MODE_TUNNEL
|
||||||
private int mMode = IpSecTransform.MODE_TRANSPORT;
|
private int mMode = IpSecTransform.MODE_TRANSPORT;
|
||||||
|
|
||||||
// Needs to be valid only for tunnel mode
|
|
||||||
// Preventing this from being null simplifies Java->Native binder
|
// Preventing this from being null simplifies Java->Native binder
|
||||||
private String mLocalAddress = "";
|
private String mSourceAddress = "";
|
||||||
|
|
||||||
// Preventing this from being null simplifies Java->Native binder
|
// Preventing this from being null simplifies Java->Native binder
|
||||||
private String mRemoteAddress = "";
|
private String mDestinationAddress = "";
|
||||||
|
|
||||||
// The underlying Network that represents the "gateway" Network
|
// The underlying Network that represents the "gateway" Network
|
||||||
// for outbound packets. It may also be used to select packets.
|
// for outbound packets. It may also be used to select packets.
|
||||||
private Network mNetwork;
|
private Network mNetwork;
|
||||||
|
|
||||||
/**
|
// Minimum requirements for identifying a transform
|
||||||
* This class captures the parameters that specifically apply to inbound or outbound traffic.
|
// SPI identifying the IPsec SA in packet processing
|
||||||
*/
|
// and a destination IP address
|
||||||
public static class Flow {
|
private int mSpiResourceId = IpSecManager.INVALID_RESOURCE_ID;
|
||||||
// Minimum requirements for identifying a transform
|
|
||||||
// SPI identifying the IPsec flow in packet processing
|
|
||||||
// and a remote IP address
|
|
||||||
private int mSpiResourceId = IpSecManager.INVALID_RESOURCE_ID;
|
|
||||||
|
|
||||||
// Encryption Algorithm
|
// Encryption Algorithm
|
||||||
private IpSecAlgorithm mEncryption;
|
private IpSecAlgorithm mEncryption;
|
||||||
|
|
||||||
// Authentication Algorithm
|
// Authentication Algorithm
|
||||||
private IpSecAlgorithm mAuthentication;
|
private IpSecAlgorithm mAuthentication;
|
||||||
|
|
||||||
// Authenticated Encryption Algorithm
|
// Authenticated Encryption Algorithm
|
||||||
private IpSecAlgorithm mAuthenticatedEncryption;
|
private IpSecAlgorithm mAuthenticatedEncryption;
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
return new StringBuilder()
|
|
||||||
.append("{mSpiResourceId=")
|
|
||||||
.append(mSpiResourceId)
|
|
||||||
.append(", mEncryption=")
|
|
||||||
.append(mEncryption)
|
|
||||||
.append(", mAuthentication=")
|
|
||||||
.append(mAuthentication)
|
|
||||||
.append(", mAuthenticatedEncryption=")
|
|
||||||
.append(mAuthenticatedEncryption)
|
|
||||||
.append("}")
|
|
||||||
.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
static boolean equals(IpSecConfig.Flow lhs, IpSecConfig.Flow rhs) {
|
|
||||||
if (lhs == null || rhs == null) return (lhs == rhs);
|
|
||||||
return (lhs.mSpiResourceId == rhs.mSpiResourceId
|
|
||||||
&& IpSecAlgorithm.equals(lhs.mEncryption, rhs.mEncryption)
|
|
||||||
&& IpSecAlgorithm.equals(lhs.mAuthentication, rhs.mAuthentication));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private final Flow[] mFlow = new Flow[] {new Flow(), new Flow()};
|
|
||||||
|
|
||||||
// For tunnel mode IPv4 UDP Encapsulation
|
// For tunnel mode IPv4 UDP Encapsulation
|
||||||
// IpSecTransform#ENCAP_ESP_*, such as ENCAP_ESP_OVER_UDP_IKE
|
// IpSecTransform#ENCAP_ESP_*, such as ENCAP_ESP_OVER_UDP_IKE
|
||||||
@@ -100,36 +70,37 @@ public final class IpSecConfig implements Parcelable {
|
|||||||
mMode = mode;
|
mMode = mode;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Set the local IP address for Tunnel mode */
|
/** Set the source IP addres for this IPsec transform */
|
||||||
public void setLocalAddress(String localAddress) {
|
public void setSourceAddress(String sourceAddress) {
|
||||||
mLocalAddress = localAddress;
|
mSourceAddress = sourceAddress;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Set the remote IP address for this IPsec transform */
|
/** Set the destination IP address for this IPsec transform */
|
||||||
public void setRemoteAddress(String remoteAddress) {
|
public void setDestinationAddress(String destinationAddress) {
|
||||||
mRemoteAddress = remoteAddress;
|
mDestinationAddress = destinationAddress;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Set the SPI for a given direction by resource ID */
|
/** Set the SPI by resource ID */
|
||||||
public void setSpiResourceId(int direction, int resourceId) {
|
public void setSpiResourceId(int resourceId) {
|
||||||
mFlow[direction].mSpiResourceId = resourceId;
|
mSpiResourceId = resourceId;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Set the encryption algorithm for a given direction */
|
/** Set the encryption algorithm */
|
||||||
public void setEncryption(int direction, IpSecAlgorithm encryption) {
|
public void setEncryption(IpSecAlgorithm encryption) {
|
||||||
mFlow[direction].mEncryption = encryption;
|
mEncryption = encryption;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Set the authentication algorithm for a given direction */
|
/** Set the authentication algorithm */
|
||||||
public void setAuthentication(int direction, IpSecAlgorithm authentication) {
|
public void setAuthentication(IpSecAlgorithm authentication) {
|
||||||
mFlow[direction].mAuthentication = authentication;
|
mAuthentication = authentication;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Set the authenticated encryption algorithm for a given direction */
|
/** Set the authenticated encryption algorithm */
|
||||||
public void setAuthenticatedEncryption(int direction, IpSecAlgorithm authenticatedEncryption) {
|
public void setAuthenticatedEncryption(IpSecAlgorithm authenticatedEncryption) {
|
||||||
mFlow[direction].mAuthenticatedEncryption = authenticatedEncryption;
|
mAuthenticatedEncryption = authenticatedEncryption;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Set the underlying network that will carry traffic for this transform */
|
||||||
public void setNetwork(Network network) {
|
public void setNetwork(Network network) {
|
||||||
mNetwork = network;
|
mNetwork = network;
|
||||||
}
|
}
|
||||||
@@ -155,28 +126,28 @@ public final class IpSecConfig implements Parcelable {
|
|||||||
return mMode;
|
return mMode;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getLocalAddress() {
|
public String getSourceAddress() {
|
||||||
return mLocalAddress;
|
return mSourceAddress;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getSpiResourceId(int direction) {
|
public int getSpiResourceId() {
|
||||||
return mFlow[direction].mSpiResourceId;
|
return mSpiResourceId;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getRemoteAddress() {
|
public String getDestinationAddress() {
|
||||||
return mRemoteAddress;
|
return mDestinationAddress;
|
||||||
}
|
}
|
||||||
|
|
||||||
public IpSecAlgorithm getEncryption(int direction) {
|
public IpSecAlgorithm getEncryption() {
|
||||||
return mFlow[direction].mEncryption;
|
return mEncryption;
|
||||||
}
|
}
|
||||||
|
|
||||||
public IpSecAlgorithm getAuthentication(int direction) {
|
public IpSecAlgorithm getAuthentication() {
|
||||||
return mFlow[direction].mAuthentication;
|
return mAuthentication;
|
||||||
}
|
}
|
||||||
|
|
||||||
public IpSecAlgorithm getAuthenticatedEncryption(int direction) {
|
public IpSecAlgorithm getAuthenticatedEncryption() {
|
||||||
return mFlow[direction].mAuthenticatedEncryption;
|
return mAuthenticatedEncryption;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Network getNetwork() {
|
public Network getNetwork() {
|
||||||
@@ -209,17 +180,13 @@ public final class IpSecConfig implements Parcelable {
|
|||||||
@Override
|
@Override
|
||||||
public void writeToParcel(Parcel out, int flags) {
|
public void writeToParcel(Parcel out, int flags) {
|
||||||
out.writeInt(mMode);
|
out.writeInt(mMode);
|
||||||
out.writeString(mLocalAddress);
|
out.writeString(mSourceAddress);
|
||||||
out.writeString(mRemoteAddress);
|
out.writeString(mDestinationAddress);
|
||||||
out.writeParcelable(mNetwork, flags);
|
out.writeParcelable(mNetwork, flags);
|
||||||
out.writeInt(mFlow[IpSecTransform.DIRECTION_IN].mSpiResourceId);
|
out.writeInt(mSpiResourceId);
|
||||||
out.writeParcelable(mFlow[IpSecTransform.DIRECTION_IN].mEncryption, flags);
|
out.writeParcelable(mEncryption, flags);
|
||||||
out.writeParcelable(mFlow[IpSecTransform.DIRECTION_IN].mAuthentication, flags);
|
out.writeParcelable(mAuthentication, flags);
|
||||||
out.writeParcelable(mFlow[IpSecTransform.DIRECTION_IN].mAuthenticatedEncryption, flags);
|
out.writeParcelable(mAuthenticatedEncryption, flags);
|
||||||
out.writeInt(mFlow[IpSecTransform.DIRECTION_OUT].mSpiResourceId);
|
|
||||||
out.writeParcelable(mFlow[IpSecTransform.DIRECTION_OUT].mEncryption, flags);
|
|
||||||
out.writeParcelable(mFlow[IpSecTransform.DIRECTION_OUT].mAuthentication, flags);
|
|
||||||
out.writeParcelable(mFlow[IpSecTransform.DIRECTION_OUT].mAuthenticatedEncryption, flags);
|
|
||||||
out.writeInt(mEncapType);
|
out.writeInt(mEncapType);
|
||||||
out.writeInt(mEncapSocketResourceId);
|
out.writeInt(mEncapSocketResourceId);
|
||||||
out.writeInt(mEncapRemotePort);
|
out.writeInt(mEncapRemotePort);
|
||||||
@@ -231,22 +198,15 @@ public final class IpSecConfig implements Parcelable {
|
|||||||
|
|
||||||
private IpSecConfig(Parcel in) {
|
private IpSecConfig(Parcel in) {
|
||||||
mMode = in.readInt();
|
mMode = in.readInt();
|
||||||
mLocalAddress = in.readString();
|
mSourceAddress = in.readString();
|
||||||
mRemoteAddress = in.readString();
|
mDestinationAddress = in.readString();
|
||||||
mNetwork = (Network) in.readParcelable(Network.class.getClassLoader());
|
mNetwork = (Network) in.readParcelable(Network.class.getClassLoader());
|
||||||
mFlow[IpSecTransform.DIRECTION_IN].mSpiResourceId = in.readInt();
|
mSpiResourceId = in.readInt();
|
||||||
mFlow[IpSecTransform.DIRECTION_IN].mEncryption =
|
mEncryption =
|
||||||
(IpSecAlgorithm) in.readParcelable(IpSecAlgorithm.class.getClassLoader());
|
(IpSecAlgorithm) in.readParcelable(IpSecAlgorithm.class.getClassLoader());
|
||||||
mFlow[IpSecTransform.DIRECTION_IN].mAuthentication =
|
mAuthentication =
|
||||||
(IpSecAlgorithm) in.readParcelable(IpSecAlgorithm.class.getClassLoader());
|
(IpSecAlgorithm) in.readParcelable(IpSecAlgorithm.class.getClassLoader());
|
||||||
mFlow[IpSecTransform.DIRECTION_IN].mAuthenticatedEncryption =
|
mAuthenticatedEncryption =
|
||||||
(IpSecAlgorithm) in.readParcelable(IpSecAlgorithm.class.getClassLoader());
|
|
||||||
mFlow[IpSecTransform.DIRECTION_OUT].mSpiResourceId = in.readInt();
|
|
||||||
mFlow[IpSecTransform.DIRECTION_OUT].mEncryption =
|
|
||||||
(IpSecAlgorithm) in.readParcelable(IpSecAlgorithm.class.getClassLoader());
|
|
||||||
mFlow[IpSecTransform.DIRECTION_OUT].mAuthentication =
|
|
||||||
(IpSecAlgorithm) in.readParcelable(IpSecAlgorithm.class.getClassLoader());
|
|
||||||
mFlow[IpSecTransform.DIRECTION_OUT].mAuthenticatedEncryption =
|
|
||||||
(IpSecAlgorithm) in.readParcelable(IpSecAlgorithm.class.getClassLoader());
|
(IpSecAlgorithm) in.readParcelable(IpSecAlgorithm.class.getClassLoader());
|
||||||
mEncapType = in.readInt();
|
mEncapType = in.readInt();
|
||||||
mEncapSocketResourceId = in.readInt();
|
mEncapSocketResourceId = in.readInt();
|
||||||
@@ -260,10 +220,10 @@ public final class IpSecConfig implements Parcelable {
|
|||||||
strBuilder
|
strBuilder
|
||||||
.append("{mMode=")
|
.append("{mMode=")
|
||||||
.append(mMode == IpSecTransform.MODE_TUNNEL ? "TUNNEL" : "TRANSPORT")
|
.append(mMode == IpSecTransform.MODE_TUNNEL ? "TUNNEL" : "TRANSPORT")
|
||||||
.append(", mLocalAddress=")
|
.append(", mSourceAddress=")
|
||||||
.append(mLocalAddress)
|
.append(mSourceAddress)
|
||||||
.append(", mRemoteAddress=")
|
.append(", mDestinationAddress=")
|
||||||
.append(mRemoteAddress)
|
.append(mDestinationAddress)
|
||||||
.append(", mNetwork=")
|
.append(", mNetwork=")
|
||||||
.append(mNetwork)
|
.append(mNetwork)
|
||||||
.append(", mEncapType=")
|
.append(", mEncapType=")
|
||||||
@@ -274,10 +234,14 @@ public final class IpSecConfig implements Parcelable {
|
|||||||
.append(mEncapRemotePort)
|
.append(mEncapRemotePort)
|
||||||
.append(", mNattKeepaliveInterval=")
|
.append(", mNattKeepaliveInterval=")
|
||||||
.append(mNattKeepaliveInterval)
|
.append(mNattKeepaliveInterval)
|
||||||
.append(", mFlow[OUT]=")
|
.append("{mSpiResourceId=")
|
||||||
.append(mFlow[IpSecTransform.DIRECTION_OUT])
|
.append(mSpiResourceId)
|
||||||
.append(", mFlow[IN]=")
|
.append(", mEncryption=")
|
||||||
.append(mFlow[IpSecTransform.DIRECTION_IN])
|
.append(mEncryption)
|
||||||
|
.append(", mAuthentication=")
|
||||||
|
.append(mAuthentication)
|
||||||
|
.append(", mAuthenticatedEncryption=")
|
||||||
|
.append(mAuthenticatedEncryption)
|
||||||
.append("}");
|
.append("}");
|
||||||
|
|
||||||
return strBuilder.toString();
|
return strBuilder.toString();
|
||||||
@@ -299,17 +263,18 @@ public final class IpSecConfig implements Parcelable {
|
|||||||
public static boolean equals(IpSecConfig lhs, IpSecConfig rhs) {
|
public static boolean equals(IpSecConfig lhs, IpSecConfig rhs) {
|
||||||
if (lhs == null || rhs == null) return (lhs == rhs);
|
if (lhs == null || rhs == null) return (lhs == rhs);
|
||||||
return (lhs.mMode == rhs.mMode
|
return (lhs.mMode == rhs.mMode
|
||||||
&& lhs.mLocalAddress.equals(rhs.mLocalAddress)
|
&& lhs.mSourceAddress.equals(rhs.mSourceAddress)
|
||||||
&& lhs.mRemoteAddress.equals(rhs.mRemoteAddress)
|
&& lhs.mDestinationAddress.equals(rhs.mDestinationAddress)
|
||||||
&& ((lhs.mNetwork != null && lhs.mNetwork.equals(rhs.mNetwork))
|
&& ((lhs.mNetwork != null && lhs.mNetwork.equals(rhs.mNetwork))
|
||||||
|| (lhs.mNetwork == rhs.mNetwork))
|
|| (lhs.mNetwork == rhs.mNetwork))
|
||||||
&& lhs.mEncapType == rhs.mEncapType
|
&& lhs.mEncapType == rhs.mEncapType
|
||||||
&& lhs.mEncapSocketResourceId == rhs.mEncapSocketResourceId
|
&& lhs.mEncapSocketResourceId == rhs.mEncapSocketResourceId
|
||||||
&& lhs.mEncapRemotePort == rhs.mEncapRemotePort
|
&& lhs.mEncapRemotePort == rhs.mEncapRemotePort
|
||||||
&& lhs.mNattKeepaliveInterval == rhs.mNattKeepaliveInterval
|
&& lhs.mNattKeepaliveInterval == rhs.mNattKeepaliveInterval
|
||||||
&& IpSecConfig.Flow.equals(lhs.mFlow[IpSecTransform.DIRECTION_OUT],
|
&& lhs.mSpiResourceId == rhs.mSpiResourceId
|
||||||
rhs.mFlow[IpSecTransform.DIRECTION_OUT])
|
&& IpSecAlgorithm.equals(lhs.mEncryption, rhs.mEncryption)
|
||||||
&& IpSecConfig.Flow.equals(lhs.mFlow[IpSecTransform.DIRECTION_IN],
|
&& IpSecAlgorithm.equals(
|
||||||
rhs.mFlow[IpSecTransform.DIRECTION_IN]));
|
lhs.mAuthenticatedEncryption, rhs.mAuthenticatedEncryption)
|
||||||
|
&& IpSecAlgorithm.equals(lhs.mAuthentication, rhs.mAuthentication));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ package android.net;
|
|||||||
|
|
||||||
import static com.android.internal.util.Preconditions.checkNotNull;
|
import static com.android.internal.util.Preconditions.checkNotNull;
|
||||||
|
|
||||||
|
import android.annotation.IntDef;
|
||||||
import android.annotation.NonNull;
|
import android.annotation.NonNull;
|
||||||
import android.annotation.SystemService;
|
import android.annotation.SystemService;
|
||||||
import android.annotation.TestApi;
|
import android.annotation.TestApi;
|
||||||
@@ -33,6 +34,8 @@ import dalvik.system.CloseGuard;
|
|||||||
|
|
||||||
import java.io.FileDescriptor;
|
import java.io.FileDescriptor;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.lang.annotation.Retention;
|
||||||
|
import java.lang.annotation.RetentionPolicy;
|
||||||
import java.net.DatagramSocket;
|
import java.net.DatagramSocket;
|
||||||
import java.net.InetAddress;
|
import java.net.InetAddress;
|
||||||
import java.net.Socket;
|
import java.net.Socket;
|
||||||
@@ -52,6 +55,23 @@ import java.net.Socket;
|
|||||||
public final class IpSecManager {
|
public final class IpSecManager {
|
||||||
private static final String TAG = "IpSecManager";
|
private static final String TAG = "IpSecManager";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* For direction-specific attributes of an {@link IpSecTransform}, indicates that an attribute
|
||||||
|
* applies to traffic towards the host.
|
||||||
|
*/
|
||||||
|
public static final int DIRECTION_IN = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* For direction-specific attributes of an {@link 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 PolicyDirection {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The Security Parameter Index (SPI) 0 indicates an unknown or invalid index.
|
* The Security Parameter Index (SPI) 0 indicates an unknown or invalid index.
|
||||||
*
|
*
|
||||||
@@ -125,7 +145,7 @@ public final class IpSecManager {
|
|||||||
*/
|
*/
|
||||||
public static final class SecurityParameterIndex implements AutoCloseable {
|
public static final class SecurityParameterIndex implements AutoCloseable {
|
||||||
private final IIpSecService mService;
|
private final IIpSecService mService;
|
||||||
private final InetAddress mRemoteAddress;
|
private final InetAddress mDestinationAddress;
|
||||||
private final CloseGuard mCloseGuard = CloseGuard.get();
|
private final CloseGuard mCloseGuard = CloseGuard.get();
|
||||||
private int mSpi = INVALID_SECURITY_PARAMETER_INDEX;
|
private int mSpi = INVALID_SECURITY_PARAMETER_INDEX;
|
||||||
private int mResourceId = INVALID_RESOURCE_ID;
|
private int mResourceId = INVALID_RESOURCE_ID;
|
||||||
@@ -164,14 +184,14 @@ public final class IpSecManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private SecurityParameterIndex(
|
private SecurityParameterIndex(
|
||||||
@NonNull IIpSecService service, int direction, InetAddress remoteAddress, int spi)
|
@NonNull IIpSecService service, InetAddress destinationAddress, int spi)
|
||||||
throws ResourceUnavailableException, SpiUnavailableException {
|
throws ResourceUnavailableException, SpiUnavailableException {
|
||||||
mService = service;
|
mService = service;
|
||||||
mRemoteAddress = remoteAddress;
|
mDestinationAddress = destinationAddress;
|
||||||
try {
|
try {
|
||||||
IpSecSpiResponse result =
|
IpSecSpiResponse result =
|
||||||
mService.allocateSecurityParameterIndex(
|
mService.allocateSecurityParameterIndex(
|
||||||
direction, remoteAddress.getHostAddress(), spi, new Binder());
|
destinationAddress.getHostAddress(), spi, new Binder());
|
||||||
|
|
||||||
if (result == null) {
|
if (result == null) {
|
||||||
throw new NullPointerException("Received null response from IpSecService");
|
throw new NullPointerException("Received null response from IpSecService");
|
||||||
@@ -216,25 +236,23 @@ public final class IpSecManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Reserve a random SPI for traffic bound to or from the specified remote address.
|
* 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
|
* <p>If successful, this SPI is guaranteed available until released by a call to {@link
|
||||||
* SecurityParameterIndex#close()}.
|
* SecurityParameterIndex#close()}.
|
||||||
*
|
*
|
||||||
* @param direction {@link IpSecTransform#DIRECTION_IN} or {@link IpSecTransform#DIRECTION_OUT}
|
* @param destinationAddress the destination address for traffic bearing the requested SPI.
|
||||||
* @param remoteAddress address of the remote. SPIs must be unique for each remoteAddress
|
* For inbound traffic, the destination should be an address currently assigned on-device.
|
||||||
* @return the reserved SecurityParameterIndex
|
* @return the reserved SecurityParameterIndex
|
||||||
* @throws ResourceUnavailableException indicating that too many SPIs are currently allocated
|
* @throws {@link #ResourceUnavailableException} indicating that too many SPIs are
|
||||||
* for this user
|
* currently allocated for this user
|
||||||
* @throws SpiUnavailableException indicating that a particular SPI cannot be reserved
|
|
||||||
*/
|
*/
|
||||||
public SecurityParameterIndex allocateSecurityParameterIndex(
|
public SecurityParameterIndex allocateSecurityParameterIndex(InetAddress destinationAddress)
|
||||||
int direction, InetAddress remoteAddress) throws ResourceUnavailableException {
|
throws ResourceUnavailableException {
|
||||||
try {
|
try {
|
||||||
return new SecurityParameterIndex(
|
return new SecurityParameterIndex(
|
||||||
mService,
|
mService,
|
||||||
direction,
|
destinationAddress,
|
||||||
remoteAddress,
|
|
||||||
IpSecManager.INVALID_SECURITY_PARAMETER_INDEX);
|
IpSecManager.INVALID_SECURITY_PARAMETER_INDEX);
|
||||||
} catch (SpiUnavailableException unlikely) {
|
} catch (SpiUnavailableException unlikely) {
|
||||||
throw new ResourceUnavailableException("No SPIs available");
|
throw new ResourceUnavailableException("No SPIs available");
|
||||||
@@ -242,26 +260,27 @@ public final class IpSecManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Reserve the requested SPI for traffic bound to or from the specified remote address.
|
* 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
|
* <p>If successful, this SPI is guaranteed available until released by a call to {@link
|
||||||
* SecurityParameterIndex#close()}.
|
* SecurityParameterIndex#close()}.
|
||||||
*
|
*
|
||||||
* @param direction {@link IpSecTransform#DIRECTION_IN} or {@link IpSecTransform#DIRECTION_OUT}
|
* @param destinationAddress the destination address for traffic bearing the requested SPI.
|
||||||
* @param remoteAddress address of the remote. SPIs must be unique for each remoteAddress
|
* 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
|
* @param requestedSpi the requested SPI, or '0' to allocate a random SPI
|
||||||
* @return the reserved SecurityParameterIndex
|
* @return the reserved SecurityParameterIndex
|
||||||
* @throws ResourceUnavailableException indicating that too many SPIs are currently allocated
|
* @throws {@link #ResourceUnavailableException} indicating that too many SPIs are
|
||||||
* for this user
|
* currently allocated for this user
|
||||||
* @throws SpiUnavailableException indicating that the requested SPI could not be reserved
|
* @throws {@link #SpiUnavailableException} indicating that the requested SPI could not be
|
||||||
|
* reserved
|
||||||
*/
|
*/
|
||||||
public SecurityParameterIndex allocateSecurityParameterIndex(
|
public SecurityParameterIndex allocateSecurityParameterIndex(
|
||||||
int direction, InetAddress remoteAddress, int requestedSpi)
|
InetAddress destinationAddress, int requestedSpi)
|
||||||
throws SpiUnavailableException, ResourceUnavailableException {
|
throws SpiUnavailableException, ResourceUnavailableException {
|
||||||
if (requestedSpi == IpSecManager.INVALID_SECURITY_PARAMETER_INDEX) {
|
if (requestedSpi == IpSecManager.INVALID_SECURITY_PARAMETER_INDEX) {
|
||||||
throw new IllegalArgumentException("Requested SPI must be a valid (non-zero) SPI");
|
throw new IllegalArgumentException("Requested SPI must be a valid (non-zero) SPI");
|
||||||
}
|
}
|
||||||
return new SecurityParameterIndex(mService, direction, remoteAddress, requestedSpi);
|
return new SecurityParameterIndex(mService, destinationAddress, requestedSpi);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -269,14 +288,14 @@ public final class IpSecManager {
|
|||||||
*
|
*
|
||||||
* <p>This applies transport mode encapsulation to the given socket. Once applied, I/O on the
|
* <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
|
* socket will be encapsulated according to the parameters of the {@code IpSecTransform}. When
|
||||||
* the transform is removed from the socket by calling {@link #removeTransportModeTransform},
|
* the transform is removed from the socket by calling {@link #removeTransportModeTransforms},
|
||||||
* unprotected traffic can resume on that socket.
|
* unprotected traffic can resume on that socket.
|
||||||
*
|
*
|
||||||
* <p>For security reasons, the destination address of any traffic on the socket must match the
|
* <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
|
* 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
|
* 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
|
* will throw IOException if the user deactivates the transform (by calling {@link
|
||||||
* IpSecTransform#close()}) without calling {@link #removeTransportModeTransform}.
|
* IpSecTransform#close()}) without calling {@link #removeTransportModeTransforms}.
|
||||||
*
|
*
|
||||||
* <h4>Rekey Procedure</h4>
|
* <h4>Rekey Procedure</h4>
|
||||||
*
|
*
|
||||||
@@ -287,14 +306,15 @@ public final class IpSecManager {
|
|||||||
* in-flight packets have been received.
|
* in-flight packets have been received.
|
||||||
*
|
*
|
||||||
* @param socket a stream socket
|
* @param socket a stream socket
|
||||||
|
* @param direction the policy direction either {@link #DIRECTION_IN} or {@link #DIRECTION_OUT}
|
||||||
* @param transform a transport mode {@code IpSecTransform}
|
* @param transform a transport mode {@code IpSecTransform}
|
||||||
* @throws IOException indicating that the transform could not be applied
|
* @throws IOException indicating that the transform could not be applied
|
||||||
* @hide
|
|
||||||
*/
|
*/
|
||||||
public void applyTransportModeTransform(Socket socket, IpSecTransform transform)
|
public void applyTransportModeTransform(
|
||||||
|
Socket socket, int direction, IpSecTransform transform)
|
||||||
throws IOException {
|
throws IOException {
|
||||||
try (ParcelFileDescriptor pfd = ParcelFileDescriptor.fromSocket(socket)) {
|
try (ParcelFileDescriptor pfd = ParcelFileDescriptor.fromSocket(socket)) {
|
||||||
applyTransportModeTransform(pfd, transform);
|
applyTransportModeTransform(pfd, direction, transform);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -303,14 +323,14 @@ public final class IpSecManager {
|
|||||||
*
|
*
|
||||||
* <p>This applies transport mode encapsulation to the given socket. Once applied, I/O on the
|
* <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
|
* socket will be encapsulated according to the parameters of the {@code IpSecTransform}. When
|
||||||
* the transform is removed from the socket by calling {@link #removeTransportModeTransform},
|
* the transform is removed from the socket by calling {@link #removeTransportModeTransforms},
|
||||||
* unprotected traffic can resume on that socket.
|
* unprotected traffic can resume on that socket.
|
||||||
*
|
*
|
||||||
* <p>For security reasons, the destination address of any traffic on the socket must match the
|
* <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
|
* 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
|
* 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
|
* will throw IOException if the user deactivates the transform (by calling {@link
|
||||||
* IpSecTransform#close()}) without calling {@link #removeTransportModeTransform}.
|
* IpSecTransform#close()}) without calling {@link #removeTransportModeTransforms}.
|
||||||
*
|
*
|
||||||
* <h4>Rekey Procedure</h4>
|
* <h4>Rekey Procedure</h4>
|
||||||
*
|
*
|
||||||
@@ -321,14 +341,14 @@ public final class IpSecManager {
|
|||||||
* in-flight packets have been received.
|
* in-flight packets have been received.
|
||||||
*
|
*
|
||||||
* @param socket a datagram socket
|
* @param socket a datagram socket
|
||||||
|
* @param direction the policy direction either DIRECTION_IN or DIRECTION_OUT
|
||||||
* @param transform a transport mode {@code IpSecTransform}
|
* @param transform a transport mode {@code IpSecTransform}
|
||||||
* @throws IOException indicating that the transform could not be applied
|
* @throws IOException indicating that the transform could not be applied
|
||||||
* @hide
|
|
||||||
*/
|
*/
|
||||||
public void applyTransportModeTransform(DatagramSocket socket, IpSecTransform transform)
|
public void applyTransportModeTransform(
|
||||||
throws IOException {
|
DatagramSocket socket, int direction, IpSecTransform transform) throws IOException {
|
||||||
try (ParcelFileDescriptor pfd = ParcelFileDescriptor.fromDatagramSocket(socket)) {
|
try (ParcelFileDescriptor pfd = ParcelFileDescriptor.fromDatagramSocket(socket)) {
|
||||||
applyTransportModeTransform(pfd, transform);
|
applyTransportModeTransform(pfd, direction, transform);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -337,14 +357,14 @@ public final class IpSecManager {
|
|||||||
*
|
*
|
||||||
* <p>This applies transport mode encapsulation to the given socket. Once applied, I/O on the
|
* <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
|
* socket will be encapsulated according to the parameters of the {@code IpSecTransform}. When
|
||||||
* the transform is removed from the socket by calling {@link #removeTransportModeTransform},
|
* the transform is removed from the socket by calling {@link #removeTransportModeTransforms},
|
||||||
* unprotected traffic can resume on that socket.
|
* unprotected traffic can resume on that socket.
|
||||||
*
|
*
|
||||||
* <p>For security reasons, the destination address of any traffic on the socket must match the
|
* <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
|
* 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
|
* 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
|
* will throw IOException if the user deactivates the transform (by calling {@link
|
||||||
* IpSecTransform#close()}) without calling {@link #removeTransportModeTransform}.
|
* IpSecTransform#close()}) without calling {@link #removeTransportModeTransforms}.
|
||||||
*
|
*
|
||||||
* <h4>Rekey Procedure</h4>
|
* <h4>Rekey Procedure</h4>
|
||||||
*
|
*
|
||||||
@@ -355,24 +375,27 @@ public final class IpSecManager {
|
|||||||
* in-flight packets have been received.
|
* in-flight packets have been received.
|
||||||
*
|
*
|
||||||
* @param socket a socket file descriptor
|
* @param socket a socket file descriptor
|
||||||
|
* @param direction the policy direction either DIRECTION_IN or DIRECTION_OUT
|
||||||
* @param transform a transport mode {@code IpSecTransform}
|
* @param transform a transport mode {@code IpSecTransform}
|
||||||
* @throws IOException indicating that the transform could not be applied
|
* @throws IOException indicating that the transform could not be applied
|
||||||
*/
|
*/
|
||||||
public void applyTransportModeTransform(FileDescriptor socket, IpSecTransform transform)
|
public void applyTransportModeTransform(
|
||||||
|
FileDescriptor socket, int direction, IpSecTransform transform)
|
||||||
throws IOException {
|
throws IOException {
|
||||||
// We dup() the FileDescriptor here because if we don't, then the ParcelFileDescriptor()
|
// 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
|
// constructor takes control and closes the user's FD when we exit the method
|
||||||
// This is behaviorally the same as the other versions, but the PFD constructor does not
|
// This is behaviorally the same as the other versions, but the PFD constructor does not
|
||||||
// dup() automatically, whereas PFD.fromSocket() and PDF.fromDatagramSocket() do dup().
|
// dup() automatically, whereas PFD.fromSocket() and PDF.fromDatagramSocket() do dup().
|
||||||
try (ParcelFileDescriptor pfd = ParcelFileDescriptor.dup(socket)) {
|
try (ParcelFileDescriptor pfd = ParcelFileDescriptor.dup(socket)) {
|
||||||
applyTransportModeTransform(pfd, transform);
|
applyTransportModeTransform(pfd, direction, transform);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Call down to activate a transform */
|
/* Call down to activate a transform */
|
||||||
private void applyTransportModeTransform(ParcelFileDescriptor pfd, IpSecTransform transform) {
|
private void applyTransportModeTransform(
|
||||||
|
ParcelFileDescriptor pfd, int direction, IpSecTransform transform) throws IOException {
|
||||||
try {
|
try {
|
||||||
mService.applyTransportModeTransform(pfd, transform.getResourceId());
|
mService.applyTransportModeTransform(pfd, direction, transform.getResourceId());
|
||||||
} catch (RemoteException e) {
|
} catch (RemoteException e) {
|
||||||
throw e.rethrowFromSystemServer();
|
throw e.rethrowFromSystemServer();
|
||||||
}
|
}
|
||||||
@@ -407,12 +430,11 @@ public final class IpSecManager {
|
|||||||
* @param socket a socket that previously had a transform applied to it
|
* @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
|
* @param transform the IPsec Transform that was previously applied to the given socket
|
||||||
* @throws IOException indicating that the transform could not be removed from the socket
|
* @throws IOException indicating that the transform could not be removed from the socket
|
||||||
* @hide
|
|
||||||
*/
|
*/
|
||||||
public void removeTransportModeTransform(Socket socket, IpSecTransform transform)
|
public void removeTransportModeTransforms(Socket socket, IpSecTransform transform)
|
||||||
throws IOException {
|
throws IOException {
|
||||||
try (ParcelFileDescriptor pfd = ParcelFileDescriptor.fromSocket(socket)) {
|
try (ParcelFileDescriptor pfd = ParcelFileDescriptor.fromSocket(socket)) {
|
||||||
removeTransportModeTransform(pfd, transform);
|
removeTransportModeTransforms(pfd, transform);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -430,12 +452,11 @@ public final class IpSecManager {
|
|||||||
* @param socket a socket that previously had a transform applied to it
|
* @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
|
* @param transform the IPsec Transform that was previously applied to the given socket
|
||||||
* @throws IOException indicating that the transform could not be removed from the socket
|
* @throws IOException indicating that the transform could not be removed from the socket
|
||||||
* @hide
|
|
||||||
*/
|
*/
|
||||||
public void removeTransportModeTransform(DatagramSocket socket, IpSecTransform transform)
|
public void removeTransportModeTransforms(DatagramSocket socket, IpSecTransform transform)
|
||||||
throws IOException {
|
throws IOException {
|
||||||
try (ParcelFileDescriptor pfd = ParcelFileDescriptor.fromDatagramSocket(socket)) {
|
try (ParcelFileDescriptor pfd = ParcelFileDescriptor.fromDatagramSocket(socket)) {
|
||||||
removeTransportModeTransform(pfd, transform);
|
removeTransportModeTransforms(pfd, transform);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -454,17 +475,17 @@ public final class IpSecManager {
|
|||||||
* @param transform the IPsec Transform that was previously applied to the given socket
|
* @param transform the IPsec Transform that was previously applied to the given socket
|
||||||
* @throws IOException indicating that the transform could not be removed from the socket
|
* @throws IOException indicating that the transform could not be removed from the socket
|
||||||
*/
|
*/
|
||||||
public void removeTransportModeTransform(FileDescriptor socket, IpSecTransform transform)
|
public void removeTransportModeTransforms(FileDescriptor socket, IpSecTransform transform)
|
||||||
throws IOException {
|
throws IOException {
|
||||||
try (ParcelFileDescriptor pfd = ParcelFileDescriptor.dup(socket)) {
|
try (ParcelFileDescriptor pfd = ParcelFileDescriptor.dup(socket)) {
|
||||||
removeTransportModeTransform(pfd, transform);
|
removeTransportModeTransforms(pfd, transform);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Call down to remove a transform */
|
/* Call down to remove a transform */
|
||||||
private void removeTransportModeTransform(ParcelFileDescriptor pfd, IpSecTransform transform) {
|
private void removeTransportModeTransforms(ParcelFileDescriptor pfd, IpSecTransform transform) {
|
||||||
try {
|
try {
|
||||||
mService.removeTransportModeTransform(pfd, transform.getResourceId());
|
mService.removeTransportModeTransforms(pfd, transform.getResourceId());
|
||||||
} catch (RemoteException e) {
|
} catch (RemoteException e) {
|
||||||
throw e.rethrowFromSystemServer();
|
throw e.rethrowFromSystemServer();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -38,13 +38,11 @@ import java.lang.annotation.RetentionPolicy;
|
|||||||
import java.net.InetAddress;
|
import java.net.InetAddress;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This class represents an IPsec transform, which comprises security associations in one or both
|
* This class represents a transform, which roughly corresponds to an IPsec Security Association.
|
||||||
* directions.
|
|
||||||
*
|
*
|
||||||
* <p>Transforms are created using {@link IpSecTransform.Builder}. Each {@code IpSecTransform}
|
* <p>Transforms are created using {@link IpSecTransform.Builder}. Each {@code IpSecTransform}
|
||||||
* object encapsulates the properties and state of an inbound and outbound IPsec security
|
* object encapsulates the properties and state of an IPsec security association. That includes,
|
||||||
* association. That includes, but is not limited to, algorithm choice, key material, and allocated
|
* but is not limited to, algorithm choice, key material, and allocated system resources.
|
||||||
* system resources.
|
|
||||||
*
|
*
|
||||||
* @see <a href="https://tools.ietf.org/html/rfc4301">RFC 4301, Security Architecture for the
|
* @see <a href="https://tools.ietf.org/html/rfc4301">RFC 4301, Security Architecture for the
|
||||||
* Internet Protocol</a>
|
* Internet Protocol</a>
|
||||||
@@ -52,23 +50,6 @@ import java.net.InetAddress;
|
|||||||
public final class IpSecTransform implements AutoCloseable {
|
public final class IpSecTransform implements AutoCloseable {
|
||||||
private static final String TAG = "IpSecTransform";
|
private static final String TAG = "IpSecTransform";
|
||||||
|
|
||||||
/**
|
|
||||||
* For direction-specific attributes of an {@link IpSecTransform}, indicates that an attribute
|
|
||||||
* applies to traffic towards the host.
|
|
||||||
*/
|
|
||||||
public static final int DIRECTION_IN = 0;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* For direction-specific attributes of an {@link 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 */
|
/** @hide */
|
||||||
public static final int MODE_TRANSPORT = 0;
|
public static final int MODE_TRANSPORT = 0;
|
||||||
|
|
||||||
@@ -170,7 +151,7 @@ public final class IpSecTransform implements AutoCloseable {
|
|||||||
*
|
*
|
||||||
* <p>Deactivating a transform while it is still applied to a socket will result in errors on
|
* <p>Deactivating a transform while it is still applied to a socket will result in errors on
|
||||||
* that socket. Make sure to remove transforms by calling {@link
|
* that socket. Make sure to remove transforms by calling {@link
|
||||||
* IpSecManager#removeTransportModeTransform}. Note, removing an {@code IpSecTransform} from a
|
* IpSecManager#removeTransportModeTransforms}. Note, removing an {@code IpSecTransform} from a
|
||||||
* socket will not deactivate it (because one transform may be applied to multiple sockets).
|
* socket will not deactivate it (because one transform may be applied to multiple sockets).
|
||||||
*
|
*
|
||||||
* <p>It is safe to call this method on a transform that has already been deactivated.
|
* <p>It is safe to call this method on a transform that has already been deactivated.
|
||||||
@@ -272,85 +253,49 @@ public final class IpSecTransform implements AutoCloseable {
|
|||||||
private IpSecConfig mConfig;
|
private IpSecConfig mConfig;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the encryption algorithm for the given direction.
|
* Set the encryption algorithm.
|
||||||
*
|
|
||||||
* <p>If encryption is set for a direction without also providing an SPI for that direction,
|
|
||||||
* creation of an {@code IpSecTransform} will fail when attempting to build the transform.
|
|
||||||
*
|
*
|
||||||
* <p>Encryption is mutually exclusive with authenticated encryption.
|
* <p>Encryption is mutually exclusive with authenticated encryption.
|
||||||
*
|
*
|
||||||
* @param direction either {@link #DIRECTION_IN} or {@link #DIRECTION_OUT}
|
|
||||||
* @param algo {@link IpSecAlgorithm} specifying the encryption to be applied.
|
* @param algo {@link IpSecAlgorithm} specifying the encryption to be applied.
|
||||||
*/
|
*/
|
||||||
public IpSecTransform.Builder setEncryption(
|
public IpSecTransform.Builder setEncryption(@NonNull IpSecAlgorithm algo) {
|
||||||
@TransformDirection int direction, IpSecAlgorithm algo) {
|
|
||||||
// TODO: throw IllegalArgumentException if algo is not an encryption algorithm.
|
// TODO: throw IllegalArgumentException if algo is not an encryption algorithm.
|
||||||
mConfig.setEncryption(direction, algo);
|
Preconditions.checkNotNull(algo);
|
||||||
|
mConfig.setEncryption(algo);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the authentication (integrity) algorithm for the given direction.
|
* Set the authentication (integrity) algorithm.
|
||||||
*
|
|
||||||
* <p>If authentication is set for a direction without also providing an SPI for that
|
|
||||||
* direction, creation of an {@code IpSecTransform} will fail when attempting to build the
|
|
||||||
* transform.
|
|
||||||
*
|
*
|
||||||
* <p>Authentication is mutually exclusive with authenticated encryption.
|
* <p>Authentication is mutually exclusive with authenticated encryption.
|
||||||
*
|
*
|
||||||
* @param direction either {@link #DIRECTION_IN} or {@link #DIRECTION_OUT}
|
|
||||||
* @param algo {@link IpSecAlgorithm} specifying the authentication to be applied.
|
* @param algo {@link IpSecAlgorithm} specifying the authentication to be applied.
|
||||||
*/
|
*/
|
||||||
public IpSecTransform.Builder setAuthentication(
|
public IpSecTransform.Builder setAuthentication(@NonNull IpSecAlgorithm algo) {
|
||||||
@TransformDirection int direction, IpSecAlgorithm algo) {
|
|
||||||
// TODO: throw IllegalArgumentException if algo is not an authentication algorithm.
|
// TODO: throw IllegalArgumentException if algo is not an authentication algorithm.
|
||||||
mConfig.setAuthentication(direction, algo);
|
Preconditions.checkNotNull(algo);
|
||||||
|
mConfig.setAuthentication(algo);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the authenticated encryption algorithm for the given direction.
|
* Set the authenticated encryption algorithm.
|
||||||
*
|
*
|
||||||
* <p>If an authenticated encryption algorithm is set for a given direction without also
|
* <p>The Authenticated Encryption (AE) class of algorithms are also known as
|
||||||
* providing an SPI for that direction, creation of an {@code IpSecTransform} will fail when
|
* Authenticated Encryption with Associated Data (AEAD) algorithms, or Combined mode
|
||||||
* attempting to build the transform.
|
* algorithms (as referred to in
|
||||||
*
|
* <a href="https://tools.ietf.org/html/rfc4301">RFC 4301</a>).
|
||||||
* <p>The Authenticated Encryption (AE) class of algorithms are also known as Authenticated
|
|
||||||
* Encryption with Associated Data (AEAD) algorithms, or Combined mode algorithms (as
|
|
||||||
* referred to in <a href="https://tools.ietf.org/html/rfc4301">RFC 4301</a>).
|
|
||||||
*
|
*
|
||||||
* <p>Authenticated encryption is mutually exclusive with encryption and authentication.
|
* <p>Authenticated encryption is mutually exclusive with encryption and authentication.
|
||||||
*
|
*
|
||||||
* @param direction either {@link #DIRECTION_IN} or {@link #DIRECTION_OUT}
|
|
||||||
* @param algo {@link IpSecAlgorithm} specifying the authenticated encryption algorithm to
|
* @param algo {@link IpSecAlgorithm} specifying the authenticated encryption algorithm to
|
||||||
* be applied.
|
* be applied.
|
||||||
*/
|
*/
|
||||||
public IpSecTransform.Builder setAuthenticatedEncryption(
|
public IpSecTransform.Builder setAuthenticatedEncryption(@NonNull IpSecAlgorithm algo) {
|
||||||
@TransformDirection int direction, IpSecAlgorithm algo) {
|
Preconditions.checkNotNull(algo);
|
||||||
mConfig.setAuthenticatedEncryption(direction, algo);
|
mConfig.setAuthenticatedEncryption(algo);
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set the SPI for the given direction.
|
|
||||||
*
|
|
||||||
* <p>Because IPsec operates at the IP layer, this 32-bit identifier uniquely identifies
|
|
||||||
* packets to a given destination address. To prevent SPI collisions, values should be
|
|
||||||
* reserved by calling {@link IpSecManager#allocateSecurityParameterIndex}.
|
|
||||||
*
|
|
||||||
* <p>If the SPI and algorithms are omitted for one direction, traffic in that direction
|
|
||||||
* will not be encrypted or authenticated.
|
|
||||||
*
|
|
||||||
* @param direction either {@link #DIRECTION_IN} or {@link #DIRECTION_OUT}
|
|
||||||
* @param spi a unique {@link IpSecManager.SecurityParameterIndex} to identify transformed
|
|
||||||
* traffic
|
|
||||||
*/
|
|
||||||
public IpSecTransform.Builder setSpi(
|
|
||||||
@TransformDirection int direction, IpSecManager.SecurityParameterIndex spi) {
|
|
||||||
if (spi.getResourceId() == INVALID_RESOURCE_ID) {
|
|
||||||
throw new IllegalArgumentException("Invalid SecurityParameterIndex");
|
|
||||||
}
|
|
||||||
mConfig.setSpiResourceId(direction, spi.getResourceId());
|
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -363,7 +308,8 @@ public final class IpSecTransform implements AutoCloseable {
|
|||||||
* @hide
|
* @hide
|
||||||
*/
|
*/
|
||||||
@SystemApi
|
@SystemApi
|
||||||
public IpSecTransform.Builder setUnderlyingNetwork(Network net) {
|
public IpSecTransform.Builder setUnderlyingNetwork(@NonNull Network net) {
|
||||||
|
Preconditions.checkNotNull(net);
|
||||||
mConfig.setNetwork(net);
|
mConfig.setNetwork(net);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
@@ -382,7 +328,8 @@ public final class IpSecTransform implements AutoCloseable {
|
|||||||
* encapsulated traffic. In the case of IKEv2, this should be port 4500.
|
* encapsulated traffic. In the case of IKEv2, this should be port 4500.
|
||||||
*/
|
*/
|
||||||
public IpSecTransform.Builder setIpv4Encapsulation(
|
public IpSecTransform.Builder setIpv4Encapsulation(
|
||||||
IpSecManager.UdpEncapsulationSocket localSocket, int remotePort) {
|
@NonNull IpSecManager.UdpEncapsulationSocket localSocket, int remotePort) {
|
||||||
|
Preconditions.checkNotNull(localSocket);
|
||||||
mConfig.setEncapType(ENCAP_ESPINUDP);
|
mConfig.setEncapType(ENCAP_ESPINUDP);
|
||||||
if (localSocket.getResourceId() == INVALID_RESOURCE_ID) {
|
if (localSocket.getResourceId() == INVALID_RESOURCE_ID) {
|
||||||
throw new IllegalArgumentException("Invalid UdpEncapsulationSocket");
|
throw new IllegalArgumentException("Invalid UdpEncapsulationSocket");
|
||||||
@@ -419,24 +366,33 @@ public final class IpSecTransform implements AutoCloseable {
|
|||||||
* will not affect any network traffic until it has been applied to one or more sockets.
|
* will not affect any network traffic until it has been applied to one or more sockets.
|
||||||
*
|
*
|
||||||
* @see IpSecManager#applyTransportModeTransform
|
* @see IpSecManager#applyTransportModeTransform
|
||||||
* @param remoteAddress the remote {@code InetAddress} of traffic on sockets that will use
|
* @param sourceAddress the source {@code InetAddress} of traffic on sockets that will use
|
||||||
* this transform
|
* this transform; this address must belong to the Network used by all sockets that
|
||||||
|
* utilize this transform; if provided, then only traffic originating from the
|
||||||
|
* specified source address will be processed.
|
||||||
|
* @param spi a unique {@link IpSecManager.SecurityParameterIndex} to identify transformed
|
||||||
|
* traffic
|
||||||
* @throws IllegalArgumentException indicating that a particular combination of transform
|
* @throws IllegalArgumentException indicating that a particular combination of transform
|
||||||
* properties is invalid
|
* properties is invalid
|
||||||
* @throws IpSecManager.ResourceUnavailableException indicating that too many transforms are
|
* @throws IpSecManager.ResourceUnavailableException indicating that too many transforms
|
||||||
* active
|
* are active
|
||||||
* @throws IpSecManager.SpiUnavailableException indicating the rare case where an SPI
|
* @throws IpSecManager.SpiUnavailableException indicating the rare case where an SPI
|
||||||
* collides with an existing transform
|
* collides with an existing transform
|
||||||
* @throws IOException indicating other errors
|
* @throws IOException indicating other errors
|
||||||
*/
|
*/
|
||||||
public IpSecTransform buildTransportModeTransform(InetAddress remoteAddress)
|
public IpSecTransform buildTransportModeTransform(
|
||||||
|
@NonNull InetAddress sourceAddress,
|
||||||
|
@NonNull IpSecManager.SecurityParameterIndex spi)
|
||||||
throws IpSecManager.ResourceUnavailableException,
|
throws IpSecManager.ResourceUnavailableException,
|
||||||
IpSecManager.SpiUnavailableException, IOException {
|
IpSecManager.SpiUnavailableException, IOException {
|
||||||
if (remoteAddress == null) {
|
Preconditions.checkNotNull(sourceAddress);
|
||||||
throw new IllegalArgumentException("Remote address may not be null or empty!");
|
Preconditions.checkNotNull(spi);
|
||||||
|
if (spi.getResourceId() == INVALID_RESOURCE_ID) {
|
||||||
|
throw new IllegalArgumentException("Invalid SecurityParameterIndex");
|
||||||
}
|
}
|
||||||
mConfig.setMode(MODE_TRANSPORT);
|
mConfig.setMode(MODE_TRANSPORT);
|
||||||
mConfig.setRemoteAddress(remoteAddress.getHostAddress());
|
mConfig.setSourceAddress(sourceAddress.getHostAddress());
|
||||||
|
mConfig.setSpiResourceId(spi.getResourceId());
|
||||||
// FIXME: modifying a builder after calling build can change the built transform.
|
// FIXME: modifying a builder after calling build can change the built transform.
|
||||||
return new IpSecTransform(mContext, mConfig).activate();
|
return new IpSecTransform(mContext, mConfig).activate();
|
||||||
}
|
}
|
||||||
@@ -445,26 +401,33 @@ public final class IpSecTransform implements AutoCloseable {
|
|||||||
* Build and return an {@link IpSecTransform} object as a Tunnel Mode Transform. Some
|
* Build and return an {@link IpSecTransform} object as a Tunnel Mode Transform. Some
|
||||||
* parameters have interdependencies that are checked at build time.
|
* parameters have interdependencies that are checked at build time.
|
||||||
*
|
*
|
||||||
* @param localAddress the {@link InetAddress} that provides the local endpoint for this
|
* @param sourceAddress the {@link InetAddress} that provides the source address for this
|
||||||
* IPsec tunnel. This is almost certainly an address belonging to the {@link Network}
|
* 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}.
|
* that will originate the traffic, which is set as the {@link #setUnderlyingNetwork}.
|
||||||
* @param remoteAddress the {@link InetAddress} representing the remote endpoint of this
|
* @param spi a unique {@link IpSecManager.SecurityParameterIndex} to identify transformed
|
||||||
* IPsec tunnel.
|
* traffic
|
||||||
* @throws IllegalArgumentException indicating that a particular combination of transform
|
* @throws IllegalArgumentException indicating that a particular combination of transform
|
||||||
* properties is invalid.
|
* properties is invalid.
|
||||||
|
* @throws IpSecManager.ResourceUnavailableException indicating that too many transforms
|
||||||
|
* are active
|
||||||
|
* @throws IpSecManager.SpiUnavailableException indicating the rare case where an SPI
|
||||||
|
* collides with an existing transform
|
||||||
|
* @throws IOException indicating other errors
|
||||||
* @hide
|
* @hide
|
||||||
*/
|
*/
|
||||||
public IpSecTransform buildTunnelModeTransform(
|
public IpSecTransform buildTunnelModeTransform(
|
||||||
InetAddress localAddress, InetAddress remoteAddress) {
|
@NonNull InetAddress sourceAddress,
|
||||||
if (localAddress == null) {
|
@NonNull IpSecManager.SecurityParameterIndex spi)
|
||||||
throw new IllegalArgumentException("Local address may not be null or empty!");
|
throws IpSecManager.ResourceUnavailableException,
|
||||||
|
IpSecManager.SpiUnavailableException, IOException {
|
||||||
|
Preconditions.checkNotNull(sourceAddress);
|
||||||
|
Preconditions.checkNotNull(spi);
|
||||||
|
if (spi.getResourceId() == INVALID_RESOURCE_ID) {
|
||||||
|
throw new IllegalArgumentException("Invalid SecurityParameterIndex");
|
||||||
}
|
}
|
||||||
if (remoteAddress == null) {
|
|
||||||
throw new IllegalArgumentException("Remote address may not be null or empty!");
|
|
||||||
}
|
|
||||||
mConfig.setLocalAddress(localAddress.getHostAddress());
|
|
||||||
mConfig.setRemoteAddress(remoteAddress.getHostAddress());
|
|
||||||
mConfig.setMode(MODE_TUNNEL);
|
mConfig.setMode(MODE_TUNNEL);
|
||||||
|
mConfig.setSourceAddress(sourceAddress.getHostAddress());
|
||||||
|
mConfig.setSpiResourceId(spi.getResourceId());
|
||||||
return new IpSecTransform(mContext, mConfig);
|
return new IpSecTransform(mContext, mConfig);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ import static android.system.OsConstants.SOCK_DGRAM;
|
|||||||
import static com.android.internal.util.Preconditions.checkNotNull;
|
import static com.android.internal.util.Preconditions.checkNotNull;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
import android.net.ConnectivityManager;
|
||||||
import android.net.IIpSecService;
|
import android.net.IIpSecService;
|
||||||
import android.net.INetd;
|
import android.net.INetd;
|
||||||
import android.net.IpSecAlgorithm;
|
import android.net.IpSecAlgorithm;
|
||||||
@@ -62,7 +63,6 @@ import java.net.InetSocketAddress;
|
|||||||
import java.net.UnknownHostException;
|
import java.net.UnknownHostException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
|
||||||
|
|
||||||
import libcore.io.IoUtils;
|
import libcore.io.IoUtils;
|
||||||
|
|
||||||
@@ -83,7 +83,7 @@ public class IpSecService extends IIpSecService.Stub {
|
|||||||
|
|
||||||
private static final String NETD_SERVICE_NAME = "netd";
|
private static final String NETD_SERVICE_NAME = "netd";
|
||||||
private static final int[] DIRECTIONS =
|
private static final int[] DIRECTIONS =
|
||||||
new int[] {IpSecTransform.DIRECTION_OUT, IpSecTransform.DIRECTION_IN};
|
new int[] {IpSecManager.DIRECTION_OUT, IpSecManager.DIRECTION_IN};
|
||||||
|
|
||||||
private static final int NETD_FETCH_TIMEOUT_MS = 5000; // ms
|
private static final int NETD_FETCH_TIMEOUT_MS = 5000; // ms
|
||||||
private static final int MAX_PORT_BIND_ATTEMPTS = 10;
|
private static final int MAX_PORT_BIND_ATTEMPTS = 10;
|
||||||
@@ -104,10 +104,10 @@ public class IpSecService extends IIpSecService.Stub {
|
|||||||
private final Context mContext;
|
private final Context mContext;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The next non-repeating global ID for tracking resources between users, this service,
|
* The next non-repeating global ID for tracking resources between users, this service, and
|
||||||
* and kernel data structures. Accessing this variable is not thread safe, so it is
|
* kernel data structures. Accessing this variable is not thread safe, so it is only read or
|
||||||
* only read or modified within blocks synchronized on IpSecService.this. We want to
|
* modified within blocks synchronized on IpSecService.this. We want to avoid -1
|
||||||
* avoid -1 (INVALID_RESOURCE_ID) and 0 (we probably forgot to initialize it).
|
* (INVALID_RESOURCE_ID) and 0 (we probably forgot to initialize it).
|
||||||
*/
|
*/
|
||||||
@GuardedBy("IpSecService.this")
|
@GuardedBy("IpSecService.this")
|
||||||
private int mNextResourceId = 1;
|
private int mNextResourceId = 1;
|
||||||
@@ -536,14 +536,14 @@ public class IpSecService extends IIpSecService.Stub {
|
|||||||
|
|
||||||
private final class TransformRecord extends KernelResourceRecord {
|
private final class TransformRecord extends KernelResourceRecord {
|
||||||
private final IpSecConfig mConfig;
|
private final IpSecConfig mConfig;
|
||||||
private final SpiRecord[] mSpis;
|
private final SpiRecord mSpi;
|
||||||
private final EncapSocketRecord mSocket;
|
private final EncapSocketRecord mSocket;
|
||||||
|
|
||||||
TransformRecord(
|
TransformRecord(
|
||||||
int resourceId, IpSecConfig config, SpiRecord[] spis, EncapSocketRecord socket) {
|
int resourceId, IpSecConfig config, SpiRecord spi, EncapSocketRecord socket) {
|
||||||
super(resourceId);
|
super(resourceId);
|
||||||
mConfig = config;
|
mConfig = config;
|
||||||
mSpis = spis;
|
mSpi = spi;
|
||||||
mSocket = socket;
|
mSocket = socket;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -551,29 +551,26 @@ public class IpSecService extends IIpSecService.Stub {
|
|||||||
return mConfig;
|
return mConfig;
|
||||||
}
|
}
|
||||||
|
|
||||||
public SpiRecord getSpiRecord(int direction) {
|
public SpiRecord getSpiRecord() {
|
||||||
return mSpis[direction];
|
return mSpi;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** always guarded by IpSecService#this */
|
/** always guarded by IpSecService#this */
|
||||||
@Override
|
@Override
|
||||||
public void freeUnderlyingResources() {
|
public void freeUnderlyingResources() {
|
||||||
for (int direction : DIRECTIONS) {
|
int spi = mSpi.getSpi();
|
||||||
int spi = mSpis[direction].getSpi();
|
try {
|
||||||
try {
|
mSrvConfig
|
||||||
mSrvConfig
|
.getNetdInstance()
|
||||||
.getNetdInstance()
|
.ipSecDeleteSecurityAssociation(
|
||||||
.ipSecDeleteSecurityAssociation(
|
mResourceId,
|
||||||
mResourceId,
|
mConfig.getSourceAddress(),
|
||||||
direction,
|
mConfig.getDestinationAddress(),
|
||||||
mConfig.getLocalAddress(),
|
spi);
|
||||||
mConfig.getRemoteAddress(),
|
} catch (ServiceSpecificException e) {
|
||||||
spi);
|
// FIXME: get the error code and throw is at an IOException from Errno Exception
|
||||||
} catch (ServiceSpecificException e) {
|
} catch (RemoteException e) {
|
||||||
// FIXME: get the error code and throw is at an IOException from Errno Exception
|
Log.e(TAG, "Failed to delete SA with ID: " + mResourceId);
|
||||||
} catch (RemoteException e) {
|
|
||||||
Log.e(TAG, "Failed to delete SA with ID: " + mResourceId);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
getResourceTracker().give();
|
getResourceTracker().give();
|
||||||
@@ -597,10 +594,8 @@ public class IpSecService extends IIpSecService.Stub {
|
|||||||
.append(super.toString())
|
.append(super.toString())
|
||||||
.append(", mSocket=")
|
.append(", mSocket=")
|
||||||
.append(mSocket)
|
.append(mSocket)
|
||||||
.append(", mSpis[OUT].mResourceId=")
|
.append(", mSpi.mResourceId=")
|
||||||
.append(mSpis[IpSecTransform.DIRECTION_OUT].mResourceId)
|
.append(mSpi.mResourceId)
|
||||||
.append(", mSpis[IN].mResourceId=")
|
|
||||||
.append(mSpis[IpSecTransform.DIRECTION_IN].mResourceId)
|
|
||||||
.append(", mConfig=")
|
.append(", mConfig=")
|
||||||
.append(mConfig)
|
.append(mConfig)
|
||||||
.append("}");
|
.append("}");
|
||||||
@@ -609,23 +604,16 @@ public class IpSecService extends IIpSecService.Stub {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private final class SpiRecord extends KernelResourceRecord {
|
private final class SpiRecord extends KernelResourceRecord {
|
||||||
private final int mDirection;
|
private final String mSourceAddress;
|
||||||
private final String mLocalAddress;
|
private final String mDestinationAddress;
|
||||||
private final String mRemoteAddress;
|
|
||||||
private int mSpi;
|
private int mSpi;
|
||||||
|
|
||||||
private boolean mOwnedByTransform = false;
|
private boolean mOwnedByTransform = false;
|
||||||
|
|
||||||
SpiRecord(
|
SpiRecord(int resourceId, String sourceAddress, String destinationAddress, int spi) {
|
||||||
int resourceId,
|
|
||||||
int direction,
|
|
||||||
String localAddress,
|
|
||||||
String remoteAddress,
|
|
||||||
int spi) {
|
|
||||||
super(resourceId);
|
super(resourceId);
|
||||||
mDirection = direction;
|
mSourceAddress = sourceAddress;
|
||||||
mLocalAddress = localAddress;
|
mDestinationAddress = destinationAddress;
|
||||||
mRemoteAddress = remoteAddress;
|
|
||||||
mSpi = spi;
|
mSpi = spi;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -646,7 +634,7 @@ public class IpSecService extends IIpSecService.Stub {
|
|||||||
mSrvConfig
|
mSrvConfig
|
||||||
.getNetdInstance()
|
.getNetdInstance()
|
||||||
.ipSecDeleteSecurityAssociation(
|
.ipSecDeleteSecurityAssociation(
|
||||||
mResourceId, mDirection, mLocalAddress, mRemoteAddress, mSpi);
|
mResourceId, mSourceAddress, mDestinationAddress, mSpi);
|
||||||
} catch (ServiceSpecificException e) {
|
} catch (ServiceSpecificException e) {
|
||||||
// FIXME: get the error code and throw is at an IOException from Errno Exception
|
// FIXME: get the error code and throw is at an IOException from Errno Exception
|
||||||
} catch (RemoteException e) {
|
} catch (RemoteException e) {
|
||||||
@@ -662,6 +650,10 @@ public class IpSecService extends IIpSecService.Stub {
|
|||||||
return mSpi;
|
return mSpi;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getDestinationAddress() {
|
||||||
|
return mDestinationAddress;
|
||||||
|
}
|
||||||
|
|
||||||
public void setOwnedByTransform() {
|
public void setOwnedByTransform() {
|
||||||
if (mOwnedByTransform) {
|
if (mOwnedByTransform) {
|
||||||
// Programming error
|
// Programming error
|
||||||
@@ -689,12 +681,10 @@ public class IpSecService extends IIpSecService.Stub {
|
|||||||
.append(super.toString())
|
.append(super.toString())
|
||||||
.append(", mSpi=")
|
.append(", mSpi=")
|
||||||
.append(mSpi)
|
.append(mSpi)
|
||||||
.append(", mDirection=")
|
.append(", mSourceAddress=")
|
||||||
.append(mDirection)
|
.append(mSourceAddress)
|
||||||
.append(", mLocalAddress=")
|
.append(", mDestinationAddress=")
|
||||||
.append(mLocalAddress)
|
.append(mDestinationAddress)
|
||||||
.append(", mRemoteAddress=")
|
|
||||||
.append(mRemoteAddress)
|
|
||||||
.append(", mOwnedByTransform=")
|
.append(", mOwnedByTransform=")
|
||||||
.append(mOwnedByTransform)
|
.append(mOwnedByTransform)
|
||||||
.append("}");
|
.append("}");
|
||||||
@@ -772,14 +762,17 @@ public class IpSecService extends IIpSecService.Stub {
|
|||||||
/** @hide */
|
/** @hide */
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
public IpSecService(Context context, IpSecServiceConfiguration config) {
|
public IpSecService(Context context, IpSecServiceConfiguration config) {
|
||||||
this(context, config, (fd, uid) -> {
|
this(
|
||||||
try{
|
context,
|
||||||
TrafficStats.setThreadStatsUid(uid);
|
config,
|
||||||
TrafficStats.tagFileDescriptor(fd);
|
(fd, uid) -> {
|
||||||
} finally {
|
try {
|
||||||
TrafficStats.clearThreadStatsUid();
|
TrafficStats.setThreadStatsUid(uid);
|
||||||
}
|
TrafficStats.tagFileDescriptor(fd);
|
||||||
});
|
} finally {
|
||||||
|
TrafficStats.clearThreadStatsUid();
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @hide */
|
/** @hide */
|
||||||
@@ -845,8 +838,8 @@ public class IpSecService extends IIpSecService.Stub {
|
|||||||
*/
|
*/
|
||||||
private static void checkDirection(int direction) {
|
private static void checkDirection(int direction) {
|
||||||
switch (direction) {
|
switch (direction) {
|
||||||
case IpSecTransform.DIRECTION_OUT:
|
case IpSecManager.DIRECTION_OUT:
|
||||||
case IpSecTransform.DIRECTION_IN:
|
case IpSecManager.DIRECTION_IN:
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
throw new IllegalArgumentException("Invalid Direction: " + direction);
|
throw new IllegalArgumentException("Invalid Direction: " + direction);
|
||||||
@@ -855,10 +848,8 @@ public class IpSecService extends IIpSecService.Stub {
|
|||||||
/** Get a new SPI and maintain the reservation in the system server */
|
/** Get a new SPI and maintain the reservation in the system server */
|
||||||
@Override
|
@Override
|
||||||
public synchronized IpSecSpiResponse allocateSecurityParameterIndex(
|
public synchronized IpSecSpiResponse allocateSecurityParameterIndex(
|
||||||
int direction, String remoteAddress, int requestedSpi, IBinder binder)
|
String destinationAddress, int requestedSpi, IBinder binder) throws RemoteException {
|
||||||
throws RemoteException {
|
checkInetAddress(destinationAddress);
|
||||||
checkDirection(direction);
|
|
||||||
checkInetAddress(remoteAddress);
|
|
||||||
/* requestedSpi can be anything in the int range, so no check is needed. */
|
/* requestedSpi can be anything in the int range, so no check is needed. */
|
||||||
checkNotNull(binder, "Null Binder passed to allocateSecurityParameterIndex");
|
checkNotNull(binder, "Null Binder passed to allocateSecurityParameterIndex");
|
||||||
|
|
||||||
@@ -866,28 +857,21 @@ public class IpSecService extends IIpSecService.Stub {
|
|||||||
final int resourceId = mNextResourceId++;
|
final int resourceId = mNextResourceId++;
|
||||||
|
|
||||||
int spi = IpSecManager.INVALID_SECURITY_PARAMETER_INDEX;
|
int spi = IpSecManager.INVALID_SECURITY_PARAMETER_INDEX;
|
||||||
String localAddress = "";
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (!userRecord.mSpiQuotaTracker.isAvailable()) {
|
if (!userRecord.mSpiQuotaTracker.isAvailable()) {
|
||||||
return new IpSecSpiResponse(
|
return new IpSecSpiResponse(
|
||||||
IpSecManager.Status.RESOURCE_UNAVAILABLE, INVALID_RESOURCE_ID, spi);
|
IpSecManager.Status.RESOURCE_UNAVAILABLE, INVALID_RESOURCE_ID, spi);
|
||||||
}
|
}
|
||||||
|
|
||||||
spi =
|
spi =
|
||||||
mSrvConfig
|
mSrvConfig
|
||||||
.getNetdInstance()
|
.getNetdInstance()
|
||||||
.ipSecAllocateSpi(
|
.ipSecAllocateSpi(resourceId, "", destinationAddress, requestedSpi);
|
||||||
resourceId,
|
|
||||||
direction,
|
|
||||||
localAddress,
|
|
||||||
remoteAddress,
|
|
||||||
requestedSpi);
|
|
||||||
Log.d(TAG, "Allocated SPI " + spi);
|
Log.d(TAG, "Allocated SPI " + spi);
|
||||||
userRecord.mSpiRecords.put(
|
userRecord.mSpiRecords.put(
|
||||||
resourceId,
|
resourceId,
|
||||||
new RefcountedResource<SpiRecord>(
|
new RefcountedResource<SpiRecord>(
|
||||||
new SpiRecord(resourceId, direction, localAddress, remoteAddress, spi),
|
new SpiRecord(resourceId, "", destinationAddress, spi), binder));
|
||||||
binder));
|
|
||||||
} catch (ServiceSpecificException e) {
|
} catch (ServiceSpecificException e) {
|
||||||
// TODO: Add appropriate checks when other ServiceSpecificException types are supported
|
// TODO: Add appropriate checks when other ServiceSpecificException types are supported
|
||||||
return new IpSecSpiResponse(
|
return new IpSecSpiResponse(
|
||||||
@@ -1032,27 +1016,27 @@ public class IpSecService extends IIpSecService.Stub {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
void validateAlgorithms(IpSecConfig config, int direction) throws IllegalArgumentException {
|
void validateAlgorithms(IpSecConfig config) throws IllegalArgumentException {
|
||||||
IpSecAlgorithm auth = config.getAuthentication(direction);
|
IpSecAlgorithm auth = config.getAuthentication();
|
||||||
IpSecAlgorithm crypt = config.getEncryption(direction);
|
IpSecAlgorithm crypt = config.getEncryption();
|
||||||
IpSecAlgorithm aead = config.getAuthenticatedEncryption(direction);
|
IpSecAlgorithm aead = config.getAuthenticatedEncryption();
|
||||||
|
|
||||||
// Validate the algorithm set
|
// Validate the algorithm set
|
||||||
Preconditions.checkArgument(
|
Preconditions.checkArgument(
|
||||||
aead != null || crypt != null || auth != null,
|
aead != null || crypt != null || auth != null,
|
||||||
"No Encryption or Authentication algorithms specified");
|
"No Encryption or Authentication algorithms specified");
|
||||||
Preconditions.checkArgument(
|
Preconditions.checkArgument(
|
||||||
auth == null || auth.isAuthentication(),
|
auth == null || auth.isAuthentication(),
|
||||||
"Unsupported algorithm for Authentication");
|
"Unsupported algorithm for Authentication");
|
||||||
Preconditions.checkArgument(
|
Preconditions.checkArgument(
|
||||||
crypt == null || crypt.isEncryption(), "Unsupported algorithm for Encryption");
|
crypt == null || crypt.isEncryption(), "Unsupported algorithm for Encryption");
|
||||||
Preconditions.checkArgument(
|
Preconditions.checkArgument(
|
||||||
aead == null || aead.isAead(),
|
aead == null || aead.isAead(),
|
||||||
"Unsupported algorithm for Authenticated Encryption");
|
"Unsupported algorithm for Authenticated Encryption");
|
||||||
Preconditions.checkArgument(
|
Preconditions.checkArgument(
|
||||||
aead == null || (auth == null && crypt == null),
|
aead == null || (auth == null && crypt == null),
|
||||||
"Authenticated Encryption is mutually exclusive with other Authentication "
|
"Authenticated Encryption is mutually exclusive with other Authentication "
|
||||||
+ "or Encryption algorithms");
|
+ "or Encryption algorithms");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1062,29 +1046,6 @@ public class IpSecService extends IIpSecService.Stub {
|
|||||||
private void checkIpSecConfig(IpSecConfig config) {
|
private void checkIpSecConfig(IpSecConfig config) {
|
||||||
UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid());
|
UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid());
|
||||||
|
|
||||||
if (config.getLocalAddress() == null) {
|
|
||||||
throw new IllegalArgumentException("Invalid null Local InetAddress");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (config.getRemoteAddress() == null) {
|
|
||||||
throw new IllegalArgumentException("Invalid null Remote InetAddress");
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (config.getMode()) {
|
|
||||||
case IpSecTransform.MODE_TRANSPORT:
|
|
||||||
if (!config.getLocalAddress().isEmpty()) {
|
|
||||||
throw new IllegalArgumentException("Non-empty Local Address");
|
|
||||||
}
|
|
||||||
// Must be valid, and not a wildcard
|
|
||||||
checkInetAddress(config.getRemoteAddress());
|
|
||||||
break;
|
|
||||||
case IpSecTransform.MODE_TUNNEL:
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throw new IllegalArgumentException(
|
|
||||||
"Invalid IpSecTransform.mode: " + config.getMode());
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (config.getEncapType()) {
|
switch (config.getEncapType()) {
|
||||||
case IpSecTransform.ENCAP_NONE:
|
case IpSecTransform.ENCAP_NONE:
|
||||||
break;
|
break;
|
||||||
@@ -1103,11 +1064,36 @@ public class IpSecService extends IIpSecService.Stub {
|
|||||||
throw new IllegalArgumentException("Invalid Encap Type: " + config.getEncapType());
|
throw new IllegalArgumentException("Invalid Encap Type: " + config.getEncapType());
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int direction : DIRECTIONS) {
|
validateAlgorithms(config);
|
||||||
validateAlgorithms(config, direction);
|
|
||||||
|
|
||||||
// Retrieve SPI record; will throw IllegalArgumentException if not found
|
// Retrieve SPI record; will throw IllegalArgumentException if not found
|
||||||
userRecord.mSpiRecords.getResourceOrThrow(config.getSpiResourceId(direction));
|
SpiRecord s = userRecord.mSpiRecords.getResourceOrThrow(config.getSpiResourceId());
|
||||||
|
|
||||||
|
// If no remote address is supplied, then use one from the SPI.
|
||||||
|
if (TextUtils.isEmpty(config.getDestinationAddress())) {
|
||||||
|
config.setDestinationAddress(s.getDestinationAddress());
|
||||||
|
}
|
||||||
|
|
||||||
|
// All remote addresses must match
|
||||||
|
if (!config.getDestinationAddress().equals(s.getDestinationAddress())) {
|
||||||
|
throw new IllegalArgumentException("Mismatched remote addresseses.");
|
||||||
|
}
|
||||||
|
|
||||||
|
// This check is technically redundant due to the chain of custody between the SPI and
|
||||||
|
// the IpSecConfig, but in the future if the dest is allowed to be set explicitly in
|
||||||
|
// the transform, this will prevent us from messing up.
|
||||||
|
checkInetAddress(config.getDestinationAddress());
|
||||||
|
|
||||||
|
// Require a valid source address for all transforms.
|
||||||
|
checkInetAddress(config.getSourceAddress());
|
||||||
|
|
||||||
|
switch (config.getMode()) {
|
||||||
|
case IpSecTransform.MODE_TRANSPORT:
|
||||||
|
case IpSecTransform.MODE_TUNNEL:
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new IllegalArgumentException(
|
||||||
|
"Invalid IpSecTransform.mode: " + config.getMode());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1127,13 +1113,12 @@ public class IpSecService extends IIpSecService.Stub {
|
|||||||
|
|
||||||
UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid());
|
UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid());
|
||||||
|
|
||||||
// Avoid resizing by creating a dependency array of min-size 3 (1 UDP encap + 2 SPIs)
|
// Avoid resizing by creating a dependency array of min-size 2 (1 UDP encap + 1 SPI)
|
||||||
List<RefcountedResource> dependencies = new ArrayList<>(3);
|
List<RefcountedResource> dependencies = new ArrayList<>(2);
|
||||||
|
|
||||||
if (!userRecord.mTransformQuotaTracker.isAvailable()) {
|
if (!userRecord.mTransformQuotaTracker.isAvailable()) {
|
||||||
return new IpSecTransformResponse(IpSecManager.Status.RESOURCE_UNAVAILABLE);
|
return new IpSecTransformResponse(IpSecManager.Status.RESOURCE_UNAVAILABLE);
|
||||||
}
|
}
|
||||||
SpiRecord[] spis = new SpiRecord[DIRECTIONS.length];
|
|
||||||
|
|
||||||
int encapType, encapLocalPort = 0, encapRemotePort = 0;
|
int encapType, encapLocalPort = 0, encapRemotePort = 0;
|
||||||
EncapSocketRecord socketRecord = null;
|
EncapSocketRecord socketRecord = null;
|
||||||
@@ -1149,51 +1134,46 @@ public class IpSecService extends IIpSecService.Stub {
|
|||||||
encapRemotePort = c.getEncapRemotePort();
|
encapRemotePort = c.getEncapRemotePort();
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int direction : DIRECTIONS) {
|
IpSecAlgorithm auth = c.getAuthentication();
|
||||||
IpSecAlgorithm auth = c.getAuthentication(direction);
|
IpSecAlgorithm crypt = c.getEncryption();
|
||||||
IpSecAlgorithm crypt = c.getEncryption(direction);
|
IpSecAlgorithm authCrypt = c.getAuthenticatedEncryption();
|
||||||
IpSecAlgorithm authCrypt = c.getAuthenticatedEncryption(direction);
|
|
||||||
|
|
||||||
RefcountedResource<SpiRecord> refcountedSpiRecord =
|
RefcountedResource<SpiRecord> refcountedSpiRecord =
|
||||||
userRecord.mSpiRecords.getRefcountedResourceOrThrow(
|
userRecord.mSpiRecords.getRefcountedResourceOrThrow(c.getSpiResourceId());
|
||||||
c.getSpiResourceId(direction));
|
dependencies.add(refcountedSpiRecord);
|
||||||
dependencies.add(refcountedSpiRecord);
|
SpiRecord spiRecord = refcountedSpiRecord.getResource();
|
||||||
|
|
||||||
spis[direction] = refcountedSpiRecord.getResource();
|
try {
|
||||||
int spi = spis[direction].getSpi();
|
mSrvConfig
|
||||||
try {
|
.getNetdInstance()
|
||||||
mSrvConfig
|
.ipSecAddSecurityAssociation(
|
||||||
.getNetdInstance()
|
resourceId,
|
||||||
.ipSecAddSecurityAssociation(
|
c.getMode(),
|
||||||
resourceId,
|
c.getSourceAddress(),
|
||||||
c.getMode(),
|
c.getDestinationAddress(),
|
||||||
direction,
|
(c.getNetwork() != null) ? c.getNetwork().netId : 0,
|
||||||
c.getLocalAddress(),
|
spiRecord.getSpi(),
|
||||||
c.getRemoteAddress(),
|
(auth != null) ? auth.getName() : "",
|
||||||
(c.getNetwork() != null) ? c.getNetwork().getNetworkHandle() : 0,
|
(auth != null) ? auth.getKey() : new byte[] {},
|
||||||
spi,
|
(auth != null) ? auth.getTruncationLengthBits() : 0,
|
||||||
(auth != null) ? auth.getName() : "",
|
(crypt != null) ? crypt.getName() : "",
|
||||||
(auth != null) ? auth.getKey() : new byte[] {},
|
(crypt != null) ? crypt.getKey() : new byte[] {},
|
||||||
(auth != null) ? auth.getTruncationLengthBits() : 0,
|
(crypt != null) ? crypt.getTruncationLengthBits() : 0,
|
||||||
(crypt != null) ? crypt.getName() : "",
|
(authCrypt != null) ? authCrypt.getName() : "",
|
||||||
(crypt != null) ? crypt.getKey() : new byte[] {},
|
(authCrypt != null) ? authCrypt.getKey() : new byte[] {},
|
||||||
(crypt != null) ? crypt.getTruncationLengthBits() : 0,
|
(authCrypt != null) ? authCrypt.getTruncationLengthBits() : 0,
|
||||||
(authCrypt != null) ? authCrypt.getName() : "",
|
encapType,
|
||||||
(authCrypt != null) ? authCrypt.getKey() : new byte[] {},
|
encapLocalPort,
|
||||||
(authCrypt != null) ? authCrypt.getTruncationLengthBits() : 0,
|
encapRemotePort);
|
||||||
encapType,
|
} catch (ServiceSpecificException e) {
|
||||||
encapLocalPort,
|
// FIXME: get the error code and throw is at an IOException from Errno Exception
|
||||||
encapRemotePort);
|
return new IpSecTransformResponse(IpSecManager.Status.RESOURCE_UNAVAILABLE);
|
||||||
} catch (ServiceSpecificException e) {
|
|
||||||
// FIXME: get the error code and throw is at an IOException from Errno Exception
|
|
||||||
return new IpSecTransformResponse(IpSecManager.Status.RESOURCE_UNAVAILABLE);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
// Both SAs were created successfully, time to construct a record and lock it away
|
// Both SAs were created successfully, time to construct a record and lock it away
|
||||||
userRecord.mTransformRecords.put(
|
userRecord.mTransformRecords.put(
|
||||||
resourceId,
|
resourceId,
|
||||||
new RefcountedResource<TransformRecord>(
|
new RefcountedResource<TransformRecord>(
|
||||||
new TransformRecord(resourceId, c, spis, socketRecord),
|
new TransformRecord(resourceId, c, spiRecord, socketRecord),
|
||||||
binder,
|
binder,
|
||||||
dependencies.toArray(new RefcountedResource[dependencies.size()])));
|
dependencies.toArray(new RefcountedResource[dependencies.size()])));
|
||||||
return new IpSecTransformResponse(IpSecManager.Status.OK, resourceId);
|
return new IpSecTransformResponse(IpSecManager.Status.OK, resourceId);
|
||||||
@@ -1217,9 +1197,9 @@ public class IpSecService extends IIpSecService.Stub {
|
|||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public synchronized void applyTransportModeTransform(
|
public synchronized void applyTransportModeTransform(
|
||||||
ParcelFileDescriptor socket, int resourceId) throws RemoteException {
|
ParcelFileDescriptor socket, int direction, int resourceId) throws RemoteException {
|
||||||
UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid());
|
UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid());
|
||||||
|
checkDirection(direction);
|
||||||
// Get transform record; if no transform is found, will throw IllegalArgumentException
|
// Get transform record; if no transform is found, will throw IllegalArgumentException
|
||||||
TransformRecord info = userRecord.mTransformRecords.getResourceOrThrow(resourceId);
|
TransformRecord info = userRecord.mTransformRecords.getResourceOrThrow(resourceId);
|
||||||
|
|
||||||
@@ -1230,17 +1210,15 @@ public class IpSecService extends IIpSecService.Stub {
|
|||||||
|
|
||||||
IpSecConfig c = info.getConfig();
|
IpSecConfig c = info.getConfig();
|
||||||
try {
|
try {
|
||||||
for (int direction : DIRECTIONS) {
|
mSrvConfig
|
||||||
mSrvConfig
|
.getNetdInstance()
|
||||||
.getNetdInstance()
|
.ipSecApplyTransportModeTransform(
|
||||||
.ipSecApplyTransportModeTransform(
|
socket.getFileDescriptor(),
|
||||||
socket.getFileDescriptor(),
|
resourceId,
|
||||||
resourceId,
|
direction,
|
||||||
direction,
|
c.getSourceAddress(),
|
||||||
c.getLocalAddress(),
|
c.getDestinationAddress(),
|
||||||
c.getRemoteAddress(),
|
info.getSpiRecord().getSpi());
|
||||||
info.getSpiRecord(direction).getSpi());
|
|
||||||
}
|
|
||||||
} catch (ServiceSpecificException e) {
|
} catch (ServiceSpecificException e) {
|
||||||
if (e.errorCode == EINVAL) {
|
if (e.errorCode == EINVAL) {
|
||||||
throw new IllegalArgumentException(e.toString());
|
throw new IllegalArgumentException(e.toString());
|
||||||
@@ -1251,14 +1229,14 @@ public class IpSecService extends IIpSecService.Stub {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Remove a transport mode transform from a socket, applying the default (empty) policy. This
|
* Remove transport mode transforms from a socket, applying the default (empty) policy. This
|
||||||
* will ensure that NO IPsec policy is applied to the socket (would be the equivalent of
|
* ensures that NO IPsec policy is applied to the socket (would be the equivalent of applying a
|
||||||
* applying a policy that performs no IPsec). Today the resourceId parameter is passed but not
|
* policy that performs no IPsec). Today the resourceId parameter is passed but not used:
|
||||||
* used: reserved for future improved input validation.
|
* reserved for future improved input validation.
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public synchronized void removeTransportModeTransform(ParcelFileDescriptor socket, int resourceId)
|
public synchronized void removeTransportModeTransforms(
|
||||||
throws RemoteException {
|
ParcelFileDescriptor socket, int resourceId) throws RemoteException {
|
||||||
try {
|
try {
|
||||||
mSrvConfig
|
mSrvConfig
|
||||||
.getNetdInstance()
|
.getNetdInstance()
|
||||||
|
|||||||
Reference in New Issue
Block a user