blob: 72315635bd552abc5df9e1f0cf39a0eca56d0d27 [file] [log] [blame]
/*
* Copyright (C) 2019 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.ipsec.ike;
import static com.android.internal.net.ipsec.ike.message.IkeConfigPayload.CONFIG_ATTR_APPLICATION_VERSION;
import static com.android.internal.net.ipsec.ike.message.IkeConfigPayload.CONFIG_ATTR_IP4_PCSCF;
import static com.android.internal.net.ipsec.ike.message.IkeConfigPayload.CONFIG_ATTR_IP6_PCSCF;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SuppressLint;
import android.annotation.SystemApi;
import android.net.eap.EapInfo;
import com.android.internal.net.ipsec.ike.message.IkeConfigPayload;
import com.android.internal.net.ipsec.ike.message.IkeConfigPayload.ConfigAttribute;
import com.android.internal.net.ipsec.ike.message.IkeConfigPayload.ConfigAttributeAppVersion;
import com.android.internal.net.ipsec.ike.message.IkeConfigPayload.ConfigAttributeIpv4Pcscf;
import com.android.internal.net.ipsec.ike.message.IkeConfigPayload.ConfigAttributeIpv6Pcscf;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.net.InetAddress;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Objects;
import java.util.Set;
/**
* IkeSessionConfiguration represents the negotiated configuration for a {@link IkeSession}.
*
* <p>Configurations include remote application version and enabled IKE extensions.
*/
public final class IkeSessionConfiguration {
/** @hide */
@Retention(RetentionPolicy.SOURCE)
@IntDef({EXTENSION_TYPE_FRAGMENTATION, EXTENSION_TYPE_MOBIKE})
public @interface ExtensionType {}
/** IKE Message Fragmentation */
public static final int EXTENSION_TYPE_FRAGMENTATION = 1;
/** IKEv2 Mobility and Multihoming Protocol */
public static final int EXTENSION_TYPE_MOBIKE = 2;
private static final int VALID_EXTENSION_MIN = EXTENSION_TYPE_FRAGMENTATION;
private static final int VALID_EXTENSION_MAX = EXTENSION_TYPE_MOBIKE;
private final String mRemoteApplicationVersion;
private final IkeSessionConnectionInfo mIkeConnInfo;
private final List<InetAddress> mPcscfServers = new ArrayList<>();
private final List<byte[]> mRemoteVendorIds = new ArrayList<>();
private final Set<Integer> mEnabledExtensions = new HashSet<>();
private final EapInfo mEapInfo;
/**
* Construct an instance of {@link IkeSessionConfiguration}.
*
* <p>IkeSessionConfigurations may contain negotiated configuration information that is included
* in a Configure(Reply) Payload. Thus the input configPayload should always be a
* Configure(Reply), and never be a Configure(Request).
*
* @hide
*/
public IkeSessionConfiguration(
IkeSessionConnectionInfo ikeConnInfo,
IkeConfigPayload configPayload,
List<byte[]> remoteVendorIds,
List<Integer> enabledExtensions,
EapInfo eapInfo) {
mIkeConnInfo = ikeConnInfo;
mRemoteVendorIds.addAll(remoteVendorIds);
mEnabledExtensions.addAll(enabledExtensions);
mEapInfo = eapInfo;
String appVersion = "";
if (configPayload != null) {
if (configPayload.configType != IkeConfigPayload.CONFIG_TYPE_REPLY) {
throw new IllegalArgumentException(
"Cannot build IkeSessionConfiguration with configuration type: "
+ configPayload.configType);
}
for (ConfigAttribute attr : configPayload.recognizedAttributeList) {
if (attr.isEmptyValue()) continue;
switch (attr.attributeType) {
case CONFIG_ATTR_APPLICATION_VERSION:
ConfigAttributeAppVersion appVersionAttr = (ConfigAttributeAppVersion) attr;
appVersion = appVersionAttr.applicationVersion;
break;
case CONFIG_ATTR_IP4_PCSCF:
ConfigAttributeIpv4Pcscf ip4Pcscf = (ConfigAttributeIpv4Pcscf) attr;
mPcscfServers.add(ip4Pcscf.getAddress());
break;
case CONFIG_ATTR_IP6_PCSCF:
ConfigAttributeIpv6Pcscf ip6Pcscf = (ConfigAttributeIpv6Pcscf) attr;
mPcscfServers.add(ip6Pcscf.getAddress());
break;
default:
// Not relevant to IKE session
}
}
}
mRemoteApplicationVersion = appVersion;
validateOrThrow();
}
/**
* Construct an instance of {@link IkeSessionConfiguration}.
*
* @hide
*/
private IkeSessionConfiguration(
IkeSessionConnectionInfo ikeConnInfo,
List<InetAddress> pcscfServers,
List<byte[]> remoteVendorIds,
Set<Integer> enabledExtensions,
String remoteApplicationVersion,
EapInfo eapInfo) {
mIkeConnInfo = ikeConnInfo;
mPcscfServers.addAll(pcscfServers);
mRemoteVendorIds.addAll(remoteVendorIds);
mEnabledExtensions.addAll(enabledExtensions);
mRemoteApplicationVersion = remoteApplicationVersion;
mEapInfo = eapInfo;
validateOrThrow();
}
private void validateOrThrow() {
String errMsg = " was null";
Objects.requireNonNull(mIkeConnInfo, "ikeConnInfo" + errMsg);
Objects.requireNonNull(mPcscfServers, "pcscfServers" + errMsg);
Objects.requireNonNull(mRemoteVendorIds, "remoteVendorIds" + errMsg);
Objects.requireNonNull(mRemoteApplicationVersion, "remoteApplicationVersion" + errMsg);
Objects.requireNonNull(mRemoteVendorIds, "remoteVendorIds" + errMsg);
}
/**
* Gets remote (server) version information.
*
* @return application version of the remote server, or an empty string if the remote server did
* not provide the application version.
*/
@NonNull
public String getRemoteApplicationVersion() {
return mRemoteApplicationVersion;
}
/**
* Returns remote vendor IDs received during IKE Session setup.
*
* <p>According to the IKEv2 specification (RFC 7296), a vendor ID may indicate the sender is
* capable of accepting certain extensions to the protocol, or it may simply identify the
* implementation as an aid in debugging.
*
* @return the vendor IDs of the remote server, or an empty list if no vendor ID is received
* during IKE Session setup.
*/
@NonNull
public List<byte[]> getRemoteVendorIds() {
return Collections.unmodifiableList(mRemoteVendorIds);
}
/**
* Checks if an IKE extension is enabled.
*
* <p>An IKE extension is enabled when both sides can support it. This negotiation always
* happens in IKE initial exchanges (IKE INIT and IKE AUTH).
*
* @param extensionType the extension type.
* @return {@code true} if this extension is enabled.
*/
public boolean isIkeExtensionEnabled(@ExtensionType int extensionType) {
return mEnabledExtensions.contains(extensionType);
}
/**
* Returns the assigned P_CSCF servers.
*
* @return the assigned P_CSCF servers, or an empty list when no servers are assigned by the
* remote IKE server.
* @hide
*/
@SystemApi
@NonNull
public List<InetAddress> getPcscfServers() {
return Collections.unmodifiableList(mPcscfServers);
}
/**
* Returns the connection information.
*
* @return the IKE Session connection information.
*/
@NonNull
public IkeSessionConnectionInfo getIkeSessionConnectionInfo() {
return mIkeConnInfo;
}
/**
* Retrieves the EAP information.
*
* @return the EAP information provided by the server during EAP authentication (e.g. next
* re-authentication ID), or null if the server did not provide any information that will be
* useful after the authentication.
*/
@Nullable
public EapInfo getEapInfo() {
return mEapInfo;
}
/**
* This class can be used to incrementally construct a {@link IkeSessionConfiguration}.
*
* <p>Except for testing, IKE library users normally do not instantiate {@link
* IkeSessionConfiguration} themselves but instead get a reference via {@link
* IkeSessionCallback}
*/
public static final class Builder {
private final IkeSessionConnectionInfo mIkeConnInfo;
private final List<InetAddress> mPcscfServers = new ArrayList<>();
private final List<byte[]> mRemoteVendorIds = new ArrayList<>();
private final Set<Integer> mEnabledExtensions = new HashSet<>();
private String mRemoteApplicationVersion = "";
private EapInfo mEapInfo;
/**
* Constructs a Builder.
*
* @param ikeConnInfo the connection information
*/
public Builder(@NonNull IkeSessionConnectionInfo ikeConnInfo) {
Objects.requireNonNull(ikeConnInfo, "ikeConnInfo was null");
mIkeConnInfo = ikeConnInfo;
}
/**
* Adds an assigned P_CSCF server for the {@link IkeSessionConfiguration} being built.
*
* @param pcscfServer an assigned P_CSCF server
* @return Builder this, to facilitate chaining
* @hide
*/
@SystemApi
@NonNull
public Builder addPcscfServer(@NonNull InetAddress pcscfServer) {
Objects.requireNonNull(pcscfServer, "pcscfServer was null");
mPcscfServers.add(pcscfServer);
return this;
}
/**
* Clear all P_CSCF servers from the {@link IkeSessionConfiguration} being built.
*
* @return Builder this, to facilitate chaining
* @hide
*/
@SystemApi
@NonNull
public Builder clearPcscfServers() {
mPcscfServers.clear();
return this;
}
/**
* Adds a remote vendor ID for the {@link IkeSessionConfiguration} being built.
*
* @param remoteVendorId a remote vendor ID
* @return Builder this, to facilitate chaining
*/
@NonNull
public Builder addRemoteVendorId(@NonNull byte[] remoteVendorId) {
Objects.requireNonNull(remoteVendorId, "remoteVendorId was null");
mRemoteVendorIds.add(remoteVendorId);
return this;
}
/**
* Clears all remote vendor IDs from the {@link IkeSessionConfiguration} being built.
*
* @return Builder this, to facilitate chaining
*/
@NonNull
public Builder clearRemoteVendorIds() {
mRemoteVendorIds.clear();
return this;
}
/**
* Sets the remote application version for the {@link IkeSessionConfiguration} being built.
*
* @param remoteApplicationVersion the remote application version. Defaults to an empty
* string.
* @return Builder this, to facilitate chaining
*/
@NonNull
public Builder setRemoteApplicationVersion(@NonNull String remoteApplicationVersion) {
Objects.requireNonNull(remoteApplicationVersion, "remoteApplicationVersion was null");
mRemoteApplicationVersion = remoteApplicationVersion;
return this;
}
/**
* Clears the remote application version from the {@link IkeSessionConfiguration} being
* built.
*
* @return Builder this, to facilitate chaining
*/
@NonNull
public Builder clearRemoteApplicationVersion() {
mRemoteApplicationVersion = "";
return this;
}
private static void validateExtensionOrThrow(@ExtensionType int extensionType) {
if (extensionType >= VALID_EXTENSION_MIN && extensionType <= VALID_EXTENSION_MAX) {
return;
}
throw new IllegalArgumentException("Invalid extension type: " + extensionType);
}
/**
* Marks an IKE extension as enabled for the {@link IkeSessionConfiguration} being built.
*
* @param extensionType the enabled extension
* @return Builder this, to facilitate chaining
*/
// MissingGetterMatchingBuilder: Use #isIkeExtensionEnabled instead of #getIkeExtension
// because #isIkeExtensionEnabled allows callers to check the presence of an IKE extension
// more easily
@SuppressLint("MissingGetterMatchingBuilder")
@NonNull
public Builder addIkeExtension(@ExtensionType int extensionType) {
validateExtensionOrThrow(extensionType);
mEnabledExtensions.add(extensionType);
return this;
}
/**
* Clear all enabled IKE extensions from the {@link IkeSessionConfiguration} being built.
*
* @return Builder this, to facilitate chaining
*/
@NonNull
public Builder clearIkeExtensions() {
mEnabledExtensions.clear();
return this;
}
/**
* Sets EapInfo for the {@link IkeSessionConfiguration} being built.
*
* @return Builder this, to facilitate chaining
*/
@NonNull
public Builder setEapInfo(@Nullable EapInfo eapInfo) {
mEapInfo = eapInfo;
return this;
}
/** Constructs an {@link IkeSessionConfiguration} instance. */
@NonNull
public IkeSessionConfiguration build() {
return new IkeSessionConfiguration(
mIkeConnInfo,
mPcscfServers,
mRemoteVendorIds,
mEnabledExtensions,
mRemoteApplicationVersion,
mEapInfo);
}
}
}