1 /*
2  * Copyright (C) 2020 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 com.android.server.connectivity;
18 
19 import static android.net.ConnectivityManager.NetworkCallback;
20 import static android.net.ipsec.ike.SaProposal.DH_GROUP_2048_BIT_MODP;
21 import static android.net.ipsec.ike.SaProposal.DH_GROUP_3072_BIT_MODP;
22 import static android.net.ipsec.ike.SaProposal.DH_GROUP_4096_BIT_MODP;
23 import static android.net.ipsec.ike.SaProposal.DH_GROUP_CURVE_25519;
24 import static android.net.ipsec.ike.SaProposal.ENCRYPTION_ALGORITHM_AES_CBC;
25 import static android.net.ipsec.ike.SaProposal.ENCRYPTION_ALGORITHM_AES_CTR;
26 import static android.net.ipsec.ike.SaProposal.ENCRYPTION_ALGORITHM_AES_GCM_12;
27 import static android.net.ipsec.ike.SaProposal.ENCRYPTION_ALGORITHM_AES_GCM_16;
28 import static android.net.ipsec.ike.SaProposal.ENCRYPTION_ALGORITHM_AES_GCM_8;
29 import static android.net.ipsec.ike.SaProposal.ENCRYPTION_ALGORITHM_CHACHA20_POLY1305;
30 import static android.net.ipsec.ike.SaProposal.INTEGRITY_ALGORITHM_AES_CMAC_96;
31 import static android.net.ipsec.ike.SaProposal.INTEGRITY_ALGORITHM_AES_XCBC_96;
32 import static android.net.ipsec.ike.SaProposal.INTEGRITY_ALGORITHM_HMAC_SHA2_256_128;
33 import static android.net.ipsec.ike.SaProposal.INTEGRITY_ALGORITHM_HMAC_SHA2_384_192;
34 import static android.net.ipsec.ike.SaProposal.INTEGRITY_ALGORITHM_HMAC_SHA2_512_256;
35 import static android.net.ipsec.ike.SaProposal.KEY_LEN_AES_128;
36 import static android.net.ipsec.ike.SaProposal.KEY_LEN_AES_192;
37 import static android.net.ipsec.ike.SaProposal.KEY_LEN_AES_256;
38 import static android.net.ipsec.ike.SaProposal.KEY_LEN_UNUSED;
39 import static android.net.ipsec.ike.SaProposal.PSEUDORANDOM_FUNCTION_AES128_CMAC;
40 import static android.net.ipsec.ike.SaProposal.PSEUDORANDOM_FUNCTION_AES128_XCBC;
41 import static android.net.ipsec.ike.SaProposal.PSEUDORANDOM_FUNCTION_HMAC_SHA1;
42 import static android.net.ipsec.ike.SaProposal.PSEUDORANDOM_FUNCTION_SHA2_256;
43 import static android.net.ipsec.ike.SaProposal.PSEUDORANDOM_FUNCTION_SHA2_384;
44 import static android.net.ipsec.ike.SaProposal.PSEUDORANDOM_FUNCTION_SHA2_512;
45 
46 import android.annotation.NonNull;
47 import android.content.Context;
48 import android.net.Ikev2VpnProfile;
49 import android.net.InetAddresses;
50 import android.net.IpPrefix;
51 import android.net.IpSecAlgorithm;
52 import android.net.IpSecTransform;
53 import android.net.LinkProperties;
54 import android.net.Network;
55 import android.net.NetworkCapabilities;
56 import android.net.RouteInfo;
57 import android.net.eap.EapSessionConfig;
58 import android.net.ipsec.ike.ChildSaProposal;
59 import android.net.ipsec.ike.ChildSessionCallback;
60 import android.net.ipsec.ike.ChildSessionConfiguration;
61 import android.net.ipsec.ike.ChildSessionParams;
62 import android.net.ipsec.ike.IkeFqdnIdentification;
63 import android.net.ipsec.ike.IkeIdentification;
64 import android.net.ipsec.ike.IkeIpv4AddrIdentification;
65 import android.net.ipsec.ike.IkeIpv6AddrIdentification;
66 import android.net.ipsec.ike.IkeKeyIdIdentification;
67 import android.net.ipsec.ike.IkeRfc822AddrIdentification;
68 import android.net.ipsec.ike.IkeSaProposal;
69 import android.net.ipsec.ike.IkeSessionCallback;
70 import android.net.ipsec.ike.IkeSessionConfiguration;
71 import android.net.ipsec.ike.IkeSessionConnectionInfo;
72 import android.net.ipsec.ike.IkeSessionParams;
73 import android.net.ipsec.ike.IkeTrafficSelector;
74 import android.net.ipsec.ike.TunnelModeChildSessionParams;
75 import android.net.ipsec.ike.exceptions.IkeException;
76 import android.net.ipsec.ike.exceptions.IkeProtocolException;
77 import android.system.OsConstants;
78 import android.util.Log;
79 
80 import com.android.internal.net.VpnProfile;
81 import com.android.internal.util.HexDump;
82 import com.android.net.module.util.IpRange;
83 
84 import java.net.Inet4Address;
85 import java.net.Inet6Address;
86 import java.net.InetAddress;
87 import java.util.ArrayList;
88 import java.util.Arrays;
89 import java.util.Collection;
90 import java.util.HashSet;
91 import java.util.List;
92 import java.util.concurrent.Executor;
93 
94 /**
95  * Utility class to build and convert IKEv2/IPsec parameters.
96  *
97  * @hide
98  */
99 public class VpnIkev2Utils {
100     private static final String TAG = VpnIkev2Utils.class.getSimpleName();
101 
makeIkeSessionParamsBuilder( @onNull Context context, @NonNull Ikev2VpnProfile profile, @NonNull Network network)102     static IkeSessionParams.Builder makeIkeSessionParamsBuilder(
103             @NonNull Context context, @NonNull Ikev2VpnProfile profile, @NonNull Network network) {
104         final IkeIdentification localId = parseIkeIdentification(profile.getUserIdentity());
105         final IkeIdentification remoteId = parseIkeIdentification(profile.getServerAddr());
106 
107         final IkeSessionParams.Builder ikeOptionsBuilder =
108                 new IkeSessionParams.Builder(context)
109                         .setServerHostname(profile.getServerAddr())
110                         .setNetwork(network)
111                         .addIkeOption(IkeSessionParams.IKE_OPTION_MOBIKE)
112                         .setLocalIdentification(localId)
113                         .setRemoteIdentification(remoteId);
114         setIkeAuth(profile, ikeOptionsBuilder);
115 
116         for (final IkeSaProposal ikeProposal : getIkeSaProposals()) {
117             ikeOptionsBuilder.addSaProposal(ikeProposal);
118         }
119 
120         return ikeOptionsBuilder;
121     }
122 
buildChildSessionParams(List<String> allowedAlgorithms)123     static ChildSessionParams buildChildSessionParams(List<String> allowedAlgorithms) {
124         final TunnelModeChildSessionParams.Builder childOptionsBuilder =
125                 new TunnelModeChildSessionParams.Builder();
126 
127         for (final ChildSaProposal childProposal : getChildSaProposals(allowedAlgorithms)) {
128             childOptionsBuilder.addSaProposal(childProposal);
129         }
130 
131         childOptionsBuilder.addInternalAddressRequest(OsConstants.AF_INET);
132         childOptionsBuilder.addInternalAddressRequest(OsConstants.AF_INET6);
133         childOptionsBuilder.addInternalDnsServerRequest(OsConstants.AF_INET);
134         childOptionsBuilder.addInternalDnsServerRequest(OsConstants.AF_INET6);
135 
136         return childOptionsBuilder.build();
137     }
138 
setIkeAuth( @onNull Ikev2VpnProfile profile, @NonNull IkeSessionParams.Builder builder)139     private static void setIkeAuth(
140             @NonNull Ikev2VpnProfile profile, @NonNull IkeSessionParams.Builder builder) {
141         switch (profile.getType()) {
142             case VpnProfile.TYPE_IKEV2_IPSEC_USER_PASS:
143                 final EapSessionConfig eapConfig =
144                         new EapSessionConfig.Builder()
145                                 .setEapMsChapV2Config(profile.getUsername(), profile.getPassword())
146                                 .build();
147                 builder.setAuthEap(profile.getServerRootCaCert(), eapConfig);
148                 break;
149             case VpnProfile.TYPE_IKEV2_IPSEC_PSK:
150                 builder.setAuthPsk(profile.getPresharedKey());
151                 break;
152             case VpnProfile.TYPE_IKEV2_IPSEC_RSA:
153                 builder.setAuthDigitalSignature(
154                         profile.getServerRootCaCert(),
155                         profile.getUserCert(),
156                         profile.getRsaPrivateKey());
157                 break;
158             default:
159                 throw new IllegalArgumentException("Unknown auth method set");
160         }
161     }
162 
getIkeSaProposals()163     private static List<IkeSaProposal> getIkeSaProposals() {
164         // TODO: Add ability to filter this when IKEv2 API is made Public API
165         final List<IkeSaProposal> proposals = new ArrayList<>();
166 
167         final IkeSaProposal.Builder normalModeBuilder = new IkeSaProposal.Builder();
168 
169         // Add normal mode encryption algorithms
170         normalModeBuilder.addEncryptionAlgorithm(ENCRYPTION_ALGORITHM_AES_CTR, KEY_LEN_AES_256);
171         normalModeBuilder.addEncryptionAlgorithm(ENCRYPTION_ALGORITHM_AES_CBC, KEY_LEN_AES_256);
172         normalModeBuilder.addEncryptionAlgorithm(ENCRYPTION_ALGORITHM_AES_CTR, KEY_LEN_AES_192);
173         normalModeBuilder.addEncryptionAlgorithm(ENCRYPTION_ALGORITHM_AES_CBC, KEY_LEN_AES_192);
174         normalModeBuilder.addEncryptionAlgorithm(ENCRYPTION_ALGORITHM_AES_CTR, KEY_LEN_AES_128);
175         normalModeBuilder.addEncryptionAlgorithm(ENCRYPTION_ALGORITHM_AES_CBC, KEY_LEN_AES_128);
176 
177         // Authentication/Integrity Algorithms
178         normalModeBuilder.addIntegrityAlgorithm(INTEGRITY_ALGORITHM_HMAC_SHA2_512_256);
179         normalModeBuilder.addIntegrityAlgorithm(INTEGRITY_ALGORITHM_HMAC_SHA2_384_192);
180         normalModeBuilder.addIntegrityAlgorithm(INTEGRITY_ALGORITHM_HMAC_SHA2_256_128);
181         normalModeBuilder.addIntegrityAlgorithm(INTEGRITY_ALGORITHM_AES_XCBC_96);
182         normalModeBuilder.addIntegrityAlgorithm(INTEGRITY_ALGORITHM_AES_CMAC_96);
183 
184         // Add AEAD options
185         final IkeSaProposal.Builder aeadBuilder = new IkeSaProposal.Builder();
186         aeadBuilder.addEncryptionAlgorithm(ENCRYPTION_ALGORITHM_CHACHA20_POLY1305, KEY_LEN_UNUSED);
187         aeadBuilder.addEncryptionAlgorithm(ENCRYPTION_ALGORITHM_AES_GCM_16, KEY_LEN_AES_256);
188         aeadBuilder.addEncryptionAlgorithm(ENCRYPTION_ALGORITHM_AES_GCM_12, KEY_LEN_AES_256);
189         aeadBuilder.addEncryptionAlgorithm(ENCRYPTION_ALGORITHM_AES_GCM_8, KEY_LEN_AES_256);
190         aeadBuilder.addEncryptionAlgorithm(ENCRYPTION_ALGORITHM_AES_GCM_16, KEY_LEN_AES_192);
191         aeadBuilder.addEncryptionAlgorithm(ENCRYPTION_ALGORITHM_AES_GCM_12, KEY_LEN_AES_192);
192         aeadBuilder.addEncryptionAlgorithm(ENCRYPTION_ALGORITHM_AES_GCM_8, KEY_LEN_AES_192);
193         aeadBuilder.addEncryptionAlgorithm(ENCRYPTION_ALGORITHM_AES_GCM_16, KEY_LEN_AES_128);
194         aeadBuilder.addEncryptionAlgorithm(ENCRYPTION_ALGORITHM_AES_GCM_12, KEY_LEN_AES_128);
195         aeadBuilder.addEncryptionAlgorithm(ENCRYPTION_ALGORITHM_AES_GCM_8, KEY_LEN_AES_128);
196 
197         // Add dh, prf for both builders
198         for (final IkeSaProposal.Builder builder : Arrays.asList(normalModeBuilder, aeadBuilder)) {
199             builder.addDhGroup(DH_GROUP_4096_BIT_MODP);
200 
201             // Curve25519 has the same security strength as MODP 3072 and cost less bytes
202             builder.addDhGroup(DH_GROUP_CURVE_25519);
203 
204             builder.addDhGroup(DH_GROUP_3072_BIT_MODP);
205             builder.addDhGroup(DH_GROUP_2048_BIT_MODP);
206             builder.addPseudorandomFunction(PSEUDORANDOM_FUNCTION_SHA2_512);
207             builder.addPseudorandomFunction(PSEUDORANDOM_FUNCTION_SHA2_384);
208             builder.addPseudorandomFunction(PSEUDORANDOM_FUNCTION_SHA2_256);
209             builder.addPseudorandomFunction(PSEUDORANDOM_FUNCTION_AES128_XCBC);
210             builder.addPseudorandomFunction(PSEUDORANDOM_FUNCTION_AES128_CMAC);
211             builder.addPseudorandomFunction(PSEUDORANDOM_FUNCTION_HMAC_SHA1);
212         }
213 
214         proposals.add(normalModeBuilder.build());
215         proposals.add(aeadBuilder.build());
216         return proposals;
217     }
218 
219     /** Builds a child SA proposal based on the allowed IPsec algorithms */
getChildSaProposals(List<String> allowedAlgorithms)220     private static List<ChildSaProposal> getChildSaProposals(List<String> allowedAlgorithms) {
221         final List<ChildSaProposal> proposals = new ArrayList<>();
222 
223         final List<Integer> aesKeyLenOptions =
224                 Arrays.asList(KEY_LEN_AES_256, KEY_LEN_AES_192, KEY_LEN_AES_128);
225 
226         // Add non-AEAD options
227         if (Ikev2VpnProfile.hasNormalModeAlgorithms(allowedAlgorithms)) {
228             final ChildSaProposal.Builder normalModeBuilder = new ChildSaProposal.Builder();
229 
230             // Encryption Algorithms:
231             // AES-CBC and AES_CTR are currently the only supported encryption algorithms.
232             for (int len : aesKeyLenOptions) {
233                 if (allowedAlgorithms.contains(IpSecAlgorithm.CRYPT_AES_CTR)) {
234                     normalModeBuilder.addEncryptionAlgorithm(ENCRYPTION_ALGORITHM_AES_CTR, len);
235                 }
236                 if (allowedAlgorithms.contains(IpSecAlgorithm.CRYPT_AES_CBC)) {
237                     normalModeBuilder.addEncryptionAlgorithm(ENCRYPTION_ALGORITHM_AES_CBC, len);
238                 }
239             }
240 
241             // Authentication/Integrity Algorithms:
242             // Guaranteed by Ikev2VpnProfile constructor to contain at least one of these.
243             if (allowedAlgorithms.contains(IpSecAlgorithm.AUTH_HMAC_SHA512)) {
244                 normalModeBuilder.addIntegrityAlgorithm(INTEGRITY_ALGORITHM_HMAC_SHA2_512_256);
245             }
246             if (allowedAlgorithms.contains(IpSecAlgorithm.AUTH_HMAC_SHA384)) {
247                 normalModeBuilder.addIntegrityAlgorithm(INTEGRITY_ALGORITHM_HMAC_SHA2_384_192);
248             }
249             if (allowedAlgorithms.contains(IpSecAlgorithm.AUTH_HMAC_SHA256)) {
250                 normalModeBuilder.addIntegrityAlgorithm(INTEGRITY_ALGORITHM_HMAC_SHA2_256_128);
251             }
252             if (allowedAlgorithms.contains(IpSecAlgorithm.AUTH_AES_XCBC)) {
253                 normalModeBuilder.addIntegrityAlgorithm(INTEGRITY_ALGORITHM_AES_XCBC_96);
254             }
255             if (allowedAlgorithms.contains(IpSecAlgorithm.AUTH_AES_CMAC)) {
256                 normalModeBuilder.addIntegrityAlgorithm(INTEGRITY_ALGORITHM_AES_CMAC_96);
257             }
258 
259             ChildSaProposal proposal = normalModeBuilder.build();
260             if (proposal.getIntegrityAlgorithms().isEmpty()) {
261                 // Should be impossible; Verified in Ikev2VpnProfile.
262                 Log.wtf(TAG, "Missing integrity algorithm when buildling Child SA proposal");
263             } else {
264                 proposals.add(normalModeBuilder.build());
265             }
266         }
267 
268         // Add AEAD options
269         if (Ikev2VpnProfile.hasAeadAlgorithms(allowedAlgorithms)) {
270             final ChildSaProposal.Builder aeadBuilder = new ChildSaProposal.Builder();
271 
272             if (allowedAlgorithms.contains(IpSecAlgorithm.AUTH_CRYPT_CHACHA20_POLY1305)) {
273                 aeadBuilder.addEncryptionAlgorithm(
274                         ENCRYPTION_ALGORITHM_CHACHA20_POLY1305, KEY_LEN_UNUSED);
275             }
276             if (allowedAlgorithms.contains(IpSecAlgorithm.AUTH_CRYPT_AES_GCM)) {
277                 aeadBuilder.addEncryptionAlgorithm(
278                         ENCRYPTION_ALGORITHM_AES_GCM_16, KEY_LEN_AES_256);
279                 aeadBuilder.addEncryptionAlgorithm(
280                         ENCRYPTION_ALGORITHM_AES_GCM_12, KEY_LEN_AES_256);
281                 aeadBuilder.addEncryptionAlgorithm(ENCRYPTION_ALGORITHM_AES_GCM_8, KEY_LEN_AES_256);
282                 aeadBuilder.addEncryptionAlgorithm(
283                         ENCRYPTION_ALGORITHM_AES_GCM_16, KEY_LEN_AES_192);
284                 aeadBuilder.addEncryptionAlgorithm(
285                         ENCRYPTION_ALGORITHM_AES_GCM_12, KEY_LEN_AES_192);
286                 aeadBuilder.addEncryptionAlgorithm(ENCRYPTION_ALGORITHM_AES_GCM_8, KEY_LEN_AES_192);
287                 aeadBuilder.addEncryptionAlgorithm(
288                         ENCRYPTION_ALGORITHM_AES_GCM_16, KEY_LEN_AES_128);
289                 aeadBuilder.addEncryptionAlgorithm(
290                         ENCRYPTION_ALGORITHM_AES_GCM_12, KEY_LEN_AES_128);
291                 aeadBuilder.addEncryptionAlgorithm(ENCRYPTION_ALGORITHM_AES_GCM_8, KEY_LEN_AES_128);
292             }
293 
294             proposals.add(aeadBuilder.build());
295         }
296 
297         return proposals;
298     }
299 
300     static class IkeSessionCallbackImpl implements IkeSessionCallback {
301         private final String mTag;
302         private final Vpn.IkeV2VpnRunnerCallback mCallback;
303         private final int mToken;
304 
IkeSessionCallbackImpl(String tag, Vpn.IkeV2VpnRunnerCallback callback, int token)305         IkeSessionCallbackImpl(String tag, Vpn.IkeV2VpnRunnerCallback callback, int token) {
306             mTag = tag;
307             mCallback = callback;
308             mToken = token;
309         }
310 
311         @Override
onOpened(@onNull IkeSessionConfiguration ikeSessionConfig)312         public void onOpened(@NonNull IkeSessionConfiguration ikeSessionConfig) {
313             Log.d(mTag, "IkeOpened for token " + mToken);
314             mCallback.onIkeOpened(mToken, ikeSessionConfig);
315         }
316 
317         @Override
onClosed()318         public void onClosed() {
319             Log.d(mTag, "IkeClosed for token " + mToken);
320             mCallback.onSessionLost(mToken, null); // Server requested session closure. Retry?
321         }
322 
323         @Override
onClosedExceptionally(@onNull IkeException exception)324         public void onClosedExceptionally(@NonNull IkeException exception) {
325             Log.d(mTag, "IkeClosedExceptionally for token " + mToken, exception);
326             mCallback.onSessionLost(mToken, exception);
327         }
328 
329         @Override
onError(@onNull IkeProtocolException exception)330         public void onError(@NonNull IkeProtocolException exception) {
331             Log.d(mTag, "IkeError for token " + mToken, exception);
332             // Non-fatal, log and continue.
333         }
334 
335         @Override
onIkeSessionConnectionInfoChanged( @onNull IkeSessionConnectionInfo connectionInfo)336         public void onIkeSessionConnectionInfoChanged(
337                 @NonNull IkeSessionConnectionInfo connectionInfo) {
338             Log.d(mTag, "onIkeSessionConnectionInfoChanged for token " + mToken);
339             mCallback.onIkeConnectionInfoChanged(mToken, connectionInfo);
340         }
341     }
342 
343     static class ChildSessionCallbackImpl implements ChildSessionCallback {
344         private final String mTag;
345         private final Vpn.IkeV2VpnRunnerCallback mCallback;
346         private final int mToken;
347 
ChildSessionCallbackImpl(String tag, Vpn.IkeV2VpnRunnerCallback callback, int token)348         ChildSessionCallbackImpl(String tag, Vpn.IkeV2VpnRunnerCallback callback, int token) {
349             mTag = tag;
350             mCallback = callback;
351             mToken = token;
352         }
353 
354         @Override
onOpened(@onNull ChildSessionConfiguration childConfig)355         public void onOpened(@NonNull ChildSessionConfiguration childConfig) {
356             Log.d(mTag, "ChildOpened for token " + mToken);
357             mCallback.onChildOpened(mToken, childConfig);
358         }
359 
360         @Override
onClosed()361         public void onClosed() {
362             Log.d(mTag, "ChildClosed for token " + mToken);
363             mCallback.onSessionLost(mToken, null);
364         }
365 
366         @Override
onClosedExceptionally(@onNull IkeException exception)367         public void onClosedExceptionally(@NonNull IkeException exception) {
368             Log.d(mTag, "ChildClosedExceptionally for token " + mToken, exception);
369             mCallback.onSessionLost(mToken, exception);
370         }
371 
372         @Override
onIpSecTransformCreated(@onNull IpSecTransform transform, int direction)373         public void onIpSecTransformCreated(@NonNull IpSecTransform transform, int direction) {
374             Log.d(mTag, "ChildTransformCreated; Direction: " + direction + "; token " + mToken);
375             mCallback.onChildTransformCreated(mToken, transform, direction);
376         }
377 
378         @Override
onIpSecTransformDeleted(@onNull IpSecTransform transform, int direction)379         public void onIpSecTransformDeleted(@NonNull IpSecTransform transform, int direction) {
380             // Nothing to be done; no references to the IpSecTransform are held by the
381             // Ikev2VpnRunner (or this callback class), and this transform will be closed by the
382             // IKE library.
383             Log.d(mTag, "ChildTransformDeleted; Direction: " + direction + "; for token " + mToken);
384         }
385 
386         @Override
onIpSecTransformsMigrated( @onNull IpSecTransform inIpSecTransform, @NonNull IpSecTransform outIpSecTransform)387         public void onIpSecTransformsMigrated(
388                 @NonNull IpSecTransform inIpSecTransform,
389                 @NonNull IpSecTransform outIpSecTransform) {
390             Log.d(mTag, "ChildTransformsMigrated; token " + mToken);
391             mCallback.onChildMigrated(mToken, inIpSecTransform, outIpSecTransform);
392         }
393     }
394 
395     static class Ikev2VpnNetworkCallback extends NetworkCallback {
396         private final String mTag;
397         private final Vpn.IkeV2VpnRunnerCallback mCallback;
398         private final Executor mExecutor;
399 
Ikev2VpnNetworkCallback(String tag, Vpn.IkeV2VpnRunnerCallback callback, Executor executor)400         Ikev2VpnNetworkCallback(String tag, Vpn.IkeV2VpnRunnerCallback callback,
401                 Executor executor) {
402             mTag = tag;
403             mCallback = callback;
404             mExecutor = executor;
405         }
406 
407         @Override
onAvailable(@onNull Network network)408         public void onAvailable(@NonNull Network network) {
409             Log.d(mTag, "onAvailable called for network: " + network);
410             mExecutor.execute(() -> mCallback.onDefaultNetworkChanged(network));
411         }
412 
413         @Override
onCapabilitiesChanged(@onNull Network network, @NonNull NetworkCapabilities networkCapabilities)414         public void onCapabilitiesChanged(@NonNull Network network,
415                 @NonNull NetworkCapabilities networkCapabilities) {
416             Log.d(mTag, "NC changed for net " + network + " : " + networkCapabilities);
417             mExecutor.execute(
418                     () -> mCallback.onDefaultNetworkCapabilitiesChanged(networkCapabilities));
419         }
420 
421         @Override
onLinkPropertiesChanged(@onNull Network network, @NonNull LinkProperties linkProperties)422         public void onLinkPropertiesChanged(@NonNull Network network,
423                 @NonNull LinkProperties linkProperties) {
424             Log.d(mTag, "LP changed for net " + network + " : " + linkProperties);
425             mExecutor.execute(
426                     () -> mCallback.onDefaultNetworkLinkPropertiesChanged(linkProperties));
427         }
428 
429         @Override
onLost(@onNull Network network)430         public void onLost(@NonNull Network network) {
431             Log.d(mTag, "onLost called for network: " + network);
432             mExecutor.execute(() -> mCallback.onDefaultNetworkLost(network));
433         }
434     }
435 
436     /**
437      * Identity parsing logic using similar logic to open source implementations of IKEv2
438      *
439      * <p>This method does NOT support using type-prefixes (eg 'fqdn:' or 'keyid'), or ASN.1 encoded
440      * identities.
441      */
parseIkeIdentification(@onNull String identityStr)442     private static IkeIdentification parseIkeIdentification(@NonNull String identityStr) {
443         // TODO: Add identity formatting to public API javadocs.
444         if (identityStr.contains("@")) {
445             if (identityStr.startsWith("@#")) {
446                 // KEY_ID
447                 final String hexStr = identityStr.substring(2);
448                 return new IkeKeyIdIdentification(HexDump.hexStringToByteArray(hexStr));
449             } else if (identityStr.startsWith("@@")) {
450                 // RFC822 (USER_FQDN)
451                 return new IkeRfc822AddrIdentification(identityStr.substring(2));
452             } else if (identityStr.startsWith("@")) {
453                 // FQDN
454                 return new IkeFqdnIdentification(identityStr.substring(1));
455             } else {
456                 // RFC822 (USER_FQDN)
457                 return new IkeRfc822AddrIdentification(identityStr);
458             }
459         } else if (InetAddresses.isNumericAddress(identityStr)) {
460             final InetAddress addr = InetAddresses.parseNumericAddress(identityStr);
461             if (addr instanceof Inet4Address) {
462                 // IPv4
463                 return new IkeIpv4AddrIdentification((Inet4Address) addr);
464             } else if (addr instanceof Inet6Address) {
465                 // IPv6
466                 return new IkeIpv6AddrIdentification((Inet6Address) addr);
467             } else {
468                 throw new IllegalArgumentException("IP version not supported");
469             }
470         } else {
471             if (identityStr.contains(":")) {
472                 // KEY_ID
473                 return new IkeKeyIdIdentification(identityStr.getBytes());
474             } else {
475                 // FQDN
476                 return new IkeFqdnIdentification(identityStr);
477             }
478         }
479     }
480 
getRoutesFromTrafficSelectors( List<IkeTrafficSelector> trafficSelectors)481     static Collection<RouteInfo> getRoutesFromTrafficSelectors(
482             List<IkeTrafficSelector> trafficSelectors) {
483         final HashSet<RouteInfo> routes = new HashSet<>();
484 
485         for (final IkeTrafficSelector selector : trafficSelectors) {
486             for (final IpPrefix prefix :
487                     new IpRange(selector.startingAddress, selector.endingAddress).asIpPrefixes()) {
488                 routes.add(new RouteInfo(prefix, null /*gateway*/, null /*iface*/,
489                         RouteInfo.RTN_UNICAST));
490             }
491         }
492 
493         return routes;
494     }
495 }
496