| /* |
| * Copyright (C) 2021 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.vcn.persistablebundleutils; |
| |
| import static android.system.OsConstants.AF_INET; |
| import static android.system.OsConstants.AF_INET6; |
| |
| import static com.android.internal.annotations.VisibleForTesting.Visibility; |
| |
| import android.annotation.NonNull; |
| import android.annotation.Nullable; |
| import android.net.InetAddresses; |
| import android.net.ipsec.ike.ChildSaProposal; |
| import android.net.ipsec.ike.IkeTrafficSelector; |
| import android.net.ipsec.ike.TunnelModeChildSessionParams; |
| import android.net.ipsec.ike.TunnelModeChildSessionParams.ConfigRequestIpv4Address; |
| import android.net.ipsec.ike.TunnelModeChildSessionParams.ConfigRequestIpv4DhcpServer; |
| import android.net.ipsec.ike.TunnelModeChildSessionParams.ConfigRequestIpv4DnsServer; |
| import android.net.ipsec.ike.TunnelModeChildSessionParams.ConfigRequestIpv4Netmask; |
| import android.net.ipsec.ike.TunnelModeChildSessionParams.ConfigRequestIpv6Address; |
| import android.net.ipsec.ike.TunnelModeChildSessionParams.ConfigRequestIpv6DnsServer; |
| import android.net.ipsec.ike.TunnelModeChildSessionParams.TunnelModeChildConfigRequest; |
| import android.os.PersistableBundle; |
| import android.util.Log; |
| |
| import com.android.internal.annotations.VisibleForTesting; |
| import com.android.server.vcn.util.PersistableBundleUtils; |
| |
| import java.net.Inet4Address; |
| import java.net.Inet6Address; |
| import java.net.InetAddress; |
| import java.util.ArrayList; |
| import java.util.List; |
| import java.util.Objects; |
| |
| /** |
| * Provides utility methods to convert TunnelModeChildSessionParams to/from PersistableBundle. |
| * |
| * @hide |
| */ |
| @VisibleForTesting(visibility = Visibility.PRIVATE) |
| public final class TunnelModeChildSessionParamsUtils { |
| private static final String TAG = TunnelModeChildSessionParamsUtils.class.getSimpleName(); |
| |
| private static final String INBOUND_TS_KEY = "INBOUND_TS_KEY"; |
| private static final String OUTBOUND_TS_KEY = "OUTBOUND_TS_KEY"; |
| private static final String SA_PROPOSALS_KEY = "SA_PROPOSALS_KEY"; |
| private static final String HARD_LIFETIME_SEC_KEY = "HARD_LIFETIME_SEC_KEY"; |
| private static final String SOFT_LIFETIME_SEC_KEY = "SOFT_LIFETIME_SEC_KEY"; |
| private static final String CONFIG_REQUESTS_KEY = "CONFIG_REQUESTS_KEY"; |
| |
| private static class ConfigRequest { |
| private static final int TYPE_IPV4_ADDRESS = 1; |
| private static final int TYPE_IPV6_ADDRESS = 2; |
| private static final int TYPE_IPV4_DNS = 3; |
| private static final int TYPE_IPV6_DNS = 4; |
| private static final int TYPE_IPV4_DHCP = 5; |
| private static final int TYPE_IPV4_NETMASK = 6; |
| |
| private static final String TYPE_KEY = "type"; |
| private static final String VALUE_KEY = "address"; |
| private static final String IP6_PREFIX_LEN = "ip6PrefixLen"; |
| |
| private static final int PREFIX_LEN_UNUSED = -1; |
| |
| public final int type; |
| public final int ip6PrefixLen; |
| |
| // Null when it is an empty request |
| @Nullable public final InetAddress address; |
| |
| ConfigRequest(TunnelModeChildConfigRequest config) { |
| int prefixLen = PREFIX_LEN_UNUSED; |
| |
| if (config instanceof ConfigRequestIpv4Address) { |
| type = TYPE_IPV4_ADDRESS; |
| address = ((ConfigRequestIpv4Address) config).getAddress(); |
| } else if (config instanceof ConfigRequestIpv6Address) { |
| type = TYPE_IPV6_ADDRESS; |
| address = ((ConfigRequestIpv6Address) config).getAddress(); |
| if (address != null) { |
| prefixLen = ((ConfigRequestIpv6Address) config).getPrefixLength(); |
| } |
| } else if (config instanceof ConfigRequestIpv4DnsServer) { |
| type = TYPE_IPV4_DNS; |
| address = null; |
| } else if (config instanceof ConfigRequestIpv6DnsServer) { |
| type = TYPE_IPV6_DNS; |
| address = null; |
| } else if (config instanceof ConfigRequestIpv4DhcpServer) { |
| type = TYPE_IPV4_DHCP; |
| address = null; |
| } else if (config instanceof ConfigRequestIpv4Netmask) { |
| type = TYPE_IPV4_NETMASK; |
| address = null; |
| } else { |
| throw new IllegalStateException("Unknown TunnelModeChildConfigRequest"); |
| } |
| |
| ip6PrefixLen = prefixLen; |
| } |
| |
| ConfigRequest(PersistableBundle in) { |
| Objects.requireNonNull(in, "PersistableBundle was null"); |
| |
| type = in.getInt(TYPE_KEY); |
| ip6PrefixLen = in.getInt(IP6_PREFIX_LEN); |
| |
| String addressStr = in.getString(VALUE_KEY); |
| if (addressStr == null) { |
| address = null; |
| } else { |
| address = InetAddresses.parseNumericAddress(addressStr); |
| } |
| } |
| |
| @NonNull |
| public PersistableBundle toPersistableBundle() { |
| final PersistableBundle result = new PersistableBundle(); |
| |
| result.putInt(TYPE_KEY, type); |
| result.putInt(IP6_PREFIX_LEN, ip6PrefixLen); |
| |
| if (address != null) { |
| result.putString(VALUE_KEY, address.getHostAddress()); |
| } |
| |
| return result; |
| } |
| } |
| |
| /** Serializes a TunnelModeChildSessionParams to a PersistableBundle. */ |
| @NonNull |
| public static PersistableBundle toPersistableBundle( |
| @NonNull TunnelModeChildSessionParams params) { |
| final PersistableBundle result = new PersistableBundle(); |
| |
| final PersistableBundle saProposalBundle = |
| PersistableBundleUtils.fromList( |
| params.getSaProposals(), ChildSaProposalUtils::toPersistableBundle); |
| result.putPersistableBundle(SA_PROPOSALS_KEY, saProposalBundle); |
| |
| final PersistableBundle inTsBundle = |
| PersistableBundleUtils.fromList( |
| params.getInboundTrafficSelectors(), |
| IkeTrafficSelectorUtils::toPersistableBundle); |
| result.putPersistableBundle(INBOUND_TS_KEY, inTsBundle); |
| |
| final PersistableBundle outTsBundle = |
| PersistableBundleUtils.fromList( |
| params.getOutboundTrafficSelectors(), |
| IkeTrafficSelectorUtils::toPersistableBundle); |
| result.putPersistableBundle(OUTBOUND_TS_KEY, outTsBundle); |
| |
| result.putInt(HARD_LIFETIME_SEC_KEY, params.getHardLifetimeSeconds()); |
| result.putInt(SOFT_LIFETIME_SEC_KEY, params.getSoftLifetimeSeconds()); |
| |
| final List<ConfigRequest> reqList = new ArrayList<>(); |
| for (TunnelModeChildConfigRequest req : params.getConfigurationRequests()) { |
| reqList.add(new ConfigRequest(req)); |
| } |
| final PersistableBundle configReqListBundle = |
| PersistableBundleUtils.fromList(reqList, ConfigRequest::toPersistableBundle); |
| result.putPersistableBundle(CONFIG_REQUESTS_KEY, configReqListBundle); |
| |
| return result; |
| } |
| |
| private static List<IkeTrafficSelector> getTsFromPersistableBundle( |
| PersistableBundle in, String key) { |
| PersistableBundle tsBundle = in.getPersistableBundle(key); |
| Objects.requireNonNull(tsBundle, "Value for key " + key + " was null"); |
| return PersistableBundleUtils.toList( |
| tsBundle, IkeTrafficSelectorUtils::fromPersistableBundle); |
| } |
| |
| /** Constructs a TunnelModeChildSessionParams by deserializing a PersistableBundle. */ |
| @NonNull |
| public static TunnelModeChildSessionParams fromPersistableBundle( |
| @NonNull PersistableBundle in) { |
| Objects.requireNonNull(in, "PersistableBundle was null"); |
| |
| final TunnelModeChildSessionParams.Builder builder = |
| new TunnelModeChildSessionParams.Builder(); |
| |
| final PersistableBundle proposalBundle = in.getPersistableBundle(SA_PROPOSALS_KEY); |
| Objects.requireNonNull(proposalBundle, "SA proposal was null"); |
| final List<ChildSaProposal> proposals = |
| PersistableBundleUtils.toList( |
| proposalBundle, ChildSaProposalUtils::fromPersistableBundle); |
| for (ChildSaProposal p : proposals) { |
| builder.addSaProposal(p); |
| } |
| |
| for (IkeTrafficSelector ts : getTsFromPersistableBundle(in, INBOUND_TS_KEY)) { |
| builder.addInboundTrafficSelectors(ts); |
| } |
| |
| for (IkeTrafficSelector ts : getTsFromPersistableBundle(in, OUTBOUND_TS_KEY)) { |
| builder.addOutboundTrafficSelectors(ts); |
| } |
| |
| builder.setLifetimeSeconds( |
| in.getInt(HARD_LIFETIME_SEC_KEY), in.getInt(SOFT_LIFETIME_SEC_KEY)); |
| final PersistableBundle configReqListBundle = in.getPersistableBundle(CONFIG_REQUESTS_KEY); |
| Objects.requireNonNull(configReqListBundle, "Config request list was null"); |
| final List<ConfigRequest> reqList = |
| PersistableBundleUtils.toList(configReqListBundle, ConfigRequest::new); |
| |
| boolean hasIpv4AddressReq = false; |
| boolean hasIpv4NetmaskReq = false; |
| for (ConfigRequest req : reqList) { |
| switch (req.type) { |
| case ConfigRequest.TYPE_IPV4_ADDRESS: |
| hasIpv4AddressReq = true; |
| if (req.address == null) { |
| builder.addInternalAddressRequest(AF_INET); |
| } else { |
| builder.addInternalAddressRequest((Inet4Address) req.address); |
| } |
| break; |
| case ConfigRequest.TYPE_IPV6_ADDRESS: |
| if (req.address == null) { |
| builder.addInternalAddressRequest(AF_INET6); |
| } else { |
| builder.addInternalAddressRequest( |
| (Inet6Address) req.address, req.ip6PrefixLen); |
| } |
| break; |
| case ConfigRequest.TYPE_IPV4_NETMASK: |
| // Do not need to set netmask because it will be automatically set by the |
| // builder when an IPv4 internal address request is set. |
| hasIpv4NetmaskReq = true; |
| break; |
| case ConfigRequest.TYPE_IPV4_DNS: |
| if (req.address != null) { |
| Log.w(TAG, "Requesting a specific IPv4 DNS server is unsupported"); |
| } |
| builder.addInternalDnsServerRequest(AF_INET); |
| break; |
| case ConfigRequest.TYPE_IPV6_DNS: |
| if (req.address != null) { |
| Log.w(TAG, "Requesting a specific IPv6 DNS server is unsupported"); |
| } |
| builder.addInternalDnsServerRequest(AF_INET6); |
| break; |
| case ConfigRequest.TYPE_IPV4_DHCP: |
| if (req.address != null) { |
| Log.w(TAG, "Requesting a specific IPv4 DHCP server is unsupported"); |
| } |
| builder.addInternalDhcpServerRequest(AF_INET); |
| break; |
| default: |
| throw new IllegalArgumentException( |
| "Unrecognized config request type: " + req.type); |
| } |
| } |
| |
| if (hasIpv4AddressReq != hasIpv4NetmaskReq) { |
| Log.w( |
| TAG, |
| String.format( |
| "Expect IPv4 address request and IPv4 netmask request either both" |
| + " exist or both absent, but found hasIpv4AddressReq exists? %b," |
| + " hasIpv4AddressReq exists? %b, ", |
| hasIpv4AddressReq, hasIpv4NetmaskReq)); |
| } |
| |
| return builder.build(); |
| } |
| } |