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