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