/*
* 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_INTERNAL_IP4_ADDRESS;
import static com.android.internal.net.ipsec.ike.message.IkeConfigPayload.CONFIG_ATTR_INTERNAL_IP4_DHCP;
import static com.android.internal.net.ipsec.ike.message.IkeConfigPayload.CONFIG_ATTR_INTERNAL_IP4_DNS;
import static com.android.internal.net.ipsec.ike.message.IkeConfigPayload.CONFIG_ATTR_INTERNAL_IP4_NETMASK;
import static com.android.internal.net.ipsec.ike.message.IkeConfigPayload.CONFIG_ATTR_INTERNAL_IP4_SUBNET;
import static com.android.internal.net.ipsec.ike.message.IkeConfigPayload.CONFIG_ATTR_INTERNAL_IP6_ADDRESS;
import static com.android.internal.net.ipsec.ike.message.IkeConfigPayload.CONFIG_ATTR_INTERNAL_IP6_DNS;
import static com.android.internal.net.ipsec.ike.message.IkeConfigPayload.CONFIG_ATTR_INTERNAL_IP6_SUBNET;
import android.annotation.NonNull;
import android.annotation.SystemApi;
import android.net.IpPrefix;
import android.net.LinkAddress;
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.ConfigAttributeIpv4Address;
import com.android.internal.net.ipsec.ike.message.IkeConfigPayload.ConfigAttributeIpv4Dhcp;
import com.android.internal.net.ipsec.ike.message.IkeConfigPayload.ConfigAttributeIpv4Dns;
import com.android.internal.net.ipsec.ike.message.IkeConfigPayload.ConfigAttributeIpv4Netmask;
import com.android.internal.net.ipsec.ike.message.IkeConfigPayload.ConfigAttributeIpv4Subnet;
import com.android.internal.net.ipsec.ike.message.IkeConfigPayload.ConfigAttributeIpv6Address;
import com.android.internal.net.ipsec.ike.message.IkeConfigPayload.ConfigAttributeIpv6Dns;
import com.android.internal.net.ipsec.ike.message.IkeConfigPayload.ConfigAttributeIpv6Subnet;
import java.net.InetAddress;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
/**
* ChildSessionConfiguration represents the negotiated configuration for a Child Session.
*
*
Configurations include traffic selectors and internal network information.
*/
public final class ChildSessionConfiguration {
private static final int IPv4_DEFAULT_PREFIX_LEN = 32;
private final List mInboundTs = new ArrayList<>();
private final List mOutboundTs = new ArrayList<>();
private final List mInternalAddressList = new ArrayList<>();
private final List mInternalDnsAddressList = new ArrayList<>();
private final List mSubnetAddressList = new ArrayList<>();
private final List mInternalDhcpAddressList = new ArrayList<>();
/**
* Construct an instance of {@link ChildSessionConfiguration}.
*
* ChildSessionConfiguration 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 ChildSessionConfiguration(
List inTs,
List outTs,
IkeConfigPayload configPayload) {
this(inTs, outTs);
if (configPayload.configType != IkeConfigPayload.CONFIG_TYPE_REPLY) {
throw new IllegalArgumentException(
"Cannot build ChildSessionConfiguration with configuration type: "
+ configPayload.configType);
}
// It is validated in IkeConfigPayload that a config reply only has at most one non-empty
// netmask and netmask exists only when IPv4 internal address exists.
ConfigAttributeIpv4Netmask netmaskAttr = null;
for (ConfigAttribute att : configPayload.recognizedAttributeList) {
if (att.attributeType == CONFIG_ATTR_INTERNAL_IP4_NETMASK && !att.isEmptyValue()) {
netmaskAttr = (ConfigAttributeIpv4Netmask) att;
}
}
for (ConfigAttribute att : configPayload.recognizedAttributeList) {
if (att.isEmptyValue()) continue;
switch (att.attributeType) {
case CONFIG_ATTR_INTERNAL_IP4_ADDRESS:
ConfigAttributeIpv4Address addressAttr = (ConfigAttributeIpv4Address) att;
if (netmaskAttr != null) {
mInternalAddressList.add(
new LinkAddress(addressAttr.address, netmaskAttr.getPrefixLen()));
} else {
mInternalAddressList.add(
new LinkAddress(addressAttr.address, IPv4_DEFAULT_PREFIX_LEN));
}
break;
case CONFIG_ATTR_INTERNAL_IP4_NETMASK:
// No action.
break;
case CONFIG_ATTR_INTERNAL_IP6_ADDRESS:
mInternalAddressList.add(((ConfigAttributeIpv6Address) att).linkAddress);
break;
case CONFIG_ATTR_INTERNAL_IP4_DNS:
mInternalDnsAddressList.add(((ConfigAttributeIpv4Dns) att).address);
break;
case CONFIG_ATTR_INTERNAL_IP6_DNS:
mInternalDnsAddressList.add(((ConfigAttributeIpv6Dns) att).address);
break;
case CONFIG_ATTR_INTERNAL_IP4_SUBNET:
ConfigAttributeIpv4Subnet ipv4SubnetAttr = (ConfigAttributeIpv4Subnet) att;
mSubnetAddressList.add(
new IpPrefix(
ipv4SubnetAttr.linkAddress.getAddress(),
ipv4SubnetAttr.linkAddress.getPrefixLength()));
break;
case CONFIG_ATTR_INTERNAL_IP6_SUBNET:
ConfigAttributeIpv6Subnet ipV6SubnetAttr = (ConfigAttributeIpv6Subnet) att;
mSubnetAddressList.add(
new IpPrefix(
ipV6SubnetAttr.linkAddress.getAddress(),
ipV6SubnetAttr.linkAddress.getPrefixLength()));
break;
case CONFIG_ATTR_INTERNAL_IP4_DHCP:
mInternalDhcpAddressList.add(((ConfigAttributeIpv4Dhcp) att).address);
break;
default:
// Not relevant to child session
}
}
}
/**
* Construct an instance of {@link ChildSessionConfiguration}.
*
* @hide
*/
public ChildSessionConfiguration(
List inTs, List outTs) {
mInboundTs.addAll(inTs);
mOutboundTs.addAll(outTs);
}
/**
* Construct an instance of {@link ChildSessionConfiguration}.
*
* @hide
*/
private ChildSessionConfiguration(
List inTs,
List outTs,
List internalAddresses,
List internalSubnets,
List internalDnsServers,
List internalDhcpServers) {
this(inTs, outTs);
mInternalAddressList.addAll(internalAddresses);
mSubnetAddressList.addAll(internalSubnets);
mInternalDnsAddressList.addAll(internalDnsServers);
mInternalDhcpAddressList.addAll(internalDhcpServers);
}
/**
* Returns the negotiated inbound traffic selectors.
*
* Only inbound traffic within the range is acceptable to the Child Session.
*
*
The Android platform does not support port-based routing. Port ranges of traffic selectors
* are only informational.
*
* @return the inbound traffic selectors.
*/
@NonNull
public List getInboundTrafficSelectors() {
return mInboundTs;
}
/**
* Returns the negotiated outbound traffic selectors.
*
* Only outbound traffic within the range is acceptable to the Child Session.
*
*
The Android platform does not support port-based routing. Port ranges of traffic selectors
* are only informational.
*
* @return the outbound traffic selectors.
*/
@NonNull
public List getOutboundTrafficSelectors() {
return mOutboundTs;
}
/**
* Returns the assigned internal addresses.
*
* @return the assigned internal addresses, or an empty list when no addresses are assigned by
* the remote IKE server (e.g. for a non-tunnel mode Child Session).
* @hide
*/
@SystemApi
@NonNull
public List getInternalAddresses() {
return Collections.unmodifiableList(mInternalAddressList);
}
/**
* Returns the internal subnets protected by the IKE server.
*
* @return the internal subnets, or an empty list when no information of protected subnets is
* provided by the IKE server (e.g. for a non-tunnel mode Child Session).
* @hide
*/
@SystemApi
@NonNull
public List getInternalSubnets() {
return Collections.unmodifiableList(mSubnetAddressList);
}
/**
* Returns the internal DNS server addresses.
*
* @return the internal DNS server addresses, or an empty list when no DNS server is provided by
* the IKE server (e.g. for a non-tunnel mode Child Session).
* @hide
*/
@SystemApi
@NonNull
public List getInternalDnsServers() {
return Collections.unmodifiableList(mInternalDnsAddressList);
}
/**
* Returns the internal DHCP server addresses.
*
* @return the internal DHCP server addresses, or an empty list when no DHCP server is provided
* by the IKE server (e.g. for a non-tunnel mode Child Session).
* @hide
*/
@SystemApi
@NonNull
public List getInternalDhcpServers() {
return Collections.unmodifiableList(mInternalDhcpAddressList);
}
/**
* This class can be used to incrementally construct a {@link ChildSessionConfiguration}.
*
* Except for testing, IKE library users normally do not instantiate {@link
* ChildSessionConfiguration} themselves but instead get a reference via {@link
* ChildSessionCallback}
*/
public static final class Builder {
private final List mInboundTs = new ArrayList<>();
private final List mOutboundTs = new ArrayList<>();
private final List mInternalAddressList = new ArrayList<>();
private final List mSubnetAddressList = new ArrayList<>();
private final List mInternalDnsAddressList = new ArrayList<>();
private final List mInternalDhcpAddressList = new ArrayList<>();
/**
* Constructs a Builder.
*
* @param inTs the negotiated inbound traffic selectors
* @param outTs the negotiated outbound traffic selectors
*/
public Builder(
@NonNull List inTs, @NonNull List outTs) {
Objects.requireNonNull(inTs, "inTs was null");
Objects.requireNonNull(outTs, "outTs was null");
if (inTs.isEmpty() || outTs.isEmpty()) {
throw new IllegalArgumentException("inTs or outTs is empty.");
}
mInboundTs.addAll(inTs);
mOutboundTs.addAll(outTs);
}
/**
* Adds an assigned internal address for the {@link ChildSessionConfiguration} being built.
*
* @param address an assigned internal addresses
* @return Builder this, to facilitate chaining
* @hide
*/
@SystemApi
@NonNull
public Builder addInternalAddress(@NonNull LinkAddress address) {
Objects.requireNonNull(address, "address was null");
mInternalAddressList.add(address);
return this;
}
/**
* Clears all assigned internal addresses from the {@link ChildSessionConfiguration} being
* built.
*
* @return Builder this, to facilitate chaining
* @hide
*/
@SystemApi
@NonNull
public Builder clearInternalAddresses() {
mInternalAddressList.clear();
return this;
}
/**
* Adds an assigned internal subnet for the {@link ChildSessionConfiguration} being built.
*
* @param subnet an assigned internal subnet
* @return Builder this, to facilitate chaining
* @hide
*/
@SystemApi
@NonNull
public Builder addInternalSubnet(@NonNull IpPrefix subnet) {
Objects.requireNonNull(subnet, "subnet was null");
mSubnetAddressList.add(subnet);
return this;
}
/**
* Clears all assigned internal subnets from the {@link ChildSessionConfiguration} being
* built.
*
* @return Builder this, to facilitate chaining
* @hide
*/
@SystemApi
@NonNull
public Builder clearInternalSubnets() {
mSubnetAddressList.clear();
return this;
}
/**
* Adds an assigned internal DNS server for the {@link ChildSessionConfiguration} being
* built.
*
* @param dnsServer an assigned internal DNS server
* @return Builder this, to facilitate chaining
* @hide
*/
@SystemApi
@NonNull
public Builder addInternalDnsServer(@NonNull InetAddress dnsServer) {
Objects.requireNonNull(dnsServer, "dnsServer was null");
mInternalDnsAddressList.add(dnsServer);
return this;
}
/**
* Clears all assigned internal DNS servers from the {@link ChildSessionConfiguration} being
* built.
*
* @return Builder this, to facilitate chaining
* @hide
*/
@SystemApi
@NonNull
public Builder clearInternalDnsServers() {
mInternalDnsAddressList.clear();
return this;
}
/**
* Adds an assigned internal DHCP server for the {@link ChildSessionConfiguration} being
* built.
*
* @param dhcpServer an assigned internal DHCP server
* @return Builder this, to facilitate chaining
* @hide
*/
@SystemApi
@NonNull
public Builder addInternalDhcpServer(@NonNull InetAddress dhcpServer) {
Objects.requireNonNull(dhcpServer, "dhcpServer was null");
mInternalDhcpAddressList.add(dhcpServer);
return this;
}
/**
* Clears all assigned internal DHCP servers for the {@link ChildSessionConfiguration} being
* built.
*
* @return Builder this, to facilitate chaining
* @hide
*/
@SystemApi
@NonNull
public Builder clearInternalDhcpServers() {
mInternalDhcpAddressList.clear();
return this;
}
/** Constructs an {@link ChildSessionConfiguration} instance. */
@NonNull
public ChildSessionConfiguration build() {
return new ChildSessionConfiguration(
mInboundTs,
mOutboundTs,
mInternalAddressList,
mSubnetAddressList,
mInternalDnsAddressList,
mInternalDhcpAddressList);
}
}
}