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