• 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 
17 package com.android.server;
18 
19 import static android.Manifest.permission.DUMP;
20 import static android.net.IpSecManager.INVALID_RESOURCE_ID;
21 import static android.system.OsConstants.AF_INET;
22 import static android.system.OsConstants.AF_INET6;
23 import static android.system.OsConstants.AF_UNSPEC;
24 import static android.system.OsConstants.EINVAL;
25 import static android.system.OsConstants.IPPROTO_UDP;
26 import static android.system.OsConstants.SOCK_DGRAM;
27 
28 import android.annotation.NonNull;
29 import android.app.AppOpsManager;
30 import android.content.Context;
31 import android.content.pm.PackageManager;
32 import android.net.ConnectivityManager;
33 import android.net.IIpSecService;
34 import android.net.INetd;
35 import android.net.InetAddresses;
36 import android.net.IpSecAlgorithm;
37 import android.net.IpSecConfig;
38 import android.net.IpSecManager;
39 import android.net.IpSecSpiResponse;
40 import android.net.IpSecTransform;
41 import android.net.IpSecTransformResponse;
42 import android.net.IpSecTunnelInterfaceResponse;
43 import android.net.IpSecUdpEncapResponse;
44 import android.net.LinkAddress;
45 import android.net.LinkProperties;
46 import android.net.Network;
47 import android.net.TrafficStats;
48 import android.os.Binder;
49 import android.os.IBinder;
50 import android.os.ParcelFileDescriptor;
51 import android.os.Process;
52 import android.os.RemoteException;
53 import android.os.ServiceSpecificException;
54 import android.system.ErrnoException;
55 import android.system.Os;
56 import android.system.OsConstants;
57 import android.text.TextUtils;
58 import android.util.Log;
59 import android.util.Range;
60 import android.util.SparseArray;
61 import android.util.SparseBooleanArray;
62 
63 import com.android.internal.annotations.GuardedBy;
64 import com.android.internal.annotations.VisibleForTesting;
65 import com.android.internal.util.Preconditions;
66 import com.android.net.module.util.BinderUtils;
67 import com.android.net.module.util.NetdUtils;
68 import com.android.net.module.util.PermissionUtils;
69 
70 import libcore.io.IoUtils;
71 
72 import java.io.FileDescriptor;
73 import java.io.IOException;
74 import java.io.PrintWriter;
75 import java.net.Inet4Address;
76 import java.net.Inet6Address;
77 import java.net.InetAddress;
78 import java.net.InetSocketAddress;
79 import java.net.UnknownHostException;
80 import java.util.ArrayList;
81 import java.util.List;
82 import java.util.Objects;
83 
84 /**
85  * A service to manage multiple clients that want to access the IpSec API. The service is
86  * responsible for maintaining a list of clients and managing the resources (and related quotas)
87  * that each of them own.
88  *
89  * <p>Synchronization in IpSecService is done on all entrypoints due to potential race conditions at
90  * the kernel/xfrm level. Further, this allows the simplifying assumption to be made that only one
91  * thread is ever running at a time.
92  *
93  * @hide
94  */
95 public class IpSecService extends IIpSecService.Stub {
96     private static final String TAG = "IpSecService";
97     private static final boolean DBG = Log.isLoggable(TAG, Log.DEBUG);
98     private static final int[] ADDRESS_FAMILIES =
99             new int[] {OsConstants.AF_INET, OsConstants.AF_INET6};
100 
101     private static final int NETD_FETCH_TIMEOUT_MS = 5000; // ms
102     private static final InetAddress INADDR_ANY;
103 
104     @VisibleForTesting static final int MAX_PORT_BIND_ATTEMPTS = 10;
105 
106     private final INetd mNetd;
107 
108     static {
109         try {
110             INADDR_ANY = InetAddress.getByAddress(new byte[] {0, 0, 0, 0});
111         } catch (UnknownHostException e) {
112             throw new RuntimeException(e);
113         }
114     }
115 
116     static final int FREE_PORT_MIN = 1024; // ports 1-1023 are reserved
117     static final int PORT_MAX = 0xFFFF; // ports are an unsigned 16-bit integer
118 
119     /* Binder context for this service */
120     private final Context mContext;
121     private final Dependencies mDeps;
122 
123     /**
124      * The next non-repeating global ID for tracking resources between users, this service, and
125      * kernel data structures. Accessing this variable is not thread safe, so it is only read or
126      * modified within blocks synchronized on IpSecService.this. We want to avoid -1
127      * (INVALID_RESOURCE_ID) and 0 (we probably forgot to initialize it).
128      */
129     @GuardedBy("IpSecService.this")
130     private int mNextResourceId = 1;
131 
132     /**
133      * Dependencies of IpSecService, for injection in tests.
134      */
135     @VisibleForTesting
136     public static class Dependencies {
137         /**
138          * Get a reference to INetd.
139          */
getNetdInstance(Context context)140         public INetd getNetdInstance(Context context) throws RemoteException {
141             final INetd netd = INetd.Stub.asInterface((IBinder)
142                     context.getSystemService(Context.NETD_SERVICE));
143             if (netd == null) {
144                 throw new RemoteException("Failed to Get Netd Instance");
145             }
146             return netd;
147         }
148     }
149 
150     final UidFdTagger mUidFdTagger;
151 
152     /**
153      * Interface for user-reference and kernel-resource cleanup.
154      *
155      * <p>This interface must be implemented for a resource to be reference counted.
156      */
157     @VisibleForTesting
158     public interface IResource {
159         /**
160          * Invalidates a IResource object, ensuring it is invalid for the purposes of allocating new
161          * objects dependent on it.
162          *
163          * <p>Implementations of this method are expected to remove references to the IResource
164          * object from the IpSecService's tracking arrays. The removal from the arrays ensures that
165          * the resource is considered invalid for user access or allocation or use in other
166          * resources.
167          *
168          * <p>References to the IResource object may be held by other RefcountedResource objects,
169          * and as such, the underlying resources and quota may not be cleaned up.
170          */
invalidate()171         void invalidate() throws RemoteException;
172 
173         /**
174          * Releases underlying resources and related quotas.
175          *
176          * <p>Implementations of this method are expected to remove all system resources that are
177          * tracked by the IResource object. Due to other RefcountedResource objects potentially
178          * having references to the IResource object, freeUnderlyingResources may not always be
179          * called from releaseIfUnreferencedRecursively().
180          */
freeUnderlyingResources()181         void freeUnderlyingResources() throws RemoteException;
182     }
183 
184     /**
185      * RefcountedResource manages references and dependencies in an exclusively acyclic graph.
186      *
187      * <p>RefcountedResource implements both explicit and implicit resource management. Creating a
188      * RefcountedResource object creates an explicit reference that must be freed by calling
189      * userRelease(). Additionally, adding this object as a child of another RefcountedResource
190      * object will add an implicit reference.
191      *
192      * <p>Resources are cleaned up when all references, both implicit and explicit, are released
193      * (ie, when userRelease() is called and when all parents have called releaseReference() on this
194      * object.)
195      */
196     @VisibleForTesting
197     public class RefcountedResource<T extends IResource> implements IBinder.DeathRecipient {
198         private final T mResource;
199         private final List<RefcountedResource> mChildren;
200         int mRefCount = 1; // starts at 1 for user's reference.
201         IBinder mBinder;
202 
RefcountedResource(T resource, IBinder binder, RefcountedResource... children)203         RefcountedResource(T resource, IBinder binder, RefcountedResource... children) {
204             synchronized (IpSecService.this) {
205                 this.mResource = resource;
206                 this.mChildren = new ArrayList<>(children.length);
207                 this.mBinder = binder;
208 
209                 for (RefcountedResource child : children) {
210                     mChildren.add(child);
211                     child.mRefCount++;
212                 }
213 
214                 try {
215                     mBinder.linkToDeath(this, 0);
216                 } catch (RemoteException e) {
217                     binderDied();
218                     e.rethrowFromSystemServer();
219                 }
220             }
221         }
222 
223         /**
224          * If the Binder object dies, this function is called to free the system resources that are
225          * being tracked by this record and to subsequently release this record for garbage
226          * collection
227          */
228         @Override
binderDied()229         public void binderDied() {
230             synchronized (IpSecService.this) {
231                 try {
232                     userRelease();
233                 } catch (Exception e) {
234                     Log.e(TAG, "Failed to release resource: " + e);
235                 }
236             }
237         }
238 
getResource()239         public T getResource() {
240             return mResource;
241         }
242 
243         /**
244          * Unlinks from binder and performs IpSecService resource cleanup (removes from resource
245          * arrays)
246          *
247          * <p>If this method has been previously called, the RefcountedResource's binder field will
248          * be null, and the method will return without performing the cleanup a second time.
249          *
250          * <p>Note that calling this function does not imply that kernel resources will be freed at
251          * this time, or that the related quota will be returned. Such actions will only be
252          * performed upon the reference count reaching zero.
253          */
254         @GuardedBy("IpSecService.this")
userRelease()255         public void userRelease() throws RemoteException {
256             // Prevent users from putting reference counts into a bad state by calling
257             // userRelease() multiple times.
258             if (mBinder == null) {
259                 return;
260             }
261 
262             mBinder.unlinkToDeath(this, 0);
263             mBinder = null;
264 
265             mResource.invalidate();
266 
267             releaseReference();
268         }
269 
270         /**
271          * Removes a reference to this resource. If the resultant reference count is zero, the
272          * underlying resources are freed, and references to all child resources are also dropped
273          * recursively (resulting in them freeing their resources and children, etcetera)
274          *
275          * <p>This method also sets the reference count to an invalid value (-1) to signify that it
276          * has been fully released. Any subsequent calls to this method will result in an
277          * IllegalStateException being thrown due to resource already having been previously
278          * released
279          */
280         @VisibleForTesting
281         @GuardedBy("IpSecService.this")
releaseReference()282         public void releaseReference() throws RemoteException {
283             mRefCount--;
284 
285             if (mRefCount > 0) {
286                 return;
287             } else if (mRefCount < 0) {
288                 throw new IllegalStateException(
289                         "Invalid operation - resource has already been released.");
290             }
291 
292             // Cleanup own resources
293             mResource.freeUnderlyingResources();
294 
295             // Cleanup child resources as needed
296             for (RefcountedResource<? extends IResource> child : mChildren) {
297                 child.releaseReference();
298             }
299 
300             // Enforce that resource cleanup can only be called once
301             // By decrementing the refcount (from 0 to -1), the next call will throw an
302             // IllegalStateException - it has already been released fully.
303             mRefCount--;
304         }
305 
306         @Override
toString()307         public String toString() {
308             return new StringBuilder()
309                     .append("{mResource=")
310                     .append(mResource)
311                     .append(", mRefCount=")
312                     .append(mRefCount)
313                     .append(", mChildren=")
314                     .append(mChildren)
315                     .append("}")
316                     .toString();
317         }
318     }
319 
320     /**
321      * Very simple counting class that looks much like a counting semaphore
322      *
323      * <p>This class is not thread-safe, and expects that that users of this class will ensure
324      * synchronization and thread safety by holding the IpSecService.this instance lock.
325      */
326     @VisibleForTesting
327     static class ResourceTracker {
328         private final int mMax;
329         int mCurrent;
330 
ResourceTracker(int max)331         ResourceTracker(int max) {
332             mMax = max;
333             mCurrent = 0;
334         }
335 
isAvailable()336         boolean isAvailable() {
337             return (mCurrent < mMax);
338         }
339 
take()340         void take() {
341             if (!isAvailable()) {
342                 Log.wtf(TAG, "Too many resources allocated!");
343             }
344             mCurrent++;
345         }
346 
give()347         void give() {
348             if (mCurrent <= 0) {
349                 Log.wtf(TAG, "We've released this resource too many times");
350             }
351             mCurrent--;
352         }
353 
354         @Override
toString()355         public String toString() {
356             return new StringBuilder()
357                     .append("{mCurrent=")
358                     .append(mCurrent)
359                     .append(", mMax=")
360                     .append(mMax)
361                     .append("}")
362                     .toString();
363         }
364     }
365 
366     @VisibleForTesting
367     static final class UserRecord {
368         /* Maximum number of each type of resource that a single UID may possess */
369 
370         // Up to 4 active VPNs/IWLAN with potential soft handover.
371         public static final int MAX_NUM_TUNNEL_INTERFACES = 8;
372         public static final int MAX_NUM_ENCAP_SOCKETS = 16;
373 
374         // SPIs and Transforms are both cheap, and are 1:1 correlated.
375         public static final int MAX_NUM_TRANSFORMS = 64;
376         public static final int MAX_NUM_SPIS = 64;
377 
378         /**
379          * Store each of the OwnedResource types in an (thinly wrapped) sparse array for indexing
380          * and explicit (user) reference management.
381          *
382          * <p>These are stored in separate arrays to improve debuggability and dump output clarity.
383          *
384          * <p>Resources are removed from this array when the user releases their explicit reference
385          * by calling one of the releaseResource() methods.
386          */
387         final RefcountedResourceArray<SpiRecord> mSpiRecords =
388                 new RefcountedResourceArray<>(SpiRecord.class.getSimpleName());
389         final RefcountedResourceArray<TransformRecord> mTransformRecords =
390                 new RefcountedResourceArray<>(TransformRecord.class.getSimpleName());
391         final RefcountedResourceArray<EncapSocketRecord> mEncapSocketRecords =
392                 new RefcountedResourceArray<>(EncapSocketRecord.class.getSimpleName());
393         final RefcountedResourceArray<TunnelInterfaceRecord> mTunnelInterfaceRecords =
394                 new RefcountedResourceArray<>(TunnelInterfaceRecord.class.getSimpleName());
395 
396         /**
397          * Trackers for quotas for each of the OwnedResource types.
398          *
399          * <p>These trackers are separate from the resource arrays, since they are incremented and
400          * decremented at different points in time. Specifically, quota is only returned upon final
401          * resource deallocation (after all explicit and implicit references are released). Note
402          * that it is possible that calls to releaseResource() will not return the used quota if
403          * there are other resources that depend on (are parents of) the resource being released.
404          */
405         final ResourceTracker mSpiQuotaTracker = new ResourceTracker(MAX_NUM_SPIS);
406         final ResourceTracker mTransformQuotaTracker = new ResourceTracker(MAX_NUM_TRANSFORMS);
407         final ResourceTracker mSocketQuotaTracker = new ResourceTracker(MAX_NUM_ENCAP_SOCKETS);
408         final ResourceTracker mTunnelQuotaTracker = new ResourceTracker(MAX_NUM_TUNNEL_INTERFACES);
409 
removeSpiRecord(int resourceId)410         void removeSpiRecord(int resourceId) {
411             mSpiRecords.remove(resourceId);
412         }
413 
removeTransformRecord(int resourceId)414         void removeTransformRecord(int resourceId) {
415             mTransformRecords.remove(resourceId);
416         }
417 
removeTunnelInterfaceRecord(int resourceId)418         void removeTunnelInterfaceRecord(int resourceId) {
419             mTunnelInterfaceRecords.remove(resourceId);
420         }
421 
removeEncapSocketRecord(int resourceId)422         void removeEncapSocketRecord(int resourceId) {
423             mEncapSocketRecords.remove(resourceId);
424         }
425 
426         @Override
toString()427         public String toString() {
428             return new StringBuilder()
429                     .append("{mSpiQuotaTracker=")
430                     .append(mSpiQuotaTracker)
431                     .append(", mTransformQuotaTracker=")
432                     .append(mTransformQuotaTracker)
433                     .append(", mSocketQuotaTracker=")
434                     .append(mSocketQuotaTracker)
435                     .append(", mTunnelQuotaTracker=")
436                     .append(mTunnelQuotaTracker)
437                     .append(", mSpiRecords=")
438                     .append(mSpiRecords)
439                     .append(", mTransformRecords=")
440                     .append(mTransformRecords)
441                     .append(", mEncapSocketRecords=")
442                     .append(mEncapSocketRecords)
443                     .append(", mTunnelInterfaceRecords=")
444                     .append(mTunnelInterfaceRecords)
445                     .append("}")
446                     .toString();
447         }
448     }
449 
450     /**
451      * This class is not thread-safe, and expects that that users of this class will ensure
452      * synchronization and thread safety by holding the IpSecService.this instance lock.
453      */
454     @VisibleForTesting
455     static final class UserResourceTracker {
456         private final SparseArray<UserRecord> mUserRecords = new SparseArray<>();
457 
458         /** Lazy-initialization/getter that populates or retrieves the UserRecord as needed */
getUserRecord(int uid)459         public UserRecord getUserRecord(int uid) {
460             checkCallerUid(uid);
461 
462             UserRecord r = mUserRecords.get(uid);
463             if (r == null) {
464                 r = new UserRecord();
465                 mUserRecords.put(uid, r);
466             }
467             return r;
468         }
469 
470         /** Safety method; guards against access of other user's UserRecords */
checkCallerUid(int uid)471         private void checkCallerUid(int uid) {
472             if (uid != Binder.getCallingUid() && Process.SYSTEM_UID != Binder.getCallingUid()) {
473                 throw new SecurityException("Attempted access of unowned resources");
474             }
475         }
476 
477         @Override
toString()478         public String toString() {
479             return mUserRecords.toString();
480         }
481     }
482 
483     @VisibleForTesting final UserResourceTracker mUserResourceTracker = new UserResourceTracker();
484 
485     /**
486      * The OwnedResourceRecord class provides a facility to cleanly and reliably track system
487      * resources. It relies on a provided resourceId that should uniquely identify the kernel
488      * resource. To use this class, the user should implement the invalidate() and
489      * freeUnderlyingResources() methods that are responsible for cleaning up IpSecService resource
490      * tracking arrays and kernel resources, respectively.
491      *
492      * <p>This class associates kernel resources with the UID that owns and controls them.
493      */
494     private abstract class OwnedResourceRecord implements IResource {
495         final int mPid;
496         final int mUid;
497         protected final int mResourceId;
498 
OwnedResourceRecord(int resourceId)499         OwnedResourceRecord(int resourceId) {
500             super();
501             if (resourceId == INVALID_RESOURCE_ID) {
502                 throw new IllegalArgumentException("Resource ID must not be INVALID_RESOURCE_ID");
503             }
504             mResourceId = resourceId;
505             mPid = Binder.getCallingPid();
506             mUid = Binder.getCallingUid();
507 
508             getResourceTracker().take();
509         }
510 
511         @Override
invalidate()512         public abstract void invalidate() throws RemoteException;
513 
514         /** Convenience method; retrieves the user resource record for the stored UID. */
getUserRecord()515         protected UserRecord getUserRecord() {
516             return mUserResourceTracker.getUserRecord(mUid);
517         }
518 
519         @Override
freeUnderlyingResources()520         public abstract void freeUnderlyingResources() throws RemoteException;
521 
522         /** Get the resource tracker for this resource */
getResourceTracker()523         protected abstract ResourceTracker getResourceTracker();
524 
525         @Override
toString()526         public String toString() {
527             return new StringBuilder()
528                     .append("{mResourceId=")
529                     .append(mResourceId)
530                     .append(", pid=")
531                     .append(mPid)
532                     .append(", uid=")
533                     .append(mUid)
534                     .append("}")
535                     .toString();
536         }
537     };
538 
539     /**
540      * Thin wrapper over SparseArray to ensure resources exist, and simplify generic typing.
541      *
542      * <p>RefcountedResourceArray prevents null insertions, and throws an IllegalArgumentException
543      * if a key is not found during a retrieval process.
544      */
545     static class RefcountedResourceArray<T extends IResource> {
546         SparseArray<RefcountedResource<T>> mArray = new SparseArray<>();
547         private final String mTypeName;
548 
RefcountedResourceArray(String typeName)549         RefcountedResourceArray(String typeName) {
550             this.mTypeName = typeName;
551         }
552 
553         /**
554          * Accessor method to get inner resource object.
555          *
556          * @throws IllegalArgumentException if no resource with provided key is found.
557          */
getResourceOrThrow(int key)558         T getResourceOrThrow(int key) {
559             return getRefcountedResourceOrThrow(key).getResource();
560         }
561 
562         /**
563          * Accessor method to get reference counting wrapper.
564          *
565          * @throws IllegalArgumentException if no resource with provided key is found.
566          */
getRefcountedResourceOrThrow(int key)567         RefcountedResource<T> getRefcountedResourceOrThrow(int key) {
568             RefcountedResource<T> resource = mArray.get(key);
569             if (resource == null) {
570                 throw new IllegalArgumentException(
571                         String.format("No such %s found for given id: %d", mTypeName, key));
572             }
573 
574             return resource;
575         }
576 
put(int key, RefcountedResource<T> obj)577         void put(int key, RefcountedResource<T> obj) {
578             Objects.requireNonNull(obj, "Null resources cannot be added");
579             mArray.put(key, obj);
580         }
581 
remove(int key)582         void remove(int key) {
583             mArray.remove(key);
584         }
585 
586         @Override
toString()587         public String toString() {
588             return mArray.toString();
589         }
590     }
591 
592     /**
593      * Tracks an SA in the kernel, and manages cleanup paths. Once a TransformRecord is
594      * created, the SpiRecord that originally tracked the SAs will reliquish the
595      * responsibility of freeing the underlying SA to this class via the mOwnedByTransform flag.
596      */
597     private final class TransformRecord extends OwnedResourceRecord {
598         private final IpSecConfig mConfig;
599         private final SpiRecord mSpi;
600         private final EncapSocketRecord mSocket;
601 
TransformRecord( int resourceId, IpSecConfig config, SpiRecord spi, EncapSocketRecord socket)602         TransformRecord(
603                 int resourceId, IpSecConfig config, SpiRecord spi, EncapSocketRecord socket) {
604             super(resourceId);
605             mConfig = config;
606             mSpi = spi;
607             mSocket = socket;
608 
609             spi.setOwnedByTransform();
610         }
611 
getConfig()612         public IpSecConfig getConfig() {
613             return mConfig;
614         }
615 
getSpiRecord()616         public SpiRecord getSpiRecord() {
617             return mSpi;
618         }
619 
getSocketRecord()620         public EncapSocketRecord getSocketRecord() {
621             return mSocket;
622         }
623 
624         /** always guarded by IpSecService#this */
625         @Override
freeUnderlyingResources()626         public void freeUnderlyingResources() {
627             int spi = mSpi.getSpi();
628             try {
629                 mNetd.ipSecDeleteSecurityAssociation(
630                         mUid,
631                         mConfig.getSourceAddress(),
632                         mConfig.getDestinationAddress(),
633                         spi,
634                         mConfig.getMarkValue(),
635                         mConfig.getMarkMask(),
636                         mConfig.getXfrmInterfaceId());
637             } catch (RemoteException | ServiceSpecificException e) {
638                 Log.e(TAG, "Failed to delete SA with ID: " + mResourceId, e);
639             }
640 
641             getResourceTracker().give();
642         }
643 
644         @Override
invalidate()645         public void invalidate() throws RemoteException {
646             getUserRecord().removeTransformRecord(mResourceId);
647         }
648 
649         @Override
getResourceTracker()650         protected ResourceTracker getResourceTracker() {
651             return getUserRecord().mTransformQuotaTracker;
652         }
653 
654         @Override
toString()655         public String toString() {
656             StringBuilder strBuilder = new StringBuilder();
657             strBuilder
658                     .append("{super=")
659                     .append(super.toString())
660                     .append(", mSocket=")
661                     .append(mSocket)
662                     .append(", mSpi.mResourceId=")
663                     .append(mSpi.mResourceId)
664                     .append(", mConfig=")
665                     .append(mConfig)
666                     .append("}");
667             return strBuilder.toString();
668         }
669     }
670 
671     /**
672      * Tracks a single SA in the kernel, and manages cleanup paths. Once used in a Transform, the
673      * responsibility for cleaning up underlying resources will be passed to the TransformRecord
674      * object
675      */
676     private final class SpiRecord extends OwnedResourceRecord {
677         private final String mSourceAddress;
678         private final String mDestinationAddress;
679         private int mSpi;
680 
681         private boolean mOwnedByTransform = false;
682 
SpiRecord(int resourceId, String sourceAddress, String destinationAddress, int spi)683         SpiRecord(int resourceId, String sourceAddress,
684                 String destinationAddress, int spi) {
685             super(resourceId);
686             mSourceAddress = sourceAddress;
687             mDestinationAddress = destinationAddress;
688             mSpi = spi;
689         }
690 
691         /** always guarded by IpSecService#this */
692         @Override
freeUnderlyingResources()693         public void freeUnderlyingResources() {
694             try {
695                 if (!mOwnedByTransform) {
696                     mNetd.ipSecDeleteSecurityAssociation(
697                             mUid, mSourceAddress, mDestinationAddress, mSpi, 0 /* mark */,
698                             0 /* mask */, 0 /* if_id */);
699                 }
700             } catch (ServiceSpecificException | RemoteException e) {
701                 Log.e(TAG, "Failed to delete SPI reservation with ID: " + mResourceId, e);
702             }
703 
704             mSpi = IpSecManager.INVALID_SECURITY_PARAMETER_INDEX;
705 
706             getResourceTracker().give();
707         }
708 
getSpi()709         public int getSpi() {
710             return mSpi;
711         }
712 
getDestinationAddress()713         public String getDestinationAddress() {
714             return mDestinationAddress;
715         }
716 
setOwnedByTransform()717         public void setOwnedByTransform() {
718             if (mOwnedByTransform) {
719                 // Programming error
720                 throw new IllegalStateException("Cannot own an SPI twice!");
721             }
722 
723             mOwnedByTransform = true;
724         }
725 
getOwnedByTransform()726         public boolean getOwnedByTransform() {
727             return mOwnedByTransform;
728         }
729 
730         @Override
invalidate()731         public void invalidate() throws RemoteException {
732             getUserRecord().removeSpiRecord(mResourceId);
733         }
734 
735         @Override
getResourceTracker()736         protected ResourceTracker getResourceTracker() {
737             return getUserRecord().mSpiQuotaTracker;
738         }
739 
740         @Override
toString()741         public String toString() {
742             StringBuilder strBuilder = new StringBuilder();
743             strBuilder
744                     .append("{super=")
745                     .append(super.toString())
746                     .append(", mSpi=")
747                     .append(mSpi)
748                     .append(", mSourceAddress=")
749                     .append(mSourceAddress)
750                     .append(", mDestinationAddress=")
751                     .append(mDestinationAddress)
752                     .append(", mOwnedByTransform=")
753                     .append(mOwnedByTransform)
754                     .append("}");
755             return strBuilder.toString();
756         }
757     }
758 
759     private final SparseBooleanArray mTunnelNetIds = new SparseBooleanArray();
760     final Range<Integer> mNetIdRange = ConnectivityManager.getIpSecNetIdRange();
761     private int mNextTunnelNetId = mNetIdRange.getLower();
762 
763     /**
764      * Reserves a netId within the range of netIds allocated for IPsec tunnel interfaces
765      *
766      * <p>This method should only be called from Binder threads. Do not call this from within the
767      * system server as it will crash the system on failure.
768      *
769      * @return an integer key within the netId range, if successful
770      * @throws IllegalStateException if unsuccessful (all netId are currently reserved)
771      */
772     @VisibleForTesting
reserveNetId()773     int reserveNetId() {
774         final int range = mNetIdRange.getUpper() - mNetIdRange.getLower() + 1;
775         synchronized (mTunnelNetIds) {
776             for (int i = 0; i < range; i++) {
777                 final int netId = mNextTunnelNetId;
778                 if (++mNextTunnelNetId > mNetIdRange.getUpper()) {
779                     mNextTunnelNetId = mNetIdRange.getLower();
780                 }
781                 if (!mTunnelNetIds.get(netId)) {
782                     mTunnelNetIds.put(netId, true);
783                     return netId;
784                 }
785             }
786         }
787         throw new IllegalStateException("No free netIds to allocate");
788     }
789 
790     @VisibleForTesting
releaseNetId(int netId)791     void releaseNetId(int netId) {
792         synchronized (mTunnelNetIds) {
793             mTunnelNetIds.delete(netId);
794         }
795     }
796 
797     /**
798      * Tracks an tunnel interface, and manages cleanup paths.
799      *
800      * <p>This class is not thread-safe, and expects that that users of this class will ensure
801      * synchronization and thread safety by holding the IpSecService.this instance lock
802      */
803     @VisibleForTesting
804     final class TunnelInterfaceRecord extends OwnedResourceRecord {
805         private final String mInterfaceName;
806 
807         // outer addresses
808         private final String mLocalAddress;
809         private final String mRemoteAddress;
810 
811         private final int mIkey;
812         private final int mOkey;
813 
814         private final int mIfId;
815 
816         private Network mUnderlyingNetwork;
817 
TunnelInterfaceRecord( int resourceId, String interfaceName, Network underlyingNetwork, String localAddr, String remoteAddr, int ikey, int okey, int intfId)818         TunnelInterfaceRecord(
819                 int resourceId,
820                 String interfaceName,
821                 Network underlyingNetwork,
822                 String localAddr,
823                 String remoteAddr,
824                 int ikey,
825                 int okey,
826                 int intfId) {
827             super(resourceId);
828 
829             mInterfaceName = interfaceName;
830             mUnderlyingNetwork = underlyingNetwork;
831             mLocalAddress = localAddr;
832             mRemoteAddress = remoteAddr;
833             mIkey = ikey;
834             mOkey = okey;
835             mIfId = intfId;
836         }
837 
838         /** always guarded by IpSecService#this */
839         @Override
freeUnderlyingResources()840         public void freeUnderlyingResources() {
841             // Calls to netd
842             //       Teardown VTI
843             //       Delete global policies
844             try {
845                 mNetd.ipSecRemoveTunnelInterface(mInterfaceName);
846 
847                 for (int selAddrFamily : ADDRESS_FAMILIES) {
848                     mNetd.ipSecDeleteSecurityPolicy(
849                             mUid,
850                             selAddrFamily,
851                             IpSecManager.DIRECTION_OUT,
852                             mOkey,
853                             0xffffffff,
854                             mIfId);
855                     mNetd.ipSecDeleteSecurityPolicy(
856                             mUid,
857                             selAddrFamily,
858                             IpSecManager.DIRECTION_IN,
859                             mIkey,
860                             0xffffffff,
861                             mIfId);
862                 }
863             } catch (ServiceSpecificException | RemoteException e) {
864                 Log.e(
865                         TAG,
866                         "Failed to delete VTI with interface name: "
867                                 + mInterfaceName
868                                 + " and id: "
869                                 + mResourceId, e);
870             }
871 
872             getResourceTracker().give();
873             releaseNetId(mIkey);
874             releaseNetId(mOkey);
875         }
876 
877         @GuardedBy("IpSecService.this")
setUnderlyingNetwork(Network underlyingNetwork)878         public void setUnderlyingNetwork(Network underlyingNetwork) {
879             // When #applyTunnelModeTransform is called, this new underlying network will be used to
880             // update the output mark of the input transform.
881             mUnderlyingNetwork = underlyingNetwork;
882         }
883 
884         @GuardedBy("IpSecService.this")
getUnderlyingNetwork()885         public Network getUnderlyingNetwork() {
886             return mUnderlyingNetwork;
887         }
888 
getInterfaceName()889         public String getInterfaceName() {
890             return mInterfaceName;
891         }
892 
893         /** Returns the local, outer address for the tunnelInterface */
getLocalAddress()894         public String getLocalAddress() {
895             return mLocalAddress;
896         }
897 
898         /** Returns the remote, outer address for the tunnelInterface */
getRemoteAddress()899         public String getRemoteAddress() {
900             return mRemoteAddress;
901         }
902 
getIkey()903         public int getIkey() {
904             return mIkey;
905         }
906 
getOkey()907         public int getOkey() {
908             return mOkey;
909         }
910 
getIfId()911         public int getIfId() {
912             return mIfId;
913         }
914 
915         @Override
getResourceTracker()916         protected ResourceTracker getResourceTracker() {
917             return getUserRecord().mTunnelQuotaTracker;
918         }
919 
920         @Override
invalidate()921         public void invalidate() {
922             getUserRecord().removeTunnelInterfaceRecord(mResourceId);
923         }
924 
925         @Override
toString()926         public String toString() {
927             return new StringBuilder()
928                     .append("{super=")
929                     .append(super.toString())
930                     .append(", mInterfaceName=")
931                     .append(mInterfaceName)
932                     .append(", mUnderlyingNetwork=")
933                     .append(mUnderlyingNetwork)
934                     .append(", mLocalAddress=")
935                     .append(mLocalAddress)
936                     .append(", mRemoteAddress=")
937                     .append(mRemoteAddress)
938                     .append(", mIkey=")
939                     .append(mIkey)
940                     .append(", mOkey=")
941                     .append(mOkey)
942                     .append("}")
943                     .toString();
944         }
945     }
946 
947     /**
948      * Tracks a UDP encap socket, and manages cleanup paths
949      *
950      * <p>While this class does not manage non-kernel resources, race conditions around socket
951      * binding require that the service creates the encap socket, binds it and applies the socket
952      * policy before handing it to a user.
953      */
954     private final class EncapSocketRecord extends OwnedResourceRecord {
955         private FileDescriptor mSocket;
956         private final int mPort;
957 
EncapSocketRecord(int resourceId, FileDescriptor socket, int port)958         EncapSocketRecord(int resourceId, FileDescriptor socket, int port) {
959             super(resourceId);
960             mSocket = socket;
961             mPort = port;
962         }
963 
964         /** always guarded by IpSecService#this */
965         @Override
freeUnderlyingResources()966         public void freeUnderlyingResources() {
967             Log.d(TAG, "Closing port " + mPort);
968             IoUtils.closeQuietly(mSocket);
969             mSocket = null;
970 
971             getResourceTracker().give();
972         }
973 
getPort()974         public int getPort() {
975             return mPort;
976         }
977 
getFileDescriptor()978         public FileDescriptor getFileDescriptor() {
979             return mSocket;
980         }
981 
982         @Override
getResourceTracker()983         protected ResourceTracker getResourceTracker() {
984             return getUserRecord().mSocketQuotaTracker;
985         }
986 
987         @Override
invalidate()988         public void invalidate() {
989             getUserRecord().removeEncapSocketRecord(mResourceId);
990         }
991 
992         @Override
toString()993         public String toString() {
994             return new StringBuilder()
995                     .append("{super=")
996                     .append(super.toString())
997                     .append(", mSocket=")
998                     .append(mSocket)
999                     .append(", mPort=")
1000                     .append(mPort)
1001                     .append("}")
1002                     .toString();
1003         }
1004     }
1005 
1006     /**
1007      * Constructs a new IpSecService instance
1008      *
1009      * @param context Binder context for this service
1010      */
IpSecService(Context context)1011     public IpSecService(Context context) {
1012         this(context, new Dependencies());
1013     }
1014 
1015     @NonNull
getAppOpsManager()1016     private AppOpsManager getAppOpsManager() {
1017         AppOpsManager appOps = (AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE);
1018         if (appOps == null) throw new RuntimeException("System Server couldn't get AppOps");
1019         return appOps;
1020     }
1021 
1022     /** @hide */
1023     @VisibleForTesting
IpSecService(Context context, Dependencies deps)1024     public IpSecService(Context context, Dependencies deps) {
1025         this(
1026                 context,
1027                 deps,
1028                 (fd, uid) -> {
1029                     try {
1030                         TrafficStats.setThreadStatsUid(uid);
1031                         TrafficStats.tagFileDescriptor(fd);
1032                     } finally {
1033                         TrafficStats.clearThreadStatsUid();
1034                     }
1035                 });
1036     }
1037 
1038     /** @hide */
1039     @VisibleForTesting
IpSecService(Context context, Dependencies deps, UidFdTagger uidFdTagger)1040     public IpSecService(Context context, Dependencies deps, UidFdTagger uidFdTagger) {
1041         mContext = context;
1042         mDeps = Objects.requireNonNull(deps, "Missing dependencies.");
1043         mUidFdTagger = uidFdTagger;
1044         try {
1045             mNetd = mDeps.getNetdInstance(mContext);
1046         } catch (RemoteException e) {
1047             throw e.rethrowFromSystemServer();
1048         }
1049     }
1050 
1051     /**
1052      * Checks that the provided InetAddress is valid for use in an IPsec SA. The address must not be
1053      * a wildcard address and must be in a numeric form such as 1.2.3.4 or 2001::1.
1054      */
checkInetAddress(String inetAddress)1055     private static void checkInetAddress(String inetAddress) {
1056         if (TextUtils.isEmpty(inetAddress)) {
1057             throw new IllegalArgumentException("Unspecified address");
1058         }
1059 
1060         InetAddress checkAddr = InetAddresses.parseNumericAddress(inetAddress);
1061 
1062         if (checkAddr.isAnyLocalAddress()) {
1063             throw new IllegalArgumentException("Inappropriate wildcard address: " + inetAddress);
1064         }
1065     }
1066 
1067     /**
1068      * Checks the user-provided direction field and throws an IllegalArgumentException if it is not
1069      * DIRECTION_IN or DIRECTION_OUT
1070      */
checkDirection(int direction)1071     private void checkDirection(int direction) {
1072         switch (direction) {
1073             case IpSecManager.DIRECTION_OUT:
1074             case IpSecManager.DIRECTION_IN:
1075                 return;
1076             case IpSecManager.DIRECTION_FWD:
1077                 // Only NETWORK_STACK or MAINLINE_NETWORK_STACK allowed to use forward policies
1078                 PermissionUtils.enforceNetworkStackPermission(mContext);
1079                 return;
1080         }
1081         throw new IllegalArgumentException("Invalid Direction: " + direction);
1082     }
1083 
1084     /** Get a new SPI and maintain the reservation in the system server */
1085     @Override
allocateSecurityParameterIndex( String destinationAddress, int requestedSpi, IBinder binder)1086     public synchronized IpSecSpiResponse allocateSecurityParameterIndex(
1087             String destinationAddress, int requestedSpi, IBinder binder) throws RemoteException {
1088         checkInetAddress(destinationAddress);
1089         // RFC 4303 Section 2.1 - 0=local, 1-255=reserved.
1090         if (requestedSpi > 0 && requestedSpi < 256) {
1091             throw new IllegalArgumentException("ESP SPI must not be in the range of 0-255.");
1092         }
1093         Objects.requireNonNull(binder, "Null Binder passed to allocateSecurityParameterIndex");
1094 
1095         int callingUid = Binder.getCallingUid();
1096         UserRecord userRecord = mUserResourceTracker.getUserRecord(callingUid);
1097         final int resourceId = mNextResourceId++;
1098 
1099         int spi = IpSecManager.INVALID_SECURITY_PARAMETER_INDEX;
1100         try {
1101             if (!userRecord.mSpiQuotaTracker.isAvailable()) {
1102                 return new IpSecSpiResponse(
1103                         IpSecManager.Status.RESOURCE_UNAVAILABLE, INVALID_RESOURCE_ID, spi);
1104             }
1105 
1106             spi = mNetd.ipSecAllocateSpi(callingUid, "", destinationAddress, requestedSpi);
1107             Log.d(TAG, "Allocated SPI " + spi);
1108             userRecord.mSpiRecords.put(
1109                     resourceId,
1110                     new RefcountedResource<SpiRecord>(
1111                             new SpiRecord(resourceId, "",
1112                             destinationAddress, spi), binder));
1113         } catch (ServiceSpecificException e) {
1114             if (e.errorCode == OsConstants.ENOENT) {
1115                 return new IpSecSpiResponse(
1116                         IpSecManager.Status.SPI_UNAVAILABLE, INVALID_RESOURCE_ID, spi);
1117             }
1118             throw e;
1119         } catch (RemoteException e) {
1120             throw e.rethrowFromSystemServer();
1121         }
1122         return new IpSecSpiResponse(IpSecManager.Status.OK, resourceId, spi);
1123     }
1124 
1125     /* This method should only be called from Binder threads. Do not call this from
1126      * within the system server as it will crash the system on failure.
1127      */
releaseResource(RefcountedResourceArray resArray, int resourceId)1128     private void releaseResource(RefcountedResourceArray resArray, int resourceId)
1129             throws RemoteException {
1130         resArray.getRefcountedResourceOrThrow(resourceId).userRelease();
1131     }
1132 
1133     /** Release a previously allocated SPI that has been registered with the system server */
1134     @Override
releaseSecurityParameterIndex(int resourceId)1135     public synchronized void releaseSecurityParameterIndex(int resourceId) throws RemoteException {
1136         UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid());
1137         releaseResource(userRecord.mSpiRecords, resourceId);
1138     }
1139 
1140     /**
1141      * This function finds and forcibly binds to a random system port, ensuring that the port cannot
1142      * be unbound.
1143      *
1144      * <p>A socket cannot be un-bound from a port if it was bound to that port by number. To select
1145      * a random open port and then bind by number, this function creates a temp socket, binds to a
1146      * random port (specifying 0), gets that port number, and then uses is to bind the user's UDP
1147      * Encapsulation Socket forcibly, so that it cannot be un-bound by the user with the returned
1148      * FileHandle.
1149      *
1150      * <p>The loop in this function handles the inherent race window between un-binding to a port
1151      * and re-binding, during which the system could *technically* hand that port out to someone
1152      * else.
1153      */
bindToRandomPort(FileDescriptor sockFd)1154     private int bindToRandomPort(FileDescriptor sockFd) throws IOException {
1155         for (int i = MAX_PORT_BIND_ATTEMPTS; i > 0; i--) {
1156             try {
1157                 FileDescriptor probeSocket = Os.socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
1158                 Os.bind(probeSocket, INADDR_ANY, 0);
1159                 int port = ((InetSocketAddress) Os.getsockname(probeSocket)).getPort();
1160                 Os.close(probeSocket);
1161                 Log.v(TAG, "Binding to port " + port);
1162                 Os.bind(sockFd, INADDR_ANY, port);
1163                 return port;
1164             } catch (ErrnoException e) {
1165                 // Someone miraculously claimed the port just after we closed probeSocket.
1166                 if (e.errno == OsConstants.EADDRINUSE) {
1167                     continue;
1168                 }
1169                 throw e.rethrowAsIOException();
1170             }
1171         }
1172         throw new IOException("Failed " + MAX_PORT_BIND_ATTEMPTS + " attempts to bind to a port");
1173     }
1174 
1175     /**
1176      * Functional interface to do traffic tagging of given sockets to UIDs.
1177      *
1178      * <p>Specifically used by openUdpEncapsulationSocket to ensure data usage on the UDP encap
1179      * sockets are billed to the UID that the UDP encap socket was created on behalf of.
1180      *
1181      * <p>Separate class so that the socket tagging logic can be mocked; TrafficStats uses static
1182      * methods that cannot be easily mocked/tested.
1183      */
1184     @VisibleForTesting
1185     public interface UidFdTagger {
1186         /**
1187          * Sets socket tag to assign all traffic to the provided UID.
1188          *
1189          * <p>Since the socket is created on behalf of an unprivileged application, all traffic
1190          * should be accounted to the UID of the unprivileged application.
1191          */
tag(FileDescriptor fd, int uid)1192         void tag(FileDescriptor fd, int uid) throws IOException;
1193     }
1194 
1195     /**
1196      * Open a socket via the system server and bind it to the specified port (random if port=0).
1197      * This will return a PFD to the user that represent a bound UDP socket. The system server will
1198      * cache the socket and a record of its owner so that it can and must be freed when no longer
1199      * needed.
1200      */
1201     @Override
openUdpEncapsulationSocket(int port, IBinder binder)1202     public synchronized IpSecUdpEncapResponse openUdpEncapsulationSocket(int port, IBinder binder)
1203             throws RemoteException {
1204         if (port != 0 && (port < FREE_PORT_MIN || port > PORT_MAX)) {
1205             throw new IllegalArgumentException(
1206                     "Specified port number must be a valid non-reserved UDP port");
1207         }
1208         Objects.requireNonNull(binder, "Null Binder passed to openUdpEncapsulationSocket");
1209 
1210         int callingUid = Binder.getCallingUid();
1211         UserRecord userRecord = mUserResourceTracker.getUserRecord(callingUid);
1212         final int resourceId = mNextResourceId++;
1213 
1214         ParcelFileDescriptor pFd = null;
1215         try {
1216             if (!userRecord.mSocketQuotaTracker.isAvailable()) {
1217                 return new IpSecUdpEncapResponse(IpSecManager.Status.RESOURCE_UNAVAILABLE);
1218             }
1219 
1220             FileDescriptor sockFd = null;
1221             try {
1222                 sockFd = Os.socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
1223                 pFd = ParcelFileDescriptor.dup(sockFd);
1224             } finally {
1225                 IoUtils.closeQuietly(sockFd);
1226             }
1227 
1228             mUidFdTagger.tag(pFd.getFileDescriptor(), callingUid);
1229             // This code is common to both the unspecified and specified port cases
1230             Os.setsockoptInt(
1231                     pFd.getFileDescriptor(),
1232                     OsConstants.IPPROTO_UDP,
1233                     OsConstants.UDP_ENCAP,
1234                     OsConstants.UDP_ENCAP_ESPINUDP);
1235 
1236             mNetd.ipSecSetEncapSocketOwner(pFd, callingUid);
1237             if (port != 0) {
1238                 Log.v(TAG, "Binding to port " + port);
1239                 Os.bind(pFd.getFileDescriptor(), INADDR_ANY, port);
1240             } else {
1241                 port = bindToRandomPort(pFd.getFileDescriptor());
1242             }
1243 
1244             userRecord.mEncapSocketRecords.put(
1245                     resourceId,
1246                     new RefcountedResource<EncapSocketRecord>(
1247                             new EncapSocketRecord(resourceId, pFd.getFileDescriptor(), port),
1248                             binder));
1249             return new IpSecUdpEncapResponse(IpSecManager.Status.OK, resourceId, port,
1250                     pFd.getFileDescriptor());
1251         } catch (IOException | ErrnoException e) {
1252             try {
1253                 if (pFd != null) {
1254                     pFd.close();
1255                 }
1256             } catch (IOException ex) {
1257                 // Nothing can be done at this point
1258                 Log.e(TAG, "Failed to close pFd.");
1259             }
1260         }
1261         // If we make it to here, then something has gone wrong and we couldn't open a socket.
1262         // The only reasonable condition that would cause that is resource unavailable.
1263         return new IpSecUdpEncapResponse(IpSecManager.Status.RESOURCE_UNAVAILABLE);
1264     }
1265 
1266     /** close a socket that has been been allocated by and registered with the system server */
1267     @Override
closeUdpEncapsulationSocket(int resourceId)1268     public synchronized void closeUdpEncapsulationSocket(int resourceId) throws RemoteException {
1269         UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid());
1270         releaseResource(userRecord.mEncapSocketRecords, resourceId);
1271     }
1272 
1273     /**
1274      * Create a tunnel interface for use in IPSec tunnel mode. The system server will cache the
1275      * tunnel interface and a record of its owner so that it can and must be freed when no longer
1276      * needed.
1277      */
1278     @Override
createTunnelInterface( String localAddr, String remoteAddr, Network underlyingNetwork, IBinder binder, String callingPackage)1279     public synchronized IpSecTunnelInterfaceResponse createTunnelInterface(
1280             String localAddr, String remoteAddr, Network underlyingNetwork, IBinder binder,
1281             String callingPackage) {
1282         enforceTunnelFeatureAndPermissions(callingPackage);
1283         Objects.requireNonNull(binder, "Null Binder passed to createTunnelInterface");
1284         Objects.requireNonNull(underlyingNetwork, "No underlying network was specified");
1285         checkInetAddress(localAddr);
1286         checkInetAddress(remoteAddr);
1287 
1288         // TODO: Check that underlying network exists, and IP addresses not assigned to a different
1289         //       network (b/72316676).
1290 
1291         int callerUid = Binder.getCallingUid();
1292         UserRecord userRecord = mUserResourceTracker.getUserRecord(callerUid);
1293         if (!userRecord.mTunnelQuotaTracker.isAvailable()) {
1294             return new IpSecTunnelInterfaceResponse(IpSecManager.Status.RESOURCE_UNAVAILABLE);
1295         }
1296 
1297         final int resourceId = mNextResourceId++;
1298         final int ikey = reserveNetId();
1299         final int okey = reserveNetId();
1300         String intfName = String.format("%s%d", INetd.IPSEC_INTERFACE_PREFIX, resourceId);
1301 
1302         try {
1303             // Calls to netd:
1304             //       Create VTI
1305             //       Add inbound/outbound global policies
1306             //              (use reqid = 0)
1307             mNetd.ipSecAddTunnelInterface(intfName, localAddr, remoteAddr, ikey, okey, resourceId);
1308 
1309             BinderUtils.withCleanCallingIdentity(() -> {
1310                 NetdUtils.setInterfaceUp(mNetd, intfName);
1311             });
1312 
1313             for (int selAddrFamily : ADDRESS_FAMILIES) {
1314                 // Always send down correct local/remote addresses for template.
1315                 mNetd.ipSecAddSecurityPolicy(
1316                         callerUid,
1317                         selAddrFamily,
1318                         IpSecManager.DIRECTION_OUT,
1319                         localAddr,
1320                         remoteAddr,
1321                         0,
1322                         okey,
1323                         0xffffffff,
1324                         resourceId);
1325                 mNetd.ipSecAddSecurityPolicy(
1326                         callerUid,
1327                         selAddrFamily,
1328                         IpSecManager.DIRECTION_IN,
1329                         remoteAddr,
1330                         localAddr,
1331                         0,
1332                         ikey,
1333                         0xffffffff,
1334                         resourceId);
1335 
1336                 // Add a forwarding policy on the tunnel interface. In order to support forwarding
1337                 // the IpSecTunnelInterface must have a forwarding policy matching the incoming SA.
1338                 //
1339                 // Unless a IpSecTransform is also applied against this interface in DIRECTION_FWD,
1340                 // forwarding will be blocked by default (as would be the case if this policy was
1341                 // absent).
1342                 //
1343                 // This is necessary only on the tunnel interface, and not any the interface to
1344                 // which traffic will be forwarded to.
1345                 mNetd.ipSecAddSecurityPolicy(
1346                         callerUid,
1347                         selAddrFamily,
1348                         IpSecManager.DIRECTION_FWD,
1349                         remoteAddr,
1350                         localAddr,
1351                         0,
1352                         ikey,
1353                         0xffffffff,
1354                         resourceId);
1355             }
1356 
1357             userRecord.mTunnelInterfaceRecords.put(
1358                     resourceId,
1359                     new RefcountedResource<TunnelInterfaceRecord>(
1360                             new TunnelInterfaceRecord(
1361                                     resourceId,
1362                                     intfName,
1363                                     underlyingNetwork,
1364                                     localAddr,
1365                                     remoteAddr,
1366                                     ikey,
1367                                     okey,
1368                                     resourceId),
1369                             binder));
1370             return new IpSecTunnelInterfaceResponse(IpSecManager.Status.OK, resourceId, intfName);
1371         } catch (RemoteException e) {
1372             // Release keys if we got an error.
1373             releaseNetId(ikey);
1374             releaseNetId(okey);
1375             throw e.rethrowFromSystemServer();
1376         } catch (Throwable t) {
1377             // Release keys if we got an error.
1378             releaseNetId(ikey);
1379             releaseNetId(okey);
1380             throw t;
1381         }
1382     }
1383 
1384     /**
1385      * Adds a new local address to the tunnel interface. This allows packets to be sent and received
1386      * from multiple local IP addresses over the same tunnel.
1387      */
1388     @Override
addAddressToTunnelInterface( int tunnelResourceId, LinkAddress localAddr, String callingPackage)1389     public synchronized void addAddressToTunnelInterface(
1390             int tunnelResourceId, LinkAddress localAddr, String callingPackage) {
1391         enforceTunnelFeatureAndPermissions(callingPackage);
1392         UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid());
1393 
1394         // Get tunnelInterface record; if no such interface is found, will throw
1395         // IllegalArgumentException
1396         TunnelInterfaceRecord tunnelInterfaceInfo =
1397                 userRecord.mTunnelInterfaceRecords.getResourceOrThrow(tunnelResourceId);
1398 
1399         try {
1400             // We can assume general validity of the IP address, since we get them as a
1401             // LinkAddress, which does some validation.
1402             mNetd.interfaceAddAddress(
1403                     tunnelInterfaceInfo.mInterfaceName,
1404                     localAddr.getAddress().getHostAddress(),
1405                     localAddr.getPrefixLength());
1406         } catch (RemoteException e) {
1407             throw e.rethrowFromSystemServer();
1408         }
1409     }
1410 
1411     /**
1412      * Remove a new local address from the tunnel interface. After removal, the address will no
1413      * longer be available to send from, or receive on.
1414      */
1415     @Override
removeAddressFromTunnelInterface( int tunnelResourceId, LinkAddress localAddr, String callingPackage)1416     public synchronized void removeAddressFromTunnelInterface(
1417             int tunnelResourceId, LinkAddress localAddr, String callingPackage) {
1418         enforceTunnelFeatureAndPermissions(callingPackage);
1419 
1420         UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid());
1421         // Get tunnelInterface record; if no such interface is found, will throw
1422         // IllegalArgumentException
1423         TunnelInterfaceRecord tunnelInterfaceInfo =
1424                 userRecord.mTunnelInterfaceRecords.getResourceOrThrow(tunnelResourceId);
1425 
1426         try {
1427             // We can assume general validity of the IP address, since we get them as a
1428             // LinkAddress, which does some validation.
1429             mNetd.interfaceDelAddress(
1430                             tunnelInterfaceInfo.mInterfaceName,
1431                             localAddr.getAddress().getHostAddress(),
1432                             localAddr.getPrefixLength());
1433         } catch (RemoteException e) {
1434             throw e.rethrowFromSystemServer();
1435         }
1436     }
1437 
1438     /** Set TunnelInterface to use a specific underlying network. */
1439     @Override
setNetworkForTunnelInterface( int tunnelResourceId, Network underlyingNetwork, String callingPackage)1440     public synchronized void setNetworkForTunnelInterface(
1441             int tunnelResourceId, Network underlyingNetwork, String callingPackage) {
1442         enforceTunnelFeatureAndPermissions(callingPackage);
1443         Objects.requireNonNull(underlyingNetwork, "No underlying network was specified");
1444 
1445         final UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid());
1446 
1447         // Get tunnelInterface record; if no such interface is found, will throw
1448         // IllegalArgumentException. userRecord.mTunnelInterfaceRecords is never null
1449         final TunnelInterfaceRecord tunnelInterfaceInfo =
1450                 userRecord.mTunnelInterfaceRecords.getResourceOrThrow(tunnelResourceId);
1451 
1452         final ConnectivityManager connectivityManager =
1453                 mContext.getSystemService(ConnectivityManager.class);
1454         final LinkProperties lp = connectivityManager.getLinkProperties(underlyingNetwork);
1455         if (tunnelInterfaceInfo.getInterfaceName().equals(lp.getInterfaceName())) {
1456             throw new IllegalArgumentException(
1457                     "Underlying network cannot be the network being exposed by this tunnel");
1458         }
1459 
1460         // It is meaningless to check if the network exists or is valid because the network might
1461         // disconnect at any time after it passes the check.
1462 
1463         tunnelInterfaceInfo.setUnderlyingNetwork(underlyingNetwork);
1464     }
1465 
1466     /**
1467      * Delete a TunnelInterface that has been been allocated by and registered with the system
1468      * server
1469      */
1470     @Override
deleteTunnelInterface( int resourceId, String callingPackage)1471     public synchronized void deleteTunnelInterface(
1472             int resourceId, String callingPackage) throws RemoteException {
1473         enforceTunnelFeatureAndPermissions(callingPackage);
1474         UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid());
1475         releaseResource(userRecord.mTunnelInterfaceRecords, resourceId);
1476     }
1477 
1478     @VisibleForTesting
validateAlgorithms(IpSecConfig config)1479     void validateAlgorithms(IpSecConfig config) throws IllegalArgumentException {
1480         IpSecAlgorithm auth = config.getAuthentication();
1481         IpSecAlgorithm crypt = config.getEncryption();
1482         IpSecAlgorithm aead = config.getAuthenticatedEncryption();
1483 
1484         // Validate the algorithm set
1485         Preconditions.checkArgument(
1486                 aead != null || crypt != null || auth != null,
1487                 "No Encryption or Authentication algorithms specified");
1488         Preconditions.checkArgument(
1489                 auth == null || auth.isAuthentication(),
1490                 "Unsupported algorithm for Authentication");
1491         Preconditions.checkArgument(
1492                 crypt == null || crypt.isEncryption(), "Unsupported algorithm for Encryption");
1493         Preconditions.checkArgument(
1494                 aead == null || aead.isAead(),
1495                 "Unsupported algorithm for Authenticated Encryption");
1496         Preconditions.checkArgument(
1497                 aead == null || (auth == null && crypt == null),
1498                 "Authenticated Encryption is mutually exclusive with other Authentication "
1499                         + "or Encryption algorithms");
1500     }
1501 
getFamily(String inetAddress)1502     private int getFamily(String inetAddress) {
1503         int family = AF_UNSPEC;
1504         InetAddress checkAddress = InetAddresses.parseNumericAddress(inetAddress);
1505         if (checkAddress instanceof Inet4Address) {
1506             family = AF_INET;
1507         } else if (checkAddress instanceof Inet6Address) {
1508             family = AF_INET6;
1509         }
1510         return family;
1511     }
1512 
1513     /**
1514      * Checks an IpSecConfig parcel to ensure that the contents are valid and throws an
1515      * IllegalArgumentException if they are not.
1516      */
checkIpSecConfig(IpSecConfig config)1517     private void checkIpSecConfig(IpSecConfig config) {
1518         UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid());
1519 
1520         switch (config.getEncapType()) {
1521             case IpSecTransform.ENCAP_NONE:
1522                 break;
1523             case IpSecTransform.ENCAP_ESPINUDP:
1524             case IpSecTransform.ENCAP_ESPINUDP_NON_IKE:
1525                 // Retrieve encap socket record; will throw IllegalArgumentException if not found
1526                 userRecord.mEncapSocketRecords.getResourceOrThrow(
1527                         config.getEncapSocketResourceId());
1528 
1529                 int port = config.getEncapRemotePort();
1530                 if (port <= 0 || port > 0xFFFF) {
1531                     throw new IllegalArgumentException("Invalid remote UDP port: " + port);
1532                 }
1533                 break;
1534             default:
1535                 throw new IllegalArgumentException("Invalid Encap Type: " + config.getEncapType());
1536         }
1537 
1538         validateAlgorithms(config);
1539 
1540         // Retrieve SPI record; will throw IllegalArgumentException if not found
1541         SpiRecord s = userRecord.mSpiRecords.getResourceOrThrow(config.getSpiResourceId());
1542 
1543         // Check to ensure that SPI has not already been used.
1544         if (s.getOwnedByTransform()) {
1545             throw new IllegalStateException("SPI already in use; cannot be used in new Transforms");
1546         }
1547 
1548         // If no remote address is supplied, then use one from the SPI.
1549         if (TextUtils.isEmpty(config.getDestinationAddress())) {
1550             config.setDestinationAddress(s.getDestinationAddress());
1551         }
1552 
1553         // All remote addresses must match
1554         if (!config.getDestinationAddress().equals(s.getDestinationAddress())) {
1555             throw new IllegalArgumentException("Mismatched remote addresseses.");
1556         }
1557 
1558         // This check is technically redundant due to the chain of custody between the SPI and
1559         // the IpSecConfig, but in the future if the dest is allowed to be set explicitly in
1560         // the transform, this will prevent us from messing up.
1561         checkInetAddress(config.getDestinationAddress());
1562 
1563         // Require a valid source address for all transforms.
1564         checkInetAddress(config.getSourceAddress());
1565 
1566         // Check to ensure source and destination have the same address family.
1567         String sourceAddress = config.getSourceAddress();
1568         String destinationAddress = config.getDestinationAddress();
1569         int sourceFamily = getFamily(sourceAddress);
1570         int destinationFamily = getFamily(destinationAddress);
1571         if (sourceFamily != destinationFamily) {
1572             throw new IllegalArgumentException(
1573                     "Source address ("
1574                             + sourceAddress
1575                             + ") and destination address ("
1576                             + destinationAddress
1577                             + ") have different address families.");
1578         }
1579 
1580         // Throw an error if UDP Encapsulation is not used in IPv4.
1581         if (config.getEncapType() != IpSecTransform.ENCAP_NONE && sourceFamily != AF_INET) {
1582             throw new IllegalArgumentException(
1583                     "UDP Encapsulation is not supported for this address family");
1584         }
1585 
1586         switch (config.getMode()) {
1587             case IpSecTransform.MODE_TRANSPORT:
1588                 break;
1589             case IpSecTransform.MODE_TUNNEL:
1590                 break;
1591             default:
1592                 throw new IllegalArgumentException(
1593                         "Invalid IpSecTransform.mode: " + config.getMode());
1594         }
1595 
1596         config.setMarkValue(0);
1597         config.setMarkMask(0);
1598     }
1599 
1600     private static final String TUNNEL_OP = AppOpsManager.OPSTR_MANAGE_IPSEC_TUNNELS;
1601 
enforceTunnelFeatureAndPermissions(String callingPackage)1602     private void enforceTunnelFeatureAndPermissions(String callingPackage) {
1603         if (!mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_IPSEC_TUNNELS)) {
1604             throw new UnsupportedOperationException(
1605                     "IPsec Tunnel Mode requires PackageManager.FEATURE_IPSEC_TUNNELS");
1606         }
1607 
1608         Objects.requireNonNull(callingPackage, "Null calling package cannot create IpSec tunnels");
1609 
1610         // OP_MANAGE_IPSEC_TUNNELS will return MODE_ERRORED by default, including for the system
1611         // server. If the appop is not granted, require that the caller has the MANAGE_IPSEC_TUNNELS
1612         // permission or is the System Server.
1613         if (AppOpsManager.MODE_ALLOWED == getAppOpsManager().noteOpNoThrow(
1614                 TUNNEL_OP, Binder.getCallingUid(), callingPackage)) {
1615             return;
1616         }
1617         mContext.enforceCallingOrSelfPermission(
1618                 android.Manifest.permission.MANAGE_IPSEC_TUNNELS, "IpSecService");
1619     }
1620 
createOrUpdateTransform( IpSecConfig c, int resourceId, SpiRecord spiRecord, EncapSocketRecord socketRecord)1621     private void createOrUpdateTransform(
1622             IpSecConfig c, int resourceId, SpiRecord spiRecord, EncapSocketRecord socketRecord)
1623             throws RemoteException {
1624 
1625         int encapType = c.getEncapType(), encapLocalPort = 0, encapRemotePort = 0;
1626         if (encapType != IpSecTransform.ENCAP_NONE) {
1627             encapLocalPort = socketRecord.getPort();
1628             encapRemotePort = c.getEncapRemotePort();
1629         }
1630 
1631         IpSecAlgorithm auth = c.getAuthentication();
1632         IpSecAlgorithm crypt = c.getEncryption();
1633         IpSecAlgorithm authCrypt = c.getAuthenticatedEncryption();
1634 
1635         String cryptName;
1636         if (crypt == null) {
1637             cryptName = (authCrypt == null) ? IpSecAlgorithm.CRYPT_NULL : "";
1638         } else {
1639             cryptName = crypt.getName();
1640         }
1641 
1642         mNetd.ipSecAddSecurityAssociation(
1643                 Binder.getCallingUid(),
1644                 c.getMode(),
1645                 c.getSourceAddress(),
1646                 c.getDestinationAddress(),
1647                 (c.getNetwork() != null) ? c.getNetwork().getNetId() : 0,
1648                 spiRecord.getSpi(),
1649                 c.getMarkValue(),
1650                 c.getMarkMask(),
1651                 (auth != null) ? auth.getName() : "",
1652                 (auth != null) ? auth.getKey() : new byte[] {},
1653                 (auth != null) ? auth.getTruncationLengthBits() : 0,
1654                 cryptName,
1655                 (crypt != null) ? crypt.getKey() : new byte[] {},
1656                 (crypt != null) ? crypt.getTruncationLengthBits() : 0,
1657                 (authCrypt != null) ? authCrypt.getName() : "",
1658                 (authCrypt != null) ? authCrypt.getKey() : new byte[] {},
1659                 (authCrypt != null) ? authCrypt.getTruncationLengthBits() : 0,
1660                 encapType,
1661                 encapLocalPort,
1662                 encapRemotePort,
1663                 c.getXfrmInterfaceId());
1664     }
1665 
1666     /**
1667      * Create a IPsec transform, which represents a single security association in the kernel. The
1668      * transform will be cached by the system server and must be freed when no longer needed. It is
1669      * possible to free one, deleting the SA from underneath sockets that are using it, which will
1670      * result in all of those sockets becoming unable to send or receive data.
1671      */
1672     @Override
createTransform( IpSecConfig c, IBinder binder, String callingPackage)1673     public synchronized IpSecTransformResponse createTransform(
1674             IpSecConfig c, IBinder binder, String callingPackage) throws RemoteException {
1675         Objects.requireNonNull(c);
1676         if (c.getMode() == IpSecTransform.MODE_TUNNEL) {
1677             enforceTunnelFeatureAndPermissions(callingPackage);
1678         }
1679         checkIpSecConfig(c);
1680         Objects.requireNonNull(binder, "Null Binder passed to createTransform");
1681         final int resourceId = mNextResourceId++;
1682 
1683         UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid());
1684         List<RefcountedResource> dependencies = new ArrayList<>();
1685 
1686         if (!userRecord.mTransformQuotaTracker.isAvailable()) {
1687             return new IpSecTransformResponse(IpSecManager.Status.RESOURCE_UNAVAILABLE);
1688         }
1689 
1690         EncapSocketRecord socketRecord = null;
1691         if (c.getEncapType() != IpSecTransform.ENCAP_NONE) {
1692             RefcountedResource<EncapSocketRecord> refcountedSocketRecord =
1693                     userRecord.mEncapSocketRecords.getRefcountedResourceOrThrow(
1694                             c.getEncapSocketResourceId());
1695             dependencies.add(refcountedSocketRecord);
1696             socketRecord = refcountedSocketRecord.getResource();
1697         }
1698 
1699         RefcountedResource<SpiRecord> refcountedSpiRecord =
1700                 userRecord.mSpiRecords.getRefcountedResourceOrThrow(c.getSpiResourceId());
1701         dependencies.add(refcountedSpiRecord);
1702         SpiRecord spiRecord = refcountedSpiRecord.getResource();
1703 
1704         createOrUpdateTransform(c, resourceId, spiRecord, socketRecord);
1705 
1706         // SA was created successfully, time to construct a record and lock it away
1707         userRecord.mTransformRecords.put(
1708                 resourceId,
1709                 new RefcountedResource<TransformRecord>(
1710                         new TransformRecord(resourceId, c, spiRecord, socketRecord),
1711                         binder,
1712                         dependencies.toArray(new RefcountedResource[dependencies.size()])));
1713         return new IpSecTransformResponse(IpSecManager.Status.OK, resourceId);
1714     }
1715 
1716     /**
1717      * Delete a transport mode transform that was previously allocated by + registered with the
1718      * system server. If this is called on an inactive (or non-existent) transform, it will not
1719      * return an error. It's safe to de-allocate transforms that may have already been deleted for
1720      * other reasons.
1721      */
1722     @Override
deleteTransform(int resourceId)1723     public synchronized void deleteTransform(int resourceId) throws RemoteException {
1724         UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid());
1725         releaseResource(userRecord.mTransformRecords, resourceId);
1726     }
1727 
1728     /**
1729      * Apply an active transport mode transform to a socket, which will apply the IPsec security
1730      * association as a correspondent policy to the provided socket
1731      */
1732     @Override
applyTransportModeTransform( ParcelFileDescriptor socket, int direction, int resourceId)1733     public synchronized void applyTransportModeTransform(
1734             ParcelFileDescriptor socket, int direction, int resourceId) throws RemoteException {
1735         int callingUid = Binder.getCallingUid();
1736         UserRecord userRecord = mUserResourceTracker.getUserRecord(callingUid);
1737         checkDirection(direction);
1738         // Get transform record; if no transform is found, will throw IllegalArgumentException
1739         TransformRecord info = userRecord.mTransformRecords.getResourceOrThrow(resourceId);
1740 
1741         // TODO: make this a function.
1742         if (info.mPid != getCallingPid() || info.mUid != callingUid) {
1743             throw new SecurityException("Only the owner of an IpSec Transform may apply it!");
1744         }
1745 
1746         // Get config and check that to-be-applied transform has the correct mode
1747         IpSecConfig c = info.getConfig();
1748         Preconditions.checkArgument(
1749                 c.getMode() == IpSecTransform.MODE_TRANSPORT,
1750                 "Transform mode was not Transport mode; cannot be applied to a socket");
1751 
1752         mNetd.ipSecApplyTransportModeTransform(
1753                 socket,
1754                 callingUid,
1755                 direction,
1756                 c.getSourceAddress(),
1757                 c.getDestinationAddress(),
1758                 info.getSpiRecord().getSpi());
1759     }
1760 
1761     /**
1762      * Remove transport mode transforms from a socket, applying the default (empty) policy. This
1763      * ensures that NO IPsec policy is applied to the socket (would be the equivalent of applying a
1764      * policy that performs no IPsec). Today the resourceId parameter is passed but not used:
1765      * reserved for future improved input validation.
1766      */
1767     @Override
removeTransportModeTransforms(ParcelFileDescriptor socket)1768     public synchronized void removeTransportModeTransforms(ParcelFileDescriptor socket)
1769             throws RemoteException {
1770         mNetd.ipSecRemoveTransportModeTransform(socket);
1771     }
1772 
1773     /**
1774      * Apply an active tunnel mode transform to a TunnelInterface, which will apply the IPsec
1775      * security association as a correspondent policy to the provided interface
1776      */
1777     @Override
applyTunnelModeTransform( int tunnelResourceId, int direction, int transformResourceId, String callingPackage)1778     public synchronized void applyTunnelModeTransform(
1779             int tunnelResourceId, int direction,
1780             int transformResourceId, String callingPackage) throws RemoteException {
1781         enforceTunnelFeatureAndPermissions(callingPackage);
1782         checkDirection(direction);
1783 
1784         int callingUid = Binder.getCallingUid();
1785         UserRecord userRecord = mUserResourceTracker.getUserRecord(callingUid);
1786 
1787         // Get transform record; if no transform is found, will throw IllegalArgumentException
1788         TransformRecord transformInfo =
1789                 userRecord.mTransformRecords.getResourceOrThrow(transformResourceId);
1790 
1791         // Get tunnelInterface record; if no such interface is found, will throw
1792         // IllegalArgumentException
1793         TunnelInterfaceRecord tunnelInterfaceInfo =
1794                 userRecord.mTunnelInterfaceRecords.getResourceOrThrow(tunnelResourceId);
1795 
1796         // Get config and check that to-be-applied transform has the correct mode
1797         IpSecConfig c = transformInfo.getConfig();
1798         Preconditions.checkArgument(
1799                 c.getMode() == IpSecTransform.MODE_TUNNEL,
1800                 "Transform mode was not Tunnel mode; cannot be applied to a tunnel interface");
1801 
1802         EncapSocketRecord socketRecord = null;
1803         if (c.getEncapType() != IpSecTransform.ENCAP_NONE) {
1804             socketRecord =
1805                     userRecord.mEncapSocketRecords.getResourceOrThrow(c.getEncapSocketResourceId());
1806         }
1807         SpiRecord spiRecord = transformInfo.getSpiRecord();
1808 
1809         int mark =
1810                 (direction == IpSecManager.DIRECTION_OUT)
1811                         ? tunnelInterfaceInfo.getOkey()
1812                         : tunnelInterfaceInfo.getIkey(); // Ikey also used for FWD policies
1813 
1814         try {
1815             // Default to using the invalid SPI of 0 for inbound SAs. This allows policies to skip
1816             // SPI matching as part of the template resolution.
1817             int spi = IpSecManager.INVALID_SECURITY_PARAMETER_INDEX;
1818             c.setXfrmInterfaceId(tunnelInterfaceInfo.getIfId());
1819 
1820             // TODO: enable this when UPDSA supports updating marks. Adding kernel support upstream
1821             //     (and backporting) would allow us to narrow the mark space, and ensure that the SA
1822             //     and SPs have matching marks (as VTI are meant to be built).
1823             // Currently update does nothing with marks. Leave empty (defaulting to 0) to ensure the
1824             //     config matches the actual allocated resources in the kernel.
1825             // All SAs will have zero marks (from creation time), and any policy that matches the
1826             //     same src/dst could match these SAs. Non-IpSecService governed processes that
1827             //     establish floating policies with the same src/dst may result in undefined
1828             //     behavior. This is generally limited to vendor code due to the permissions
1829             //     (CAP_NET_ADMIN) required.
1830             //
1831             // c.setMarkValue(mark);
1832             // c.setMarkMask(0xffffffff);
1833 
1834             if (direction == IpSecManager.DIRECTION_OUT) {
1835                 // Set output mark via underlying network (output only)
1836                 c.setNetwork(tunnelInterfaceInfo.getUnderlyingNetwork());
1837 
1838                 // Set outbound SPI only. We want inbound to use any valid SA (old, new) on rekeys,
1839                 // but want to guarantee outbound packets are sent over the new SA.
1840                 spi = spiRecord.getSpi();
1841             }
1842 
1843             // Always update the policy with the relevant XFRM_IF_ID
1844             for (int selAddrFamily : ADDRESS_FAMILIES) {
1845                 mNetd.ipSecUpdateSecurityPolicy(
1846                         callingUid,
1847                         selAddrFamily,
1848                         direction,
1849                         transformInfo.getConfig().getSourceAddress(),
1850                         transformInfo.getConfig().getDestinationAddress(),
1851                         spi, // If outbound, also add SPI to the policy.
1852                         mark, // Must always set policy mark; ikey/okey for VTIs
1853                         0xffffffff,
1854                         c.getXfrmInterfaceId());
1855             }
1856 
1857             // Update SA with tunnel mark (ikey or okey based on direction)
1858             createOrUpdateTransform(c, transformResourceId, spiRecord, socketRecord);
1859         } catch (ServiceSpecificException e) {
1860             if (e.errorCode == EINVAL) {
1861                 throw new IllegalArgumentException(e.toString());
1862             } else {
1863                 throw e;
1864             }
1865         }
1866     }
1867 
1868     @Override
dump(FileDescriptor fd, PrintWriter pw, String[] args)1869     protected synchronized void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
1870         mContext.enforceCallingOrSelfPermission(DUMP, TAG);
1871 
1872         pw.println("IpSecService dump:");
1873         pw.println();
1874 
1875         pw.println("mUserResourceTracker:");
1876         pw.println(mUserResourceTracker);
1877     }
1878 }
1879