1 /* 2 * Copyright (C) 2021 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package android.net.vcn.persistablebundleutils; 18 19 import static android.system.OsConstants.AF_INET; 20 import static android.system.OsConstants.AF_INET6; 21 22 import static com.android.internal.annotations.VisibleForTesting.Visibility; 23 24 import android.annotation.NonNull; 25 import android.annotation.Nullable; 26 import android.net.InetAddresses; 27 import android.net.ipsec.ike.ChildSaProposal; 28 import android.net.ipsec.ike.IkeTrafficSelector; 29 import android.net.ipsec.ike.TunnelModeChildSessionParams; 30 import android.net.ipsec.ike.TunnelModeChildSessionParams.ConfigRequestIpv4Address; 31 import android.net.ipsec.ike.TunnelModeChildSessionParams.ConfigRequestIpv4DhcpServer; 32 import android.net.ipsec.ike.TunnelModeChildSessionParams.ConfigRequestIpv4DnsServer; 33 import android.net.ipsec.ike.TunnelModeChildSessionParams.ConfigRequestIpv4Netmask; 34 import android.net.ipsec.ike.TunnelModeChildSessionParams.ConfigRequestIpv6Address; 35 import android.net.ipsec.ike.TunnelModeChildSessionParams.ConfigRequestIpv6DnsServer; 36 import android.net.ipsec.ike.TunnelModeChildSessionParams.TunnelModeChildConfigRequest; 37 import android.os.PersistableBundle; 38 import android.util.Log; 39 40 import com.android.internal.annotations.VisibleForTesting; 41 import com.android.server.vcn.util.PersistableBundleUtils; 42 43 import java.net.Inet4Address; 44 import java.net.Inet6Address; 45 import java.net.InetAddress; 46 import java.util.ArrayList; 47 import java.util.List; 48 import java.util.Objects; 49 50 /** 51 * Provides utility methods to convert TunnelModeChildSessionParams to/from PersistableBundle. 52 * 53 * @hide 54 */ 55 @VisibleForTesting(visibility = Visibility.PRIVATE) 56 public final class TunnelModeChildSessionParamsUtils { 57 private static final String TAG = TunnelModeChildSessionParamsUtils.class.getSimpleName(); 58 59 private static final String INBOUND_TS_KEY = "INBOUND_TS_KEY"; 60 private static final String OUTBOUND_TS_KEY = "OUTBOUND_TS_KEY"; 61 private static final String SA_PROPOSALS_KEY = "SA_PROPOSALS_KEY"; 62 private static final String HARD_LIFETIME_SEC_KEY = "HARD_LIFETIME_SEC_KEY"; 63 private static final String SOFT_LIFETIME_SEC_KEY = "SOFT_LIFETIME_SEC_KEY"; 64 private static final String CONFIG_REQUESTS_KEY = "CONFIG_REQUESTS_KEY"; 65 66 private static class ConfigRequest { 67 private static final int TYPE_IPV4_ADDRESS = 1; 68 private static final int TYPE_IPV6_ADDRESS = 2; 69 private static final int TYPE_IPV4_DNS = 3; 70 private static final int TYPE_IPV6_DNS = 4; 71 private static final int TYPE_IPV4_DHCP = 5; 72 private static final int TYPE_IPV4_NETMASK = 6; 73 74 private static final String TYPE_KEY = "type"; 75 private static final String VALUE_KEY = "address"; 76 private static final String IP6_PREFIX_LEN = "ip6PrefixLen"; 77 78 private static final int PREFIX_LEN_UNUSED = -1; 79 80 public final int type; 81 public final int ip6PrefixLen; 82 83 // Null when it is an empty request 84 @Nullable public final InetAddress address; 85 ConfigRequest(TunnelModeChildConfigRequest config)86 ConfigRequest(TunnelModeChildConfigRequest config) { 87 int prefixLen = PREFIX_LEN_UNUSED; 88 89 if (config instanceof ConfigRequestIpv4Address) { 90 type = TYPE_IPV4_ADDRESS; 91 address = ((ConfigRequestIpv4Address) config).getAddress(); 92 } else if (config instanceof ConfigRequestIpv6Address) { 93 type = TYPE_IPV6_ADDRESS; 94 address = ((ConfigRequestIpv6Address) config).getAddress(); 95 if (address != null) { 96 prefixLen = ((ConfigRequestIpv6Address) config).getPrefixLength(); 97 } 98 } else if (config instanceof ConfigRequestIpv4DnsServer) { 99 type = TYPE_IPV4_DNS; 100 address = null; 101 } else if (config instanceof ConfigRequestIpv6DnsServer) { 102 type = TYPE_IPV6_DNS; 103 address = null; 104 } else if (config instanceof ConfigRequestIpv4DhcpServer) { 105 type = TYPE_IPV4_DHCP; 106 address = null; 107 } else if (config instanceof ConfigRequestIpv4Netmask) { 108 type = TYPE_IPV4_NETMASK; 109 address = null; 110 } else { 111 throw new IllegalStateException("Unknown TunnelModeChildConfigRequest"); 112 } 113 114 ip6PrefixLen = prefixLen; 115 } 116 ConfigRequest(PersistableBundle in)117 ConfigRequest(PersistableBundle in) { 118 Objects.requireNonNull(in, "PersistableBundle was null"); 119 120 type = in.getInt(TYPE_KEY); 121 ip6PrefixLen = in.getInt(IP6_PREFIX_LEN); 122 123 String addressStr = in.getString(VALUE_KEY); 124 if (addressStr == null) { 125 address = null; 126 } else { 127 address = InetAddresses.parseNumericAddress(addressStr); 128 } 129 } 130 131 @NonNull toPersistableBundle()132 public PersistableBundle toPersistableBundle() { 133 final PersistableBundle result = new PersistableBundle(); 134 135 result.putInt(TYPE_KEY, type); 136 result.putInt(IP6_PREFIX_LEN, ip6PrefixLen); 137 138 if (address != null) { 139 result.putString(VALUE_KEY, address.getHostAddress()); 140 } 141 142 return result; 143 } 144 } 145 146 /** Serializes a TunnelModeChildSessionParams to a PersistableBundle. */ 147 @NonNull toPersistableBundle( @onNull TunnelModeChildSessionParams params)148 public static PersistableBundle toPersistableBundle( 149 @NonNull TunnelModeChildSessionParams params) { 150 final PersistableBundle result = new PersistableBundle(); 151 152 final PersistableBundle saProposalBundle = 153 PersistableBundleUtils.fromList( 154 params.getSaProposals(), ChildSaProposalUtils::toPersistableBundle); 155 result.putPersistableBundle(SA_PROPOSALS_KEY, saProposalBundle); 156 157 final PersistableBundle inTsBundle = 158 PersistableBundleUtils.fromList( 159 params.getInboundTrafficSelectors(), 160 IkeTrafficSelectorUtils::toPersistableBundle); 161 result.putPersistableBundle(INBOUND_TS_KEY, inTsBundle); 162 163 final PersistableBundle outTsBundle = 164 PersistableBundleUtils.fromList( 165 params.getOutboundTrafficSelectors(), 166 IkeTrafficSelectorUtils::toPersistableBundle); 167 result.putPersistableBundle(OUTBOUND_TS_KEY, outTsBundle); 168 169 result.putInt(HARD_LIFETIME_SEC_KEY, params.getHardLifetimeSeconds()); 170 result.putInt(SOFT_LIFETIME_SEC_KEY, params.getSoftLifetimeSeconds()); 171 172 final List<ConfigRequest> reqList = new ArrayList<>(); 173 for (TunnelModeChildConfigRequest req : params.getConfigurationRequests()) { 174 reqList.add(new ConfigRequest(req)); 175 } 176 final PersistableBundle configReqListBundle = 177 PersistableBundleUtils.fromList(reqList, ConfigRequest::toPersistableBundle); 178 result.putPersistableBundle(CONFIG_REQUESTS_KEY, configReqListBundle); 179 180 return result; 181 } 182 getTsFromPersistableBundle( PersistableBundle in, String key)183 private static List<IkeTrafficSelector> getTsFromPersistableBundle( 184 PersistableBundle in, String key) { 185 PersistableBundle tsBundle = in.getPersistableBundle(key); 186 Objects.requireNonNull(tsBundle, "Value for key " + key + " was null"); 187 return PersistableBundleUtils.toList( 188 tsBundle, IkeTrafficSelectorUtils::fromPersistableBundle); 189 } 190 191 /** Constructs a TunnelModeChildSessionParams by deserializing a PersistableBundle. */ 192 @NonNull fromPersistableBundle( @onNull PersistableBundle in)193 public static TunnelModeChildSessionParams fromPersistableBundle( 194 @NonNull PersistableBundle in) { 195 Objects.requireNonNull(in, "PersistableBundle was null"); 196 197 final TunnelModeChildSessionParams.Builder builder = 198 new TunnelModeChildSessionParams.Builder(); 199 200 final PersistableBundle proposalBundle = in.getPersistableBundle(SA_PROPOSALS_KEY); 201 Objects.requireNonNull(proposalBundle, "SA proposal was null"); 202 final List<ChildSaProposal> proposals = 203 PersistableBundleUtils.toList( 204 proposalBundle, ChildSaProposalUtils::fromPersistableBundle); 205 for (ChildSaProposal p : proposals) { 206 builder.addSaProposal(p); 207 } 208 209 for (IkeTrafficSelector ts : getTsFromPersistableBundle(in, INBOUND_TS_KEY)) { 210 builder.addInboundTrafficSelectors(ts); 211 } 212 213 for (IkeTrafficSelector ts : getTsFromPersistableBundle(in, OUTBOUND_TS_KEY)) { 214 builder.addOutboundTrafficSelectors(ts); 215 } 216 217 builder.setLifetimeSeconds( 218 in.getInt(HARD_LIFETIME_SEC_KEY), in.getInt(SOFT_LIFETIME_SEC_KEY)); 219 final PersistableBundle configReqListBundle = in.getPersistableBundle(CONFIG_REQUESTS_KEY); 220 Objects.requireNonNull(configReqListBundle, "Config request list was null"); 221 final List<ConfigRequest> reqList = 222 PersistableBundleUtils.toList(configReqListBundle, ConfigRequest::new); 223 224 boolean hasIpv4AddressReq = false; 225 boolean hasIpv4NetmaskReq = false; 226 for (ConfigRequest req : reqList) { 227 switch (req.type) { 228 case ConfigRequest.TYPE_IPV4_ADDRESS: 229 hasIpv4AddressReq = true; 230 if (req.address == null) { 231 builder.addInternalAddressRequest(AF_INET); 232 } else { 233 builder.addInternalAddressRequest((Inet4Address) req.address); 234 } 235 break; 236 case ConfigRequest.TYPE_IPV6_ADDRESS: 237 if (req.address == null) { 238 builder.addInternalAddressRequest(AF_INET6); 239 } else { 240 builder.addInternalAddressRequest( 241 (Inet6Address) req.address, req.ip6PrefixLen); 242 } 243 break; 244 case ConfigRequest.TYPE_IPV4_NETMASK: 245 // Do not need to set netmask because it will be automatically set by the 246 // builder when an IPv4 internal address request is set. 247 hasIpv4NetmaskReq = true; 248 break; 249 case ConfigRequest.TYPE_IPV4_DNS: 250 if (req.address != null) { 251 Log.w(TAG, "Requesting a specific IPv4 DNS server is unsupported"); 252 } 253 builder.addInternalDnsServerRequest(AF_INET); 254 break; 255 case ConfigRequest.TYPE_IPV6_DNS: 256 if (req.address != null) { 257 Log.w(TAG, "Requesting a specific IPv6 DNS server is unsupported"); 258 } 259 builder.addInternalDnsServerRequest(AF_INET6); 260 break; 261 case ConfigRequest.TYPE_IPV4_DHCP: 262 if (req.address != null) { 263 Log.w(TAG, "Requesting a specific IPv4 DHCP server is unsupported"); 264 } 265 builder.addInternalDhcpServerRequest(AF_INET); 266 break; 267 default: 268 throw new IllegalArgumentException( 269 "Unrecognized config request type: " + req.type); 270 } 271 } 272 273 if (hasIpv4AddressReq != hasIpv4NetmaskReq) { 274 Log.w( 275 TAG, 276 String.format( 277 "Expect IPv4 address request and IPv4 netmask request either both" 278 + " exist or both absent, but found hasIpv4AddressReq exists? %b," 279 + " hasIpv4AddressReq exists? %b, ", 280 hasIpv4AddressReq, hasIpv4NetmaskReq)); 281 } 282 283 return builder.build(); 284 } 285 } 286