1 /*
2  * Copyright (C) 2017 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 package android.net;
17 
18 import static android.annotation.SystemApi.Client.MODULE_LIBRARIES;
19 
20 import android.annotation.IntDef;
21 import android.annotation.NonNull;
22 import android.annotation.RequiresFeature;
23 import android.annotation.RequiresPermission;
24 import android.annotation.SystemApi;
25 import android.annotation.SystemService;
26 import android.annotation.TestApi;
27 import android.content.Context;
28 import android.content.pm.PackageManager;
29 import android.os.Binder;
30 import android.os.IBinder;
31 import android.os.ParcelFileDescriptor;
32 import android.os.RemoteException;
33 import android.os.ServiceSpecificException;
34 import android.system.ErrnoException;
35 import android.system.OsConstants;
36 import android.util.AndroidException;
37 import android.util.Log;
38 
39 import com.android.internal.annotations.VisibleForTesting;
40 import com.android.modules.utils.build.SdkLevel;
41 
42 import dalvik.system.CloseGuard;
43 
44 import java.io.FileDescriptor;
45 import java.io.IOException;
46 import java.lang.annotation.Retention;
47 import java.lang.annotation.RetentionPolicy;
48 import java.net.DatagramSocket;
49 import java.net.InetAddress;
50 import java.net.Socket;
51 import java.util.Objects;
52 
53 /**
54  * This class contains methods for managing IPsec sessions. Once configured, the kernel will apply
55  * confidentiality (encryption) and integrity (authentication) to IP traffic.
56  *
57  * <p>Note that not all aspects of IPsec are permitted by this API. Applications may create
58  * transport mode security associations and apply them to individual sockets. Applications looking
59  * to create an IPsec VPN should use {@link VpnManager} and {@link Ikev2VpnProfile}.
60  *
61  * @see <a href="https://tools.ietf.org/html/rfc4301">RFC 4301, Security Architecture for the
62  *     Internet Protocol</a>
63  */
64 @SystemService(Context.IPSEC_SERVICE)
65 public class IpSecManager {
66     private static final String TAG = "IpSecManager";
67 
68     // TODO : remove this class when udc-mainline-prod is abandoned and android.net.flags.Flags is
69     // available here
70     /** @hide */
71     public static class Flags {
72         static final String IPSEC_TRANSFORM_STATE = "com.android.net.flags.ipsec_transform_state";
73     }
74 
75     /**
76      * Feature flag to declare the kernel support of updating IPsec SAs.
77      *
78      * <p>Feature for {@link #getSystemAvailableFeatures} and {@link #hasSystemFeature}: The device
79      * has the requisite kernel support for migrating IPsec tunnels to new source/destination
80      * addresses.
81      *
82      * <p>This feature implies that the device supports XFRM Migration (CONFIG_XFRM_MIGRATE) and has
83      * the kernel fixes to allow XFRM Migration correctly
84      *
85      * @see android.content.pm.PackageManager#FEATURE_IPSEC_TUNNEL_MIGRATION
86      * @hide
87      */
88     // Redefine this flag here so that IPsec code shipped in a mainline module can build on old
89     // platforms before FEATURE_IPSEC_TUNNEL_MIGRATION API is released.
90     public static final String FEATURE_IPSEC_TUNNEL_MIGRATION =
91             "android.software.ipsec_tunnel_migration";
92 
93     /**
94      * Used when applying a transform to direct traffic through an {@link IpSecTransform}
95      * towards the host.
96      *
97      * <p>See {@link #applyTransportModeTransform(Socket, int, IpSecTransform)}.
98      */
99     public static final int DIRECTION_IN = 0;
100 
101     /**
102      * Used when applying a transform to direct traffic through an {@link IpSecTransform}
103      * away from the host.
104      *
105      * <p>See {@link #applyTransportModeTransform(Socket, int, IpSecTransform)}.
106      */
107     public static final int DIRECTION_OUT = 1;
108 
109     /**
110      * Used when applying a transform to direct traffic through an {@link IpSecTransform} for
111      * forwarding between interfaces.
112      *
113      * <p>See {@link #applyTransportModeTransform(Socket, int, IpSecTransform)}.
114      *
115      * @hide
116      */
117     @SystemApi(client = MODULE_LIBRARIES)
118     public static final int DIRECTION_FWD = 2;
119 
120     /** @hide */
121     @IntDef(value = {DIRECTION_IN, DIRECTION_OUT})
122     @Retention(RetentionPolicy.SOURCE)
123     public @interface PolicyDirection {}
124 
125     /**
126      * The Security Parameter Index (SPI) 0 indicates an unknown or invalid index.
127      *
128      * <p>No IPsec packet may contain an SPI of 0.
129      *
130      * @hide
131      */
132     @TestApi public static final int INVALID_SECURITY_PARAMETER_INDEX = 0;
133 
134     /** @hide */
135     public interface Status {
136         int OK = 0;
137         int RESOURCE_UNAVAILABLE = 1;
138         int SPI_UNAVAILABLE = 2;
139     }
140 
141     /** @hide */
142     public static final int INVALID_RESOURCE_ID = -1;
143 
144     /**
145      * Thrown to indicate that a requested SPI is in use.
146      *
147      * <p>The combination of remote {@code InetAddress} and SPI must be unique across all apps on
148      * one device. If this error is encountered, a new SPI is required before a transform may be
149      * created. This error can be avoided by calling {@link
150      * IpSecManager#allocateSecurityParameterIndex}.
151      */
152     public static final class SpiUnavailableException extends AndroidException {
153         private final int mSpi;
154 
155         /**
156          * Construct an exception indicating that a transform with the given SPI is already in use
157          * or otherwise unavailable.
158          *
159          * @param msg description indicating the colliding SPI
160          * @param spi the SPI that could not be used due to a collision
161          */
SpiUnavailableException(String msg, int spi)162         SpiUnavailableException(String msg, int spi) {
163             super(msg + " (spi: " + spi + ")");
164             mSpi = spi;
165         }
166 
167         /** Get the SPI that caused a collision. */
getSpi()168         public int getSpi() {
169             return mSpi;
170         }
171     }
172 
173     /**
174      * Thrown to indicate that an IPsec resource is unavailable.
175      *
176      * <p>This could apply to resources such as sockets, {@link SecurityParameterIndex}, {@link
177      * IpSecTransform}, or other system resources. If this exception is thrown, users should release
178      * allocated objects of the type requested.
179      */
180     public static final class ResourceUnavailableException extends AndroidException {
181 
ResourceUnavailableException(String msg)182         ResourceUnavailableException(String msg) {
183             super(msg);
184         }
185     }
186 
187     private final Context mContext;
188     private final IIpSecService mService;
189 
190     /**
191      * This class represents a reserved SPI.
192      *
193      * <p>Objects of this type are used to track reserved security parameter indices. They can be
194      * obtained by calling {@link IpSecManager#allocateSecurityParameterIndex} and must be released
195      * by calling {@link #close()} when they are no longer needed.
196      */
197     public static final class SecurityParameterIndex implements AutoCloseable {
198         private final IIpSecService mService;
199         private final InetAddress mDestinationAddress;
200         private final CloseGuard mCloseGuard = CloseGuard.get();
201         private int mSpi = INVALID_SECURITY_PARAMETER_INDEX;
202         private int mResourceId = INVALID_RESOURCE_ID;
203 
204         /** Get the underlying SPI held by this object. */
getSpi()205         public int getSpi() {
206             return mSpi;
207         }
208 
209         /**
210          * Release an SPI that was previously reserved.
211          *
212          * <p>Release an SPI for use by other users in the system. If a SecurityParameterIndex is
213          * applied to an IpSecTransform, it will become unusable for future transforms but should
214          * still be closed to ensure system resources are released.
215          */
216         @Override
close()217         public void close() {
218             try {
219                 mService.releaseSecurityParameterIndex(mResourceId);
220             } catch (RemoteException e) {
221                 throw e.rethrowFromSystemServer();
222             } catch (Exception e) {
223                 // On close we swallow all random exceptions since failure to close is not
224                 // actionable by the user.
225                 Log.e(TAG, "Failed to close " + this + ", Exception=" + e);
226             } finally {
227                 mResourceId = INVALID_RESOURCE_ID;
228                 mCloseGuard.close();
229             }
230         }
231 
232         /** Check that the SPI was closed properly. */
233         @Override
finalize()234         protected void finalize() throws Throwable {
235             if (mCloseGuard != null) {
236                 mCloseGuard.warnIfOpen();
237             }
238 
239             close();
240         }
241 
SecurityParameterIndex( @onNull IIpSecService service, InetAddress destinationAddress, int spi)242         private SecurityParameterIndex(
243                 @NonNull IIpSecService service, InetAddress destinationAddress, int spi)
244                 throws ResourceUnavailableException, SpiUnavailableException {
245             mService = service;
246             mDestinationAddress = destinationAddress;
247             try {
248                 IpSecSpiResponse result =
249                         mService.allocateSecurityParameterIndex(
250                                 destinationAddress.getHostAddress(), spi, new Binder());
251 
252                 if (result == null) {
253                     throw new NullPointerException("Received null response from IpSecService");
254                 }
255 
256                 int status = result.status;
257                 switch (status) {
258                     case Status.OK:
259                         break;
260                     case Status.RESOURCE_UNAVAILABLE:
261                         throw new ResourceUnavailableException(
262                                 "No more SPIs may be allocated by this requester.");
263                     case Status.SPI_UNAVAILABLE:
264                         throw new SpiUnavailableException("Requested SPI is unavailable", spi);
265                     default:
266                         throw new RuntimeException(
267                                 "Unknown status returned by IpSecService: " + status);
268                 }
269                 mSpi = result.spi;
270                 mResourceId = result.resourceId;
271 
272                 if (mSpi == INVALID_SECURITY_PARAMETER_INDEX) {
273                     throw new RuntimeException("Invalid SPI returned by IpSecService: " + status);
274                 }
275 
276                 if (mResourceId == INVALID_RESOURCE_ID) {
277                     throw new RuntimeException(
278                             "Invalid Resource ID returned by IpSecService: " + status);
279                 }
280             } catch (RemoteException e) {
281                 throw e.rethrowFromSystemServer();
282             }
283             mCloseGuard.open("close");
284         }
285 
286         /** @hide */
287         @VisibleForTesting
getResourceId()288         public int getResourceId() {
289             return mResourceId;
290         }
291 
292         @Override
toString()293         public String toString() {
294             return new StringBuilder()
295                 .append("SecurityParameterIndex{spi=")
296                 .append(mSpi)
297                 .append(",resourceId=")
298                 .append(mResourceId)
299                 .append("}")
300                 .toString();
301         }
302     }
303 
304     /**
305      * Reserve a random SPI for traffic bound to or from the specified destination address.
306      *
307      * <p>If successful, this SPI is guaranteed available until released by a call to {@link
308      * SecurityParameterIndex#close()}.
309      *
310      * @param destinationAddress the destination address for traffic bearing the requested SPI.
311      *     For inbound traffic, the destination should be an address currently assigned on-device.
312      * @return the reserved SecurityParameterIndex
313      * @throws ResourceUnavailableException indicating that too many SPIs are
314      *     currently allocated for this user
315      */
316     @NonNull
allocateSecurityParameterIndex( @onNull InetAddress destinationAddress)317     public SecurityParameterIndex allocateSecurityParameterIndex(
318                 @NonNull InetAddress destinationAddress) throws ResourceUnavailableException {
319         try {
320             return new SecurityParameterIndex(
321                     mService,
322                     destinationAddress,
323                     IpSecManager.INVALID_SECURITY_PARAMETER_INDEX);
324         } catch (ServiceSpecificException e) {
325             throw rethrowUncheckedExceptionFromServiceSpecificException(e);
326         } catch (SpiUnavailableException unlikely) {
327             // Because this function allocates a totally random SPI, it really shouldn't ever
328             // fail to allocate an SPI; we simply need this because the exception is checked.
329             throw new ResourceUnavailableException("No SPIs available");
330         }
331     }
332 
333     /**
334      * Reserve the requested SPI for traffic bound to or from the specified destination address.
335      *
336      * <p>If successful, this SPI is guaranteed available until released by a call to {@link
337      * SecurityParameterIndex#close()}.
338      *
339      * @param destinationAddress the destination address for traffic bearing the requested SPI.
340      *     For inbound traffic, the destination should be an address currently assigned on-device.
341      * @param requestedSpi the requested SPI. The range 1-255 is reserved and may not be used. See
342      *     RFC 4303 Section 2.1.
343      * @return the reserved SecurityParameterIndex
344      * @throws ResourceUnavailableException indicating that too many SPIs are
345      *     currently allocated for this user
346      * @throws SpiUnavailableException indicating that the requested SPI could not be
347      *     reserved
348      */
349     @NonNull
allocateSecurityParameterIndex( @onNull InetAddress destinationAddress, int requestedSpi)350     public SecurityParameterIndex allocateSecurityParameterIndex(
351             @NonNull InetAddress destinationAddress, int requestedSpi)
352             throws SpiUnavailableException, ResourceUnavailableException {
353         if (requestedSpi == IpSecManager.INVALID_SECURITY_PARAMETER_INDEX) {
354             throw new IllegalArgumentException("Requested SPI must be a valid (non-zero) SPI");
355         }
356         try {
357             return new SecurityParameterIndex(mService, destinationAddress, requestedSpi);
358         } catch (ServiceSpecificException e) {
359             throw rethrowUncheckedExceptionFromServiceSpecificException(e);
360         }
361     }
362 
363     /**
364      * Apply an IPsec transform to a stream socket.
365      *
366      * <p>This applies transport mode encapsulation to the given socket. Once applied, I/O on the
367      * socket will be encapsulated according to the parameters of the {@code IpSecTransform}. When
368      * the transform is removed from the socket by calling {@link #removeTransportModeTransforms},
369      * unprotected traffic can resume on that socket.
370      *
371      * <p>For security reasons, the destination address of any traffic on the socket must match the
372      * remote {@code InetAddress} of the {@code IpSecTransform}. Attempts to send traffic to any
373      * other IP address will result in an IOException. In addition, reads and writes on the socket
374      * will throw IOException if the user deactivates the transform (by calling {@link
375      * IpSecTransform#close()}) without calling {@link #removeTransportModeTransforms}.
376      *
377      * <p>Note that when applied to TCP sockets, calling {@link IpSecTransform#close()} on an
378      * applied transform before completion of graceful shutdown may result in the shutdown sequence
379      * failing to complete. As such, applications requiring graceful shutdown MUST close the socket
380      * prior to deactivating the applied transform. Socket closure may be performed asynchronously
381      * (in batches), so the returning of a close function does not guarantee shutdown of a socket.
382      * Setting an SO_LINGER timeout results in socket closure being performed synchronously, and is
383      * sufficient to ensure shutdown.
384      *
385      * Specifically, if the transform is deactivated (by calling {@link IpSecTransform#close()}),
386      * prior to the socket being closed, the standard [FIN - FIN/ACK - ACK], or the reset [RST]
387      * packets are dropped due to the lack of a valid Transform. Similarly, if a socket without the
388      * SO_LINGER option set is closed, the delayed/batched FIN packets may be dropped.
389      *
390      * <h4>Rekey Procedure</h4>
391      *
392      * <p>When applying a new tranform to a socket in the outbound direction, the previous transform
393      * will be removed and the new transform will take effect immediately, sending all traffic on
394      * the new transform; however, when applying a transform in the inbound direction, traffic
395      * on the old transform will continue to be decrypted and delivered until that transform is
396      * deallocated by calling {@link IpSecTransform#close()}. This overlap allows lossless rekey
397      * procedures where both transforms are valid until both endpoints are using the new transform
398      * and all in-flight packets have been received.
399      *
400      * @param socket a stream socket
401      * @param direction the direction in which the transform should be applied
402      * @param transform a transport mode {@code IpSecTransform}
403      * @throws IOException indicating that the transform could not be applied
404      */
applyTransportModeTransform(@onNull Socket socket, @PolicyDirection int direction, @NonNull IpSecTransform transform)405     public void applyTransportModeTransform(@NonNull Socket socket,
406             @PolicyDirection int direction, @NonNull IpSecTransform transform) throws IOException {
407         // Ensure creation of FD. See b/77548890 for more details.
408         socket.getSoLinger();
409 
410         applyTransportModeTransform(socket.getFileDescriptor$(), direction, transform);
411     }
412 
413     /**
414      * Apply an IPsec transform to a datagram socket.
415      *
416      * <p>This applies transport mode encapsulation to the given socket. Once applied, I/O on the
417      * socket will be encapsulated according to the parameters of the {@code IpSecTransform}. When
418      * the transform is removed from the socket by calling {@link #removeTransportModeTransforms},
419      * unprotected traffic can resume on that socket.
420      *
421      * <p>For security reasons, the destination address of any traffic on the socket must match the
422      * remote {@code InetAddress} of the {@code IpSecTransform}. Attempts to send traffic to any
423      * other IP address will result in an IOException. In addition, reads and writes on the socket
424      * will throw IOException if the user deactivates the transform (by calling {@link
425      * IpSecTransform#close()}) without calling {@link #removeTransportModeTransforms}.
426      *
427      * <h4>Rekey Procedure</h4>
428      *
429      * <p>When applying a new tranform to a socket in the outbound direction, the previous transform
430      * will be removed and the new transform will take effect immediately, sending all traffic on
431      * the new transform; however, when applying a transform in the inbound direction, traffic
432      * on the old transform will continue to be decrypted and delivered until that transform is
433      * deallocated by calling {@link IpSecTransform#close()}. This overlap allows lossless rekey
434      * procedures where both transforms are valid until both endpoints are using the new transform
435      * and all in-flight packets have been received.
436      *
437      * @param socket a datagram socket
438      * @param direction the direction in which the transform should be applied
439      * @param transform a transport mode {@code IpSecTransform}
440      * @throws IOException indicating that the transform could not be applied
441      */
applyTransportModeTransform(@onNull DatagramSocket socket, @PolicyDirection int direction, @NonNull IpSecTransform transform)442     public void applyTransportModeTransform(@NonNull DatagramSocket socket,
443             @PolicyDirection int direction, @NonNull IpSecTransform transform) throws IOException {
444         applyTransportModeTransform(socket.getFileDescriptor$(), direction, transform);
445     }
446 
447     /**
448      * Apply an IPsec transform to a socket.
449      *
450      * <p>This applies transport mode encapsulation to the given socket. Once applied, I/O on the
451      * socket will be encapsulated according to the parameters of the {@code IpSecTransform}. When
452      * the transform is removed from the socket by calling {@link #removeTransportModeTransforms},
453      * unprotected traffic can resume on that socket.
454      *
455      * <p>For security reasons, the destination address of any traffic on the socket must match the
456      * remote {@code InetAddress} of the {@code IpSecTransform}. Attempts to send traffic to any
457      * other IP address will result in an IOException. In addition, reads and writes on the socket
458      * will throw IOException if the user deactivates the transform (by calling {@link
459      * IpSecTransform#close()}) without calling {@link #removeTransportModeTransforms}.
460      *
461      * <p>Note that when applied to TCP sockets, calling {@link IpSecTransform#close()} on an
462      * applied transform before completion of graceful shutdown may result in the shutdown sequence
463      * failing to complete. As such, applications requiring graceful shutdown MUST close the socket
464      * prior to deactivating the applied transform. Socket closure may be performed asynchronously
465      * (in batches), so the returning of a close function does not guarantee shutdown of a socket.
466      * Setting an SO_LINGER timeout results in socket closure being performed synchronously, and is
467      * sufficient to ensure shutdown.
468      *
469      * Specifically, if the transform is deactivated (by calling {@link IpSecTransform#close()}),
470      * prior to the socket being closed, the standard [FIN - FIN/ACK - ACK], or the reset [RST]
471      * packets are dropped due to the lack of a valid Transform. Similarly, if a socket without the
472      * SO_LINGER option set is closed, the delayed/batched FIN packets may be dropped.
473      *
474      * <h4>Rekey Procedure</h4>
475      *
476      * <p>When applying a new tranform to a socket in the outbound direction, the previous transform
477      * will be removed and the new transform will take effect immediately, sending all traffic on
478      * the new transform; however, when applying a transform in the inbound direction, traffic
479      * on the old transform will continue to be decrypted and delivered until that transform is
480      * deallocated by calling {@link IpSecTransform#close()}. This overlap allows lossless rekey
481      * procedures where both transforms are valid until both endpoints are using the new transform
482      * and all in-flight packets have been received.
483      *
484      * @param socket a socket file descriptor
485      * @param direction the direction in which the transform should be applied
486      * @param transform a transport mode {@code IpSecTransform}
487      * @throws IOException indicating that the transform could not be applied
488      */
applyTransportModeTransform(@onNull FileDescriptor socket, @PolicyDirection int direction, @NonNull IpSecTransform transform)489     public void applyTransportModeTransform(@NonNull FileDescriptor socket,
490             @PolicyDirection int direction, @NonNull IpSecTransform transform) throws IOException {
491         // We dup() the FileDescriptor here because if we don't, then the ParcelFileDescriptor()
492         // constructor takes control and closes the user's FD when we exit the method.
493         try (ParcelFileDescriptor pfd = ParcelFileDescriptor.dup(socket)) {
494             mService.applyTransportModeTransform(pfd, direction, transform.getResourceId());
495         } catch (ServiceSpecificException e) {
496             throw rethrowCheckedExceptionFromServiceSpecificException(e);
497         } catch (RemoteException e) {
498             throw e.rethrowFromSystemServer();
499         }
500     }
501 
502     /**
503      * Remove an IPsec transform from a stream socket.
504      *
505      * <p>Once removed, traffic on the socket will not be encrypted. Removing transforms from a
506      * socket allows the socket to be reused for communication in the clear.
507      *
508      * <p>If an {@code IpSecTransform} object applied to this socket was deallocated by calling
509      * {@link IpSecTransform#close()}, then communication on the socket will fail until this method
510      * is called.
511      *
512      * @param socket a socket that previously had a transform applied to it
513      * @throws IOException indicating that the transform could not be removed from the socket
514      */
removeTransportModeTransforms(@onNull Socket socket)515     public void removeTransportModeTransforms(@NonNull Socket socket) throws IOException {
516         // Ensure creation of FD. See b/77548890 for more details.
517         socket.getSoLinger();
518 
519         removeTransportModeTransforms(socket.getFileDescriptor$());
520     }
521 
522     /**
523      * Remove an IPsec transform from a datagram socket.
524      *
525      * <p>Once removed, traffic on the socket will not be encrypted. Removing transforms from a
526      * socket allows the socket to be reused for communication in the clear.
527      *
528      * <p>If an {@code IpSecTransform} object applied to this socket was deallocated by calling
529      * {@link IpSecTransform#close()}, then communication on the socket will fail until this method
530      * is called.
531      *
532      * @param socket a socket that previously had a transform applied to it
533      * @throws IOException indicating that the transform could not be removed from the socket
534      */
removeTransportModeTransforms(@onNull DatagramSocket socket)535     public void removeTransportModeTransforms(@NonNull DatagramSocket socket) throws IOException {
536         removeTransportModeTransforms(socket.getFileDescriptor$());
537     }
538 
539     /**
540      * Remove an IPsec transform from a socket.
541      *
542      * <p>Once removed, traffic on the socket will not be encrypted. Removing transforms from a
543      * socket allows the socket to be reused for communication in the clear.
544      *
545      * <p>If an {@code IpSecTransform} object applied to this socket was deallocated by calling
546      * {@link IpSecTransform#close()}, then communication on the socket will fail until this method
547      * is called.
548      *
549      * @param socket a socket that previously had a transform applied to it
550      * @throws IOException indicating that the transform could not be removed from the socket
551      */
removeTransportModeTransforms(@onNull FileDescriptor socket)552     public void removeTransportModeTransforms(@NonNull FileDescriptor socket) throws IOException {
553         try (ParcelFileDescriptor pfd = ParcelFileDescriptor.dup(socket)) {
554             mService.removeTransportModeTransforms(pfd);
555         } catch (ServiceSpecificException e) {
556             throw rethrowCheckedExceptionFromServiceSpecificException(e);
557         } catch (RemoteException e) {
558             throw e.rethrowFromSystemServer();
559         }
560     }
561 
562     /**
563      * Remove a Tunnel Mode IPsec Transform from a {@link Network}. This must be used as part of
564      * cleanup if a tunneled Network experiences a change in default route. The Network will drop
565      * all traffic that cannot be routed to the Tunnel's outbound interface. If that interface is
566      * lost, all traffic will drop.
567      *
568      * <p>TODO: Update javadoc for tunnel mode APIs at the same time the APIs are re-worked.
569      *
570      * @param net a network that currently has transform applied to it.
571      * @param transform a Tunnel Mode IPsec Transform that has been previously applied to the given
572      *     network
573      * @hide
574      */
removeTunnelModeTransform(Network net, IpSecTransform transform)575     public void removeTunnelModeTransform(Network net, IpSecTransform transform) {}
576 
577     /**
578      * This class provides access to a UDP encapsulation Socket.
579      *
580      * <p>{@code UdpEncapsulationSocket} wraps a system-provided datagram socket intended for IKEv2
581      * signalling and UDP encapsulated IPsec traffic. Instances can be obtained by calling {@link
582      * IpSecManager#openUdpEncapsulationSocket}. The provided socket cannot be re-bound by the
583      * caller. The caller should not close the {@code FileDescriptor} returned by {@link
584      * #getFileDescriptor}, but should use {@link #close} instead.
585      *
586      * <p>Allowing the user to close or unbind a UDP encapsulation socket could impact the traffic
587      * of the next user who binds to that port. To prevent this scenario, these sockets are held
588      * open by the system so that they may only be closed by calling {@link #close} or when the user
589      * process exits.
590      */
591     public static final class UdpEncapsulationSocket implements AutoCloseable {
592         private final ParcelFileDescriptor mPfd;
593         private final IIpSecService mService;
594         private int mResourceId = INVALID_RESOURCE_ID;
595         private final int mPort;
596         private final CloseGuard mCloseGuard = CloseGuard.get();
597 
UdpEncapsulationSocket(@onNull IIpSecService service, int port)598         private UdpEncapsulationSocket(@NonNull IIpSecService service, int port)
599                 throws ResourceUnavailableException, IOException {
600             mService = service;
601             try {
602                 IpSecUdpEncapResponse result =
603                         mService.openUdpEncapsulationSocket(port, new Binder());
604                 switch (result.status) {
605                     case Status.OK:
606                         break;
607                     case Status.RESOURCE_UNAVAILABLE:
608                         throw new ResourceUnavailableException(
609                                 "No more Sockets may be allocated by this requester.");
610                     default:
611                         throw new RuntimeException(
612                                 "Unknown status returned by IpSecService: " + result.status);
613                 }
614                 mResourceId = result.resourceId;
615                 mPort = result.port;
616                 mPfd = result.fileDescriptor;
617             } catch (RemoteException e) {
618                 throw e.rethrowFromSystemServer();
619             }
620             mCloseGuard.open("close");
621         }
622 
623         /** Get the encapsulation socket's file descriptor. */
getFileDescriptor()624         public FileDescriptor getFileDescriptor() {
625             if (mPfd == null) {
626                 return null;
627             }
628             return mPfd.getFileDescriptor();
629         }
630 
631         /** Get the bound port of the wrapped socket. */
getPort()632         public int getPort() {
633             return mPort;
634         }
635 
636         /**
637          * Close this socket.
638          *
639          * <p>This closes the wrapped socket. Open encapsulation sockets count against a user's
640          * resource limits, and forgetting to close them eventually will result in {@link
641          * ResourceUnavailableException} being thrown.
642          */
643         @Override
close()644         public void close() throws IOException {
645             try {
646                 mService.closeUdpEncapsulationSocket(mResourceId);
647                 mResourceId = INVALID_RESOURCE_ID;
648             } catch (RemoteException e) {
649                 throw e.rethrowFromSystemServer();
650             } catch (Exception e) {
651                 // On close we swallow all random exceptions since failure to close is not
652                 // actionable by the user.
653                 Log.e(TAG, "Failed to close " + this + ", Exception=" + e);
654             } finally {
655                 mResourceId = INVALID_RESOURCE_ID;
656                 mCloseGuard.close();
657             }
658 
659             try {
660                 mPfd.close();
661             } catch (IOException e) {
662                 Log.e(TAG, "Failed to close UDP Encapsulation Socket with Port= " + mPort);
663                 throw e;
664             }
665         }
666 
667         /** Check that the socket was closed properly. */
668         @Override
finalize()669         protected void finalize() throws Throwable {
670             if (mCloseGuard != null) {
671                 mCloseGuard.warnIfOpen();
672             }
673             close();
674         }
675 
676         /** @hide */
677         @SystemApi(client = MODULE_LIBRARIES)
getResourceId()678         public int getResourceId() {
679             return mResourceId;
680         }
681 
682         @Override
toString()683         public String toString() {
684             return new StringBuilder()
685                 .append("UdpEncapsulationSocket{port=")
686                 .append(mPort)
687                 .append(",resourceId=")
688                 .append(mResourceId)
689                 .append("}")
690                 .toString();
691         }
692     };
693 
694     /**
695      * Open a socket for UDP encapsulation and bind to the given port.
696      *
697      * <p>See {@link UdpEncapsulationSocket} for the proper way to close the returned socket.
698      *
699      * @param port a local UDP port
700      * @return a socket that is bound to the given port
701      * @throws IOException indicating that the socket could not be opened or bound
702      * @throws ResourceUnavailableException indicating that too many encapsulation sockets are open
703      */
704     // Returning a socket in this fashion that has been created and bound by the system
705     // is the only safe way to ensure that a socket is both accessible to the user and
706     // safely usable for Encapsulation without allowing a user to possibly unbind from/close
707     // the port, which could potentially impact the traffic of the next user who binds to that
708     // socket.
709     @NonNull
openUdpEncapsulationSocket(int port)710     public UdpEncapsulationSocket openUdpEncapsulationSocket(int port)
711             throws IOException, ResourceUnavailableException {
712         /*
713          * Most range checking is done in the service, but this version of the constructor expects
714          * a valid port number, and zero cannot be checked after being passed to the service.
715          */
716         if (port == 0) {
717             throw new IllegalArgumentException("Specified port must be a valid port number!");
718         }
719         try {
720             return new UdpEncapsulationSocket(mService, port);
721         } catch (ServiceSpecificException e) {
722             throw rethrowCheckedExceptionFromServiceSpecificException(e);
723         }
724     }
725 
726     /**
727      * Open a socket for UDP encapsulation.
728      *
729      * <p>See {@link UdpEncapsulationSocket} for the proper way to close the returned socket.
730      *
731      * <p>The local port of the returned socket can be obtained by calling {@link
732      * UdpEncapsulationSocket#getPort()}.
733      *
734      * @return a socket that is bound to a local port
735      * @throws IOException indicating that the socket could not be opened or bound
736      * @throws ResourceUnavailableException indicating that too many encapsulation sockets are open
737      */
738     // Returning a socket in this fashion that has been created and bound by the system
739     // is the only safe way to ensure that a socket is both accessible to the user and
740     // safely usable for Encapsulation without allowing a user to possibly unbind from/close
741     // the port, which could potentially impact the traffic of the next user who binds to that
742     // socket.
743     @NonNull
openUdpEncapsulationSocket()744     public UdpEncapsulationSocket openUdpEncapsulationSocket()
745             throws IOException, ResourceUnavailableException {
746         try {
747             return new UdpEncapsulationSocket(mService, 0);
748         } catch (ServiceSpecificException e) {
749             throw rethrowCheckedExceptionFromServiceSpecificException(e);
750         }
751     }
752 
753     /**
754      * This class represents an IpSecTunnelInterface
755      *
756      * <p>IpSecTunnelInterface objects track tunnel interfaces that serve as
757      * local endpoints for IPsec tunnels.
758      *
759      * <p>Creating an IpSecTunnelInterface creates a device to which IpSecTransforms may be
760      * applied to provide IPsec security to packets sent through the tunnel. While a tunnel
761      * cannot be used in standalone mode within Android, the higher layers may use the tunnel
762      * to create Network objects which are accessible to the Android system.
763      * @hide
764      */
765     @SystemApi
766     public static final class IpSecTunnelInterface implements AutoCloseable {
767         private final String mOpPackageName;
768         private final IIpSecService mService;
769         private final InetAddress mRemoteAddress;
770         private final InetAddress mLocalAddress;
771         private final Network mUnderlyingNetwork;
772         private final CloseGuard mCloseGuard = CloseGuard.get();
773         private String mInterfaceName;
774         private int mResourceId = INVALID_RESOURCE_ID;
775 
776         /** Get the underlying SPI held by this object. */
777         @NonNull
getInterfaceName()778         public String getInterfaceName() {
779             return mInterfaceName;
780         }
781 
782         /**
783          * Add an address to the IpSecTunnelInterface
784          *
785          * <p>Add an address which may be used as the local inner address for
786          * tunneled traffic.
787          *
788          * @param address the local address for traffic inside the tunnel
789          * @param prefixLen length of the InetAddress prefix
790          * @hide
791          */
792         @SystemApi
793         @RequiresFeature(PackageManager.FEATURE_IPSEC_TUNNELS)
794         @RequiresPermission(android.Manifest.permission.MANAGE_IPSEC_TUNNELS)
addAddress(@onNull InetAddress address, int prefixLen)795         public void addAddress(@NonNull InetAddress address, int prefixLen) throws IOException {
796             try {
797                 mService.addAddressToTunnelInterface(
798                         mResourceId, new LinkAddress(address, prefixLen), mOpPackageName);
799             } catch (ServiceSpecificException e) {
800                 throw rethrowCheckedExceptionFromServiceSpecificException(e);
801             } catch (RemoteException e) {
802                 throw e.rethrowFromSystemServer();
803             }
804         }
805 
806         /**
807          * Remove an address from the IpSecTunnelInterface
808          *
809          * <p>Remove an address which was previously added to the IpSecTunnelInterface
810          *
811          * @param address to be removed
812          * @param prefixLen length of the InetAddress prefix
813          * @hide
814          */
815         @SystemApi
816         @RequiresFeature(PackageManager.FEATURE_IPSEC_TUNNELS)
817         @RequiresPermission(android.Manifest.permission.MANAGE_IPSEC_TUNNELS)
removeAddress(@onNull InetAddress address, int prefixLen)818         public void removeAddress(@NonNull InetAddress address, int prefixLen) throws IOException {
819             try {
820                 mService.removeAddressFromTunnelInterface(
821                         mResourceId, new LinkAddress(address, prefixLen), mOpPackageName);
822             } catch (ServiceSpecificException e) {
823                 throw rethrowCheckedExceptionFromServiceSpecificException(e);
824             } catch (RemoteException e) {
825                 throw e.rethrowFromSystemServer();
826             }
827         }
828 
829         /**
830          * Update the underlying network for this IpSecTunnelInterface.
831          *
832          * <p>This new underlying network will be used for all transforms applied AFTER this call is
833          * complete. Before {@link IpSecTransform}(s) with matching addresses are applied to this
834          * tunnel interface, traffic will still use the old transform, and be routed on the old
835          * underlying network.
836          *
837          * <p>To migrate IPsec tunnel mode traffic, a caller should:
838          *
839          * <ol>
840          *   <li>Update the IpSecTunnelInterface’s underlying network.
841          *   <li>Apply the new {@link IpSecTransform}(s) to this IpSecTunnelInterface. These can be
842          *       new {@link IpSecTransform}(s) with matching addresses, or {@link IpSecTransform}(s)
843          *       that have started migration (see {@link
844          *       IpSecManager#startTunnelModeTransformMigration}).
845          * </ol>
846          *
847          * @param underlyingNetwork the new {@link Network} that will carry traffic for this tunnel.
848          *     This network MUST be a functional {@link Network} with valid {@link LinkProperties},
849          *     and MUST never be the network exposing this IpSecTunnelInterface, otherwise this
850          *     method will throw an {@link IllegalArgumentException}. If the IpSecTunnelInterface is
851          *     later added to this network, all outbound traffic will be blackholed.
852          */
853         // The purpose of making updating network and applying transforms separate is to leave open
854         // the possibility to support lossless migration procedures. To do that, Android platform
855         // will need to support multiple inbound tunnel mode transforms, just like it can support
856         // multiple transport mode transforms.
857         @RequiresFeature(PackageManager.FEATURE_IPSEC_TUNNELS)
858         @RequiresPermission(android.Manifest.permission.MANAGE_IPSEC_TUNNELS)
setUnderlyingNetwork(@onNull Network underlyingNetwork)859         public void setUnderlyingNetwork(@NonNull Network underlyingNetwork) throws IOException {
860             try {
861                 mService.setNetworkForTunnelInterface(
862                         mResourceId, underlyingNetwork, mOpPackageName);
863             } catch (RemoteException e) {
864                 throw e.rethrowFromSystemServer();
865             }
866         }
867 
IpSecTunnelInterface(@onNull Context ctx, @NonNull IIpSecService service, @NonNull InetAddress localAddress, @NonNull InetAddress remoteAddress, @NonNull Network underlyingNetwork)868         private IpSecTunnelInterface(@NonNull Context ctx, @NonNull IIpSecService service,
869                 @NonNull InetAddress localAddress, @NonNull InetAddress remoteAddress,
870                 @NonNull Network underlyingNetwork)
871                 throws ResourceUnavailableException, IOException {
872             mOpPackageName = ctx.getOpPackageName();
873             mService = service;
874             mLocalAddress = localAddress;
875             mRemoteAddress = remoteAddress;
876             mUnderlyingNetwork = underlyingNetwork;
877 
878             try {
879                 IpSecTunnelInterfaceResponse result =
880                         mService.createTunnelInterface(
881                                 localAddress.getHostAddress(),
882                                 remoteAddress.getHostAddress(),
883                                 underlyingNetwork,
884                                 new Binder(),
885                                 mOpPackageName);
886                 switch (result.status) {
887                     case Status.OK:
888                         break;
889                     case Status.RESOURCE_UNAVAILABLE:
890                         throw new ResourceUnavailableException(
891                                 "No more tunnel interfaces may be allocated by this requester.");
892                     default:
893                         throw new RuntimeException(
894                                 "Unknown status returned by IpSecService: " + result.status);
895                 }
896                 mResourceId = result.resourceId;
897                 mInterfaceName = result.interfaceName;
898             } catch (RemoteException e) {
899                 throw e.rethrowFromSystemServer();
900             }
901             mCloseGuard.open("close");
902         }
903 
904         /**
905          * Delete an IpSecTunnelInterface
906          *
907          * <p>Calling close will deallocate the IpSecTunnelInterface and all of its system
908          * resources. Any packets bound for this interface either inbound or outbound will
909          * all be lost.
910          */
911         @Override
close()912         public void close() {
913             try {
914                 mService.deleteTunnelInterface(mResourceId, mOpPackageName);
915             } catch (RemoteException e) {
916                 throw e.rethrowFromSystemServer();
917             } catch (Exception e) {
918                 // On close we swallow all random exceptions since failure to close is not
919                 // actionable by the user.
920                 Log.e(TAG, "Failed to close " + this + ", Exception=" + e);
921             } finally {
922                 mResourceId = INVALID_RESOURCE_ID;
923                 mCloseGuard.close();
924             }
925         }
926 
927         /** Check that the Interface was closed properly. */
928         @Override
finalize()929         protected void finalize() throws Throwable {
930             if (mCloseGuard != null) {
931                 mCloseGuard.warnIfOpen();
932             }
933             close();
934         }
935 
936         /** @hide */
937         @VisibleForTesting
getResourceId()938         public int getResourceId() {
939             return mResourceId;
940         }
941 
942         @NonNull
943         @Override
toString()944         public String toString() {
945             return new StringBuilder()
946                 .append("IpSecTunnelInterface{ifname=")
947                 .append(mInterfaceName)
948                 .append(",resourceId=")
949                 .append(mResourceId)
950                 .append("}")
951                 .toString();
952         }
953     }
954 
955     /**
956      * Create a new IpSecTunnelInterface as a local endpoint for tunneled IPsec traffic.
957      *
958      * <p>An application that creates tunnels is responsible for cleaning up the tunnel when the
959      * underlying network goes away, and the onLost() callback is received.
960      *
961      * @param localAddress The local addres of the tunnel
962      * @param remoteAddress The local addres of the tunnel
963      * @param underlyingNetwork the {@link Network} that will carry traffic for this tunnel.
964      *        This network should almost certainly be a network such as WiFi with an L2 address.
965      * @return a new {@link IpSecManager#IpSecTunnelInterface} with the specified properties
966      * @throws IOException indicating that the socket could not be opened or bound
967      * @throws ResourceUnavailableException indicating that too many encapsulation sockets are open
968      * @hide
969      */
970     @SystemApi
971     @NonNull
972     @RequiresFeature(PackageManager.FEATURE_IPSEC_TUNNELS)
973     @RequiresPermission(android.Manifest.permission.MANAGE_IPSEC_TUNNELS)
createIpSecTunnelInterface(@onNull InetAddress localAddress, @NonNull InetAddress remoteAddress, @NonNull Network underlyingNetwork)974     public IpSecTunnelInterface createIpSecTunnelInterface(@NonNull InetAddress localAddress,
975             @NonNull InetAddress remoteAddress, @NonNull Network underlyingNetwork)
976             throws ResourceUnavailableException, IOException {
977         try {
978             return new IpSecTunnelInterface(
979                     mContext, mService, localAddress, remoteAddress, underlyingNetwork);
980         } catch (ServiceSpecificException e) {
981             throw rethrowCheckedExceptionFromServiceSpecificException(e);
982         }
983     }
984 
985     /**
986      * Apply an active Tunnel Mode IPsec Transform to a {@link IpSecTunnelInterface}, which will
987      * tunnel all traffic for the given direction through the underlying network's interface with
988      * IPsec (applies an outer IP header and IPsec Header to all traffic, and expects an additional
989      * IP header and IPsec Header on all inbound traffic).
990      * <p>Applications should probably not use this API directly.
991      *
992      * @param tunnel The {@link IpSecManager#IpSecTunnelInterface} that will use the supplied
993      *        transform.
994      * @param direction the direction, {@link DIRECTION_OUT} or {@link #DIRECTION_IN} in which
995      *        the transform will be used.
996      * @param transform an {@link IpSecTransform} created in tunnel mode
997      * @throws IOException indicating that the transform could not be applied due to a lower
998      *         layer failure.
999      * @hide
1000      */
1001     @SystemApi
1002     @RequiresFeature(PackageManager.FEATURE_IPSEC_TUNNELS)
1003     @RequiresPermission(android.Manifest.permission.MANAGE_IPSEC_TUNNELS)
applyTunnelModeTransform(@onNull IpSecTunnelInterface tunnel, @PolicyDirection int direction, @NonNull IpSecTransform transform)1004     public void applyTunnelModeTransform(@NonNull IpSecTunnelInterface tunnel,
1005             @PolicyDirection int direction, @NonNull IpSecTransform transform) throws IOException {
1006         try {
1007             mService.applyTunnelModeTransform(
1008                     tunnel.getResourceId(), direction,
1009                     transform.getResourceId(), mContext.getOpPackageName());
1010         } catch (ServiceSpecificException e) {
1011             throw rethrowCheckedExceptionFromServiceSpecificException(e);
1012         } catch (RemoteException e) {
1013             throw e.rethrowFromSystemServer();
1014         }
1015     }
1016 
1017     /**
1018      * Migrate an active Tunnel Mode IPsec Transform to new source/destination addresses.
1019      *
1020      * <p>Begins the process of migrating a transform and cache the new addresses. To complete the
1021      * migration once started, callers MUST apply the same transform to the appropriate tunnel using
1022      * {@link IpSecManager#applyTunnelModeTransform}. Otherwise, the address update will not be
1023      * committed and the transform will still only process traffic between the current source and
1024      * destination address. One common use case is that the control plane will start the migration
1025      * process and then hand off the transform to the IPsec caller to perform the actual migration
1026      * when the tunnel is ready.
1027      *
1028      * <p>If this method is called multiple times before {@link
1029      * IpSecManager#applyTunnelModeTransform} is called, when the transform is applied, it will be
1030      * migrated to the addresses from the last call.
1031      *
1032      * <p>The provided source and destination addresses MUST share the same address family, but they
1033      * can have a different family from the current addresses.
1034      *
1035      * <p>Transform migration is only supported for tunnel mode transforms. Calling this method on
1036      * other types of transforms will throw an {@code UnsupportedOperationException}.
1037      *
1038      * @see IpSecTunnelInterface#setUnderlyingNetwork
1039      * @param transform a tunnel mode {@link IpSecTransform}
1040      * @param newSourceAddress the new source address
1041      * @param newDestinationAddress the new destination address
1042      * @hide
1043      */
1044     @SystemApi
1045     @RequiresFeature(FEATURE_IPSEC_TUNNEL_MIGRATION)
1046     @RequiresPermission(android.Manifest.permission.MANAGE_IPSEC_TUNNELS)
startTunnelModeTransformMigration( @onNull IpSecTransform transform, @NonNull InetAddress newSourceAddress, @NonNull InetAddress newDestinationAddress)1047     public void startTunnelModeTransformMigration(
1048             @NonNull IpSecTransform transform,
1049             @NonNull InetAddress newSourceAddress,
1050             @NonNull InetAddress newDestinationAddress) {
1051         if (!SdkLevel.isAtLeastU()) {
1052             throw new UnsupportedOperationException(
1053                     "Transform migration only supported for Android 14+");
1054         }
1055 
1056         Objects.requireNonNull(transform, "transform was null");
1057         Objects.requireNonNull(newSourceAddress, "newSourceAddress was null");
1058         Objects.requireNonNull(newDestinationAddress, "newDestinationAddress was null");
1059 
1060         try {
1061             mService.migrateTransform(
1062                     transform.getResourceId(),
1063                     newSourceAddress.getHostAddress(),
1064                     newDestinationAddress.getHostAddress(),
1065                     mContext.getOpPackageName());
1066         } catch (RemoteException e) {
1067             throw e.rethrowFromSystemServer();
1068         }
1069     }
1070 
1071     /**
1072      * @hide
1073      */
createTransform(IpSecConfig config, IBinder binder, String callingPackage)1074     public IpSecTransformResponse createTransform(IpSecConfig config, IBinder binder,
1075             String callingPackage) {
1076         try {
1077             return mService.createTransform(config, binder, callingPackage);
1078         } catch (RemoteException e) {
1079             throw e.rethrowFromSystemServer();
1080         }
1081     }
1082 
1083     /**
1084      * @hide
1085      */
deleteTransform(int resourceId)1086     public void deleteTransform(int resourceId) {
1087         try {
1088             mService.deleteTransform(resourceId);
1089         } catch (RemoteException e) {
1090             throw e.rethrowFromSystemServer();
1091         }
1092     }
1093 
1094     /** @hide */
getTransformState(int transformId)1095     public IpSecTransformState getTransformState(int transformId)
1096             throws IllegalStateException, RemoteException {
1097         return mService.getTransformState(transformId);
1098     }
1099 
1100     /**
1101      * Construct an instance of IpSecManager within an application context.
1102      *
1103      * @param context the application context for this manager
1104      * @hide
1105      */
IpSecManager(Context ctx, IIpSecService service)1106     public IpSecManager(Context ctx, IIpSecService service) {
1107         mContext = ctx;
1108         mService = Objects.requireNonNull(service, "missing service");
1109     }
1110 
maybeHandleServiceSpecificException(ServiceSpecificException sse)1111     private static void maybeHandleServiceSpecificException(ServiceSpecificException sse) {
1112         // OsConstants are late binding, so switch statements can't be used.
1113         if (sse.errorCode == OsConstants.EINVAL) {
1114             throw new IllegalArgumentException(sse);
1115         } else if (sse.errorCode == OsConstants.EAGAIN) {
1116             throw new IllegalStateException(sse);
1117         } else if (sse.errorCode == OsConstants.EOPNOTSUPP
1118                 || sse.errorCode == OsConstants.EPROTONOSUPPORT) {
1119             throw new UnsupportedOperationException(sse);
1120         }
1121     }
1122 
1123     /**
1124      * Convert an Errno SSE to the correct Unchecked exception type.
1125      *
1126      * This method never actually returns.
1127      */
1128     // package
1129     static RuntimeException
rethrowUncheckedExceptionFromServiceSpecificException(ServiceSpecificException sse)1130             rethrowUncheckedExceptionFromServiceSpecificException(ServiceSpecificException sse) {
1131         maybeHandleServiceSpecificException(sse);
1132         throw new RuntimeException(sse);
1133     }
1134 
1135     /**
1136      * Convert an Errno SSE to the correct Checked or Unchecked exception type.
1137      *
1138      * This method may throw IOException, or it may throw an unchecked exception; it will never
1139      * actually return.
1140      */
1141     // package
rethrowCheckedExceptionFromServiceSpecificException( ServiceSpecificException sse)1142     static IOException rethrowCheckedExceptionFromServiceSpecificException(
1143             ServiceSpecificException sse) throws IOException {
1144         // First see if this is an unchecked exception of a type we know.
1145         // If so, then we prefer the unchecked (specific) type of exception.
1146         maybeHandleServiceSpecificException(sse);
1147         // If not, then all we can do is provide the SSE in the form of an IOException.
1148         throw new ErrnoException(
1149                 "IpSec encountered errno=" + sse.errorCode, sse.errorCode).rethrowAsIOException();
1150     }
1151 }
1152