1 /*
2  * Copyright (C) 2015 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.server.connectivity;
18 
19 import static android.content.pm.PackageManager.PERMISSION_GRANTED;
20 import static android.net.NattSocketKeepalive.NATT_PORT;
21 import static android.net.SocketKeepalive.BINDER_DIED;
22 import static android.net.SocketKeepalive.DATA_RECEIVED;
23 import static android.net.SocketKeepalive.ERROR_INSUFFICIENT_RESOURCES;
24 import static android.net.SocketKeepalive.ERROR_INVALID_INTERVAL;
25 import static android.net.SocketKeepalive.ERROR_INVALID_IP_ADDRESS;
26 import static android.net.SocketKeepalive.ERROR_INVALID_NETWORK;
27 import static android.net.SocketKeepalive.ERROR_INVALID_SOCKET;
28 import static android.net.SocketKeepalive.ERROR_NO_SUCH_SLOT;
29 import static android.net.SocketKeepalive.ERROR_STOP_REASON_UNINITIALIZED;
30 import static android.net.SocketKeepalive.ERROR_UNSUPPORTED;
31 import static android.net.SocketKeepalive.MAX_INTERVAL_SEC;
32 import static android.net.SocketKeepalive.MIN_INTERVAL_SEC;
33 import static android.net.SocketKeepalive.NO_KEEPALIVE;
34 import static android.net.SocketKeepalive.SUCCESS;
35 import static android.net.SocketKeepalive.SUCCESS_PAUSED;
36 
37 import static com.android.net.module.util.FeatureVersions.FEATURE_CLAT_ADDRESS_TRANSLATE;
38 
39 import android.annotation.NonNull;
40 import android.annotation.Nullable;
41 import android.content.Context;
42 import android.net.ISocketKeepaliveCallback;
43 import android.net.InetAddresses;
44 import android.net.InvalidPacketException;
45 import android.net.KeepalivePacketData;
46 import android.net.NattKeepalivePacketData;
47 import android.net.NetworkAgent;
48 import android.net.SocketKeepalive.InvalidSocketException;
49 import android.net.TcpKeepalivePacketData;
50 import android.net.util.KeepaliveUtils;
51 import android.os.Binder;
52 import android.os.Handler;
53 import android.os.IBinder;
54 import android.os.Process;
55 import android.os.RemoteException;
56 import android.system.ErrnoException;
57 import android.system.Os;
58 import android.util.Log;
59 import android.util.Pair;
60 
61 import com.android.connectivity.resources.R;
62 import com.android.internal.annotations.VisibleForTesting;
63 import com.android.internal.util.IndentingPrintWriter;
64 import com.android.net.module.util.DeviceConfigUtils;
65 import com.android.net.module.util.HexDump;
66 import com.android.net.module.util.IpUtils;
67 
68 import java.io.FileDescriptor;
69 import java.net.Inet4Address;
70 import java.net.Inet6Address;
71 import java.net.InetAddress;
72 import java.net.InetSocketAddress;
73 import java.net.SocketAddress;
74 import java.util.ArrayList;
75 import java.util.Arrays;
76 import java.util.HashMap;
77 
78 /**
79  * Manages socket keepalive requests.
80  *
81  * Provides methods to stop and start keepalive requests, and keeps track of keepalives across all
82  * networks. This class is tightly coupled to ConnectivityService. It is not thread-safe and its
83  * handle* methods must be called only from the ConnectivityService handler thread.
84  */
85 public class KeepaliveTracker {
86 
87     private static final String TAG = "KeepaliveTracker";
88     private static final boolean DBG = false;
89 
90     public static final String PERMISSION = android.Manifest.permission.PACKET_KEEPALIVE_OFFLOAD;
91 
92     private static final String CONFIG_DISABLE_CLAT_ADDRESS_TRANSLATE =
93             "disable_clat_address_translate";
94 
95     /** Keeps track of keepalive requests. */
96     private final HashMap <NetworkAgentInfo, HashMap<Integer, KeepaliveInfo>> mKeepalives =
97             new HashMap<> ();
98     @NonNull
99     private final TcpKeepaliveController mTcpController;
100     @NonNull
101     private final Context mContext;
102 
103     // Supported keepalive count for each transport type, can be configured through
104     // config_networkSupportedKeepaliveCount. For better error handling, use
105     // {@link getSupportedKeepalivesForNetworkCapabilities} instead of direct access.
106     @NonNull
107     private final int[] mSupportedKeepalives;
108 
109     // Reserved privileged keepalive slots per transport. Caller's permission will be enforced if
110     // the number of remaining keepalive slots is less than or equal to the threshold.
111     private final int mReservedPrivilegedSlots;
112 
113     // Allowed unprivileged keepalive slots per uid. Caller's permission will be enforced if
114     // the number of remaining keepalive slots is less than or equal to the threshold.
115     private final int mAllowedUnprivilegedSlotsForUid;
116     private final Dependencies mDependencies;
KeepaliveTracker(Context context, Handler handler)117     public KeepaliveTracker(Context context, Handler handler) {
118         this(context, handler, new TcpKeepaliveController(handler), new Dependencies());
119     }
120 
121     @VisibleForTesting
KeepaliveTracker(Context context, Handler handler, TcpKeepaliveController tcpController, Dependencies deps)122     public KeepaliveTracker(Context context, Handler handler, TcpKeepaliveController tcpController,
123             Dependencies deps) {
124         mTcpController = tcpController;
125         mContext = context;
126         mDependencies = deps;
127 
128         mSupportedKeepalives = mDependencies.getSupportedKeepalives(mContext);
129 
130         final ConnectivityResources res = mDependencies.createConnectivityResources(mContext);
131         mReservedPrivilegedSlots = res.get().getInteger(
132                 R.integer.config_reservedPrivilegedKeepaliveSlots);
133         mAllowedUnprivilegedSlotsForUid = res.get().getInteger(
134                 R.integer.config_allowedUnprivilegedKeepalivePerUid);
135     }
136 
137     /**
138      * Tracks information about a socket keepalive.
139      *
140      * All information about this keepalive is known at construction time except the slot number,
141      * which is only returned when the hardware has successfully started the keepalive.
142      */
143     @VisibleForTesting
144     public class KeepaliveInfo implements IBinder.DeathRecipient {
145         // TODO : remove this member. Only AutoOnOffKeepalive should have a reference to this.
146         public final ISocketKeepaliveCallback mCallback;
147         // Bookkeeping data.
148         private final int mUid;
149         private final int mPid;
150         private final boolean mPrivileged;
151         public final NetworkAgentInfo mNai;
152         private final int mType;
153         public final FileDescriptor mFd;
154         // True if this was resumed from a previously turned off keepalive, otherwise false.
155         // This is necessary to send the correct callbacks.
156         public final boolean mResumed;
157 
158         public static final int TYPE_NATT = 1;
159         public static final int TYPE_TCP = 2;
160 
161         // Keepalive slot. A small integer that identifies this keepalive among the ones handled
162         // by this network. This is initialized to NO_KEEPALIVE for new keepalives, but to the
163         // old slot for resumed keepalives.
164         private int mSlot;
165 
166         // Packet data.
167         private final KeepalivePacketData mPacket;
168         private final int mInterval;
169 
170         // Whether the keepalive is started or not. The initial state is NOT_STARTED.
171         private static final int NOT_STARTED = 1;
172         private static final int STARTING = 2;
173         private static final int STARTED = 3;
174         private static final int STOPPING = 4;
175         private int mStartedState = NOT_STARTED;
176         private int mStopReason = ERROR_STOP_REASON_UNINITIALIZED;
177 
KeepaliveInfo(@onNull ISocketKeepaliveCallback callback, @NonNull NetworkAgentInfo nai, @NonNull KeepalivePacketData packet, int interval, int type, @Nullable FileDescriptor fd)178         KeepaliveInfo(@NonNull ISocketKeepaliveCallback callback,
179                 @NonNull NetworkAgentInfo nai,
180                 @NonNull KeepalivePacketData packet,
181                 int interval,
182                 int type,
183                 @Nullable FileDescriptor fd) throws InvalidSocketException {
184             this(callback, nai, packet, Binder.getCallingPid(), Binder.getCallingUid(), interval,
185                     type, fd, NO_KEEPALIVE /* slot */, false /* resumed */);
186         }
187 
KeepaliveInfo(@onNull ISocketKeepaliveCallback callback, @NonNull NetworkAgentInfo nai, @NonNull KeepalivePacketData packet, int pid, int uid, int interval, int type, @Nullable FileDescriptor fd, int slot, boolean resumed)188         KeepaliveInfo(@NonNull ISocketKeepaliveCallback callback,
189                 @NonNull NetworkAgentInfo nai,
190                 @NonNull KeepalivePacketData packet,
191                 int pid,
192                 int uid,
193                 int interval,
194                 int type,
195                 @Nullable FileDescriptor fd,
196                 int slot,
197                 boolean resumed) throws InvalidSocketException {
198             mCallback = callback;
199             mPid = pid;
200             mUid = uid;
201             mPrivileged = (PERMISSION_GRANTED == mContext.checkPermission(PERMISSION, mPid, mUid));
202 
203             mNai = nai;
204             mPacket = packet;
205             mInterval = interval;
206             mType = type;
207             mSlot = slot;
208             mResumed = resumed;
209 
210             // For SocketKeepalive, a dup of fd is kept in mFd so the source port from which the
211             // keepalives are sent cannot be reused by another app even if the fd gets closed by
212             // the user. A null is acceptable here for backward compatibility of PacketKeepalive
213             // API.
214             try {
215                 if (fd != null) {
216                     mFd = Os.dup(fd);
217                 }  else {
218                     Log.d(TAG, toString() + " calls with null fd");
219                     if (!mPrivileged) {
220                         throw new SecurityException(
221                                 "null fd is not allowed for unprivileged access.");
222                     }
223                     if (mType == TYPE_TCP) {
224                         throw new IllegalArgumentException(
225                                 "null fd is not allowed for tcp socket keepalives.");
226                     }
227                     mFd = null;
228                 }
229             } catch (ErrnoException e) {
230                 Log.e(TAG, "Cannot dup fd: ", e);
231                 throw new InvalidSocketException(ERROR_INVALID_SOCKET, e);
232             }
233 
234             try {
235                 mCallback.asBinder().linkToDeath(this, 0);
236             } catch (RemoteException e) {
237                 binderDied();
238             }
239         }
240 
getNai()241         public NetworkAgentInfo getNai() {
242             return mNai;
243         }
244 
startedStateString(final int state)245         private String startedStateString(final int state) {
246             switch (state) {
247                 case NOT_STARTED : return "NOT_STARTED";
248                 case STARTING : return "STARTING";
249                 case STARTED : return "STARTED";
250                 case STOPPING : return "STOPPING";
251             }
252             throw new IllegalArgumentException("Unknown state");
253         }
254 
toString()255         public String toString() {
256             return "KeepaliveInfo ["
257                     + " type=" + mType
258                     + " network=" + mNai.network
259                     + " startedState=" + startedStateString(mStartedState)
260                     + " "
261                     + IpUtils.addressAndPortToString(mPacket.getSrcAddress(), mPacket.getSrcPort())
262                     + "->"
263                     + IpUtils.addressAndPortToString(mPacket.getDstAddress(), mPacket.getDstPort())
264                     + " interval=" + mInterval
265                     + " uid=" + mUid + " pid=" + mPid + " privileged=" + mPrivileged
266                     + " packetData=" + HexDump.toHexString(mPacket.getPacket())
267                     + " ]";
268         }
269 
270         /** Called when the application process is killed. */
binderDied()271         public void binderDied() {
272             // TODO b/267106526 : this is not called on the handler thread but stop() happily
273             // assumes it is, which means this is a pretty dangerous race condition.
274             stop(BINDER_DIED);
275         }
276 
unlinkDeathRecipient()277         void unlinkDeathRecipient() {
278             if (mCallback != null) {
279                 mCallback.asBinder().unlinkToDeath(this, 0);
280             }
281         }
282 
getSlot()283         public int getSlot() {
284             return mSlot;
285         }
286 
getKeepaliveIntervalSec()287         int getKeepaliveIntervalSec() {
288             return mInterval;
289         }
290 
getUid()291         public int getUid() {
292             return mUid;
293         }
294 
checkNetworkConnected()295         private int checkNetworkConnected() {
296             if (!mNai.networkInfo.isConnectedOrConnecting()) {
297                 return ERROR_INVALID_NETWORK;
298             }
299             return SUCCESS;
300         }
301 
checkSourceAddress()302         private int checkSourceAddress() {
303             // Check that we have the source address.
304             for (InetAddress address : mNai.linkProperties.getAllAddresses()) {
305                 if (address.equals(mPacket.getSrcAddress())) {
306                     return SUCCESS;
307                 }
308             }
309             // Or the address is the clat source address.
310             if (mPacket.getSrcAddress().equals(mNai.getClatv6SrcAddress())) {
311                 return SUCCESS;
312             }
313             return ERROR_INVALID_IP_ADDRESS;
314         }
315 
checkInterval()316         private int checkInterval() {
317             if (mInterval < MIN_INTERVAL_SEC || mInterval > MAX_INTERVAL_SEC) {
318                 return ERROR_INVALID_INTERVAL;
319             }
320             return SUCCESS;
321         }
322 
checkPermission()323         private int checkPermission() {
324             final HashMap<Integer, KeepaliveInfo> networkKeepalives = mKeepalives.get(mNai);
325             if (networkKeepalives == null) {
326                 return ERROR_INVALID_NETWORK;
327             }
328 
329             if (mPrivileged) return SUCCESS;
330 
331             final int supported = KeepaliveUtils.getSupportedKeepalivesForNetworkCapabilities(
332                     mSupportedKeepalives, mNai.networkCapabilities);
333 
334             int takenUnprivilegedSlots = 0;
335             for (final KeepaliveInfo ki : networkKeepalives.values()) {
336                 if (!ki.mPrivileged) ++takenUnprivilegedSlots;
337             }
338             if (takenUnprivilegedSlots > supported - mReservedPrivilegedSlots) {
339                 return ERROR_INSUFFICIENT_RESOURCES;
340             }
341 
342             // Count unprivileged keepalives for the same uid across networks.
343             int unprivilegedCountSameUid = 0;
344             for (final HashMap<Integer, KeepaliveInfo> kaForNetwork : mKeepalives.values()) {
345                 for (final KeepaliveInfo ki : kaForNetwork.values()) {
346                     if (ki.mUid == mUid) {
347                         unprivilegedCountSameUid++;
348                     }
349                 }
350             }
351             if (unprivilegedCountSameUid > mAllowedUnprivilegedSlotsForUid) {
352                 return ERROR_INSUFFICIENT_RESOURCES;
353             }
354             return SUCCESS;
355         }
356 
checkLimit()357         private int checkLimit() {
358             final HashMap<Integer, KeepaliveInfo> networkKeepalives = mKeepalives.get(mNai);
359             if (networkKeepalives == null) {
360                 return ERROR_INVALID_NETWORK;
361             }
362             final int supported = KeepaliveUtils.getSupportedKeepalivesForNetworkCapabilities(
363                     mSupportedKeepalives, mNai.networkCapabilities);
364             if (supported == 0) return ERROR_UNSUPPORTED;
365             if (networkKeepalives.size() > supported) return ERROR_INSUFFICIENT_RESOURCES;
366             return SUCCESS;
367         }
368 
369         /**
370          * Checks if the keepalive info is valid to start.
371          *
372          * @return SUCCESS if the keepalive is valid and the error reason otherwise.
373          */
isValid()374         public int isValid() {
375             synchronized (mNai) {
376                 int error = checkInterval();
377                 if (error == SUCCESS) error = checkLimit();
378                 if (error == SUCCESS) error = checkPermission();
379                 if (error == SUCCESS) error = checkNetworkConnected();
380                 if (error == SUCCESS) error = checkSourceAddress();
381                 return error;
382             }
383         }
384 
385         /**
386          * Attempt to start the keepalive on the given slot.
387          *
388          * @param slot the slot to start the keepalive on.
389          * @return SUCCESS if the keepalive is successfully starting and the error reason otherwise.
390          */
start(int slot)391         int start(int slot) {
392             // BINDER_DIED can happen if the binder died before the KeepaliveInfo was created and
393             // the constructor set the state to BINDER_DIED. If that's the case, the KI is already
394             // cleaned up.
395             if (BINDER_DIED == mStartedState) return BINDER_DIED;
396             mSlot = slot;
397             int error = isValid();
398             if (error == SUCCESS) {
399                 Log.d(TAG, "Starting keepalive " + mSlot + " on " + mNai.toShortString());
400                 switch (mType) {
401                     case TYPE_NATT:
402                         final NattKeepalivePacketData nattData = (NattKeepalivePacketData) mPacket;
403                         mNai.onAddNattKeepalivePacketFilter(slot, nattData);
404                         mNai.onStartNattSocketKeepalive(slot, mInterval, nattData);
405                         break;
406                     case TYPE_TCP:
407                         try {
408                             mTcpController.startSocketMonitor(mFd, mCallback, mSlot);
409                         } catch (InvalidSocketException e) {
410                             handleStopKeepalive(mNai, mSlot, ERROR_INVALID_SOCKET);
411                             return ERROR_INVALID_SOCKET;
412                         }
413                         final TcpKeepalivePacketData tcpData = (TcpKeepalivePacketData) mPacket;
414                         mNai.onAddTcpKeepalivePacketFilter(slot, tcpData);
415                         // TODO: check result from apf and notify of failure as needed.
416                         mNai.onStartTcpSocketKeepalive(slot, mInterval, tcpData);
417                         break;
418                     default:
419                         Log.wtf(TAG, "Starting keepalive with unknown type: " + mType);
420                         handleStopKeepalive(mNai, mSlot, ERROR_UNSUPPORTED);
421                         return ERROR_UNSUPPORTED;
422                 }
423                 mStartedState = STARTING;
424                 return SUCCESS;
425             } else {
426                 handleStopKeepalive(mNai, mSlot, error);
427                 return error;
428             }
429         }
430 
stop(int reason)431         void stop(int reason) {
432             int uid = Binder.getCallingUid();
433             if (uid != mUid && uid != Process.SYSTEM_UID) {
434                 if (DBG) {
435                     Log.e(TAG, "Cannot stop unowned keepalive " + mSlot + " on " + mNai.network);
436                 }
437             }
438             // To prevent races from re-entrance of stop(), return if the state is already stopping.
439             // This might happen if multiple event sources stop keepalive in a short time. Such as
440             // network disconnect after user calls stop(), or tear down socket after binder died.
441             // Note that it's always possible this method is called by the auto keepalive timer
442             // or any other way after the binder died, hence the check for BINDER_DIED. If the
443             // binder has died, then the KI has already been cleaned up.
444             if (mStartedState == STOPPING || mStartedState == BINDER_DIED) return;
445 
446             // Store the reason of stopping, and report it after the keepalive is fully stopped.
447             if (mStopReason != ERROR_STOP_REASON_UNINITIALIZED) {
448                 throw new IllegalStateException("Unexpected stop reason: " + mStopReason);
449             }
450             mStopReason = reason;
451             Log.d(TAG, "Stopping keepalive " + mSlot + " on " + mNai.toShortString()
452                     + ": " + reason);
453             switch (mStartedState) {
454                 case NOT_STARTED:
455                     // Remove the reference to this keepalive that had an error before starting,
456                     // e.g. invalid parameter.
457                     cleanupStoppedKeepalive(mNai, mSlot);
458                     if (BINDER_DIED == reason) mStartedState = BINDER_DIED;
459                     break;
460                 default:
461                     mStartedState = STOPPING;
462                     switch (mType) {
463                         case TYPE_TCP:
464                             mTcpController.stopSocketMonitor(mSlot);
465                             // fall through
466                         case TYPE_NATT:
467                             mNai.onStopSocketKeepalive(mSlot);
468                             mNai.onRemoveKeepalivePacketFilter(mSlot);
469                             break;
470                         default:
471                             Log.wtf(TAG, "Stopping keepalive with unknown type: " + mType);
472                     }
473             }
474 
475             // Close the duplicated fd that maintains the lifecycle of socket whenever
476             // keepalive is running.
477             if (mFd != null) {
478                 try {
479                     Os.close(mFd);
480                 } catch (ErrnoException e) {
481                     // This should not happen since system server controls the lifecycle of fd when
482                     // keepalive offload is running.
483                     Log.wtf(TAG, "Error closing fd for keepalive " + mSlot + ": " + e);
484                 }
485             }
486         }
487 
488         /**
489          * Construct a new KeepaliveInfo from existing KeepaliveInfo with a new fd.
490          */
withFd(@onNull FileDescriptor fd)491         public KeepaliveInfo withFd(@NonNull FileDescriptor fd) throws InvalidSocketException {
492             return new KeepaliveInfo(mCallback, mNai, mPacket, mPid, mUid, mInterval, mType,
493                     fd, mSlot, true /* resumed */);
494         }
495 
496         /**
497          * Construct a new KeepaliveInfo from existing KeepaliveInfo with a new KeepalivePacketData.
498          */
withPacketData(@onNull KeepalivePacketData packet)499         public KeepaliveInfo withPacketData(@NonNull KeepalivePacketData packet)
500                 throws InvalidSocketException {
501             return new KeepaliveInfo(mCallback, mNai, packet, mPid, mUid, mInterval, mType,
502                     mFd, mSlot, mResumed);
503         }
504     }
505 
notifyErrorCallback(ISocketKeepaliveCallback cb, int error)506     void notifyErrorCallback(ISocketKeepaliveCallback cb, int error) {
507         if (DBG) Log.w(TAG, "Sending onError(" + error + ") callback");
508         try {
509             cb.onError(error);
510         } catch (RemoteException e) {
511             Log.w(TAG, "Discarded onError(" + error + ") callback");
512         }
513     }
514 
findFirstFreeSlot(NetworkAgentInfo nai)515     private  int findFirstFreeSlot(NetworkAgentInfo nai) {
516         HashMap networkKeepalives = mKeepalives.get(nai);
517         if (networkKeepalives == null) {
518             networkKeepalives = new HashMap<Integer, KeepaliveInfo>();
519             mKeepalives.put(nai, networkKeepalives);
520         }
521 
522         // Find the lowest-numbered free slot. Slot numbers start from 1, because that's what two
523         // separate chipset implementations independently came up with.
524         int slot;
525         for (slot = 1; slot <= networkKeepalives.size(); slot++) {
526             if (networkKeepalives.get(slot) == null) {
527                 return slot;
528             }
529         }
530         return slot;
531     }
532 
533     /**
534      * Handle start keepalives with the message.
535      *
536      * @param ki the keepalive to start.
537      * @return Pair of (SUCCESS if the keepalive is successfully starting and the error reason
538      *         otherwise, the started KeepaliveInfo object)
539      */
handleStartKeepalive(KeepaliveInfo ki)540     public Pair<Integer, KeepaliveInfo> handleStartKeepalive(KeepaliveInfo ki) {
541         final KeepaliveInfo newKi;
542         try {
543             newKi = handleUpdateKeepaliveForClat(ki);
544         } catch (InvalidSocketException | InvalidPacketException e) {
545             Log.e(TAG, "Fail to construct keepalive packet");
546             notifyErrorCallback(ki.mCallback, ERROR_INVALID_IP_ADDRESS);
547             // Fail to create new keepalive packet for clat. Return the original keepalive info.
548             return new Pair<>(ERROR_INVALID_IP_ADDRESS, ki);
549         }
550 
551         final NetworkAgentInfo nai = newKi.getNai();
552         // If this was a paused keepalive, then reuse the same slot that was kept for it. Otherwise,
553         // use the first free slot for this network agent.
554         final int slot = NO_KEEPALIVE != newKi.mSlot ? newKi.mSlot : findFirstFreeSlot(nai);
555         mKeepalives.get(nai).put(slot, newKi);
556 
557         return new Pair<>(newKi.start(slot), newKi);
558     }
559 
handleUpdateKeepaliveForClat(KeepaliveInfo ki)560     private KeepaliveInfo handleUpdateKeepaliveForClat(KeepaliveInfo ki)
561             throws InvalidSocketException, InvalidPacketException {
562         if (!mDependencies.isAddressTranslationEnabled(mContext)) return ki;
563 
564         // Translation applies to only NAT-T keepalive
565         if (ki.mType != KeepaliveInfo.TYPE_NATT) return ki;
566         // Only try to translate address if the packet source address is the clat's source address.
567         if (!ki.mPacket.getSrcAddress().equals(ki.getNai().getClatv4SrcAddress())) return ki;
568 
569         final InetAddress dstAddr = ki.mPacket.getDstAddress();
570         // Do not perform translation for a v6 dst address.
571         if (!(dstAddr instanceof Inet4Address)) return ki;
572 
573         final Inet6Address address = ki.getNai().translateV4toClatV6((Inet4Address) dstAddr);
574 
575         if (address == null) return ki;
576 
577         final int srcPort = ki.mPacket.getSrcPort();
578         final KeepaliveInfo newInfo = ki.withPacketData(NattKeepalivePacketData.nattKeepalivePacket(
579                 ki.getNai().getClatv6SrcAddress(), srcPort, address, NATT_PORT));
580         Log.d(TAG, "Src is clat v4 address. Convert from " + ki + " to " + newInfo);
581         return newInfo;
582     }
583 
handleStopAllKeepalives(NetworkAgentInfo nai, int reason)584     public void handleStopAllKeepalives(NetworkAgentInfo nai, int reason) {
585         final HashMap<Integer, KeepaliveInfo> networkKeepalives = mKeepalives.get(nai);
586         if (networkKeepalives != null) {
587             final ArrayList<KeepaliveInfo> kalist = new ArrayList(networkKeepalives.values());
588             for (KeepaliveInfo ki : kalist) {
589                 // If the keepalive is paused, then it is already stopped with the hardware and so
590                 // continue. Note that to send the appropriate stop reason callback,
591                 // AutomaticOnOffKeepaliveTracker will call finalizePausedKeepalive which will also
592                 // finally remove this keepalive slot from the array.
593                 if (ki.mStopReason == SUCCESS_PAUSED) continue;
594 
595                 ki.stop(reason);
596                 // Clean up keepalives since the network agent is disconnected and unable to pass
597                 // back asynchronous result of stop().
598                 cleanupStoppedKeepalive(nai, ki.mSlot);
599             }
600         }
601     }
602 
handleStopKeepalive(NetworkAgentInfo nai, int slot, int reason)603     public void handleStopKeepalive(NetworkAgentInfo nai, int slot, int reason) {
604         final String networkName = NetworkAgentInfo.toShortString(nai);
605         HashMap <Integer, KeepaliveInfo> networkKeepalives = mKeepalives.get(nai);
606         if (networkKeepalives == null) {
607             Log.e(TAG, "Attempt to stop keepalive on nonexistent network " + networkName);
608             return;
609         }
610         KeepaliveInfo ki = networkKeepalives.get(slot);
611         if (ki == null) {
612             Log.e(TAG, "Attempt to stop nonexistent keepalive " + slot + " on " + networkName);
613             return;
614         }
615         ki.stop(reason);
616         // Clean up keepalives will be done as a result of calling ki.stop() after the slots are
617         // freed.
618     }
619 
cleanupStoppedKeepalive(NetworkAgentInfo nai, int slot)620     private void cleanupStoppedKeepalive(NetworkAgentInfo nai, int slot) {
621         final String networkName = NetworkAgentInfo.toShortString(nai);
622         HashMap<Integer, KeepaliveInfo> networkKeepalives = mKeepalives.get(nai);
623         if (networkKeepalives == null) {
624             Log.e(TAG, "Attempt to remove keepalive on nonexistent network " + networkName);
625             return;
626         }
627         KeepaliveInfo ki = networkKeepalives.get(slot);
628         if (ki == null) {
629             Log.e(TAG, "Attempt to remove nonexistent keepalive " + slot + " on " + networkName);
630             return;
631         }
632 
633         // If the keepalive was stopped for good, remove it from the hash table so the slot can
634         // be considered available when reusing it. If it was only a pause, let it sit in the map
635         // so it sits on the slot.
636         final int reason = ki.mStopReason;
637         if (reason != SUCCESS_PAUSED) {
638             networkKeepalives.remove(slot);
639             Log.d(TAG, "Remove keepalive " + slot + " on " + networkName + ", "
640                     + networkKeepalives.size() + " remains.");
641         } else {
642             Log.d(TAG, "Pause keepalive " + slot + " on " + networkName + ", keep slot reserved");
643         }
644         if (networkKeepalives.isEmpty()) {
645             mKeepalives.remove(nai);
646         }
647 
648         // Notify app that the keepalive is stopped.
649         if (reason == SUCCESS) {
650             try {
651                 ki.mCallback.onStopped();
652             } catch (RemoteException e) {
653                 Log.w(TAG, "Discarded onStop callback: " + reason);
654             }
655         } else if (reason == SUCCESS_PAUSED) {
656             try {
657                 ki.mCallback.onPaused();
658             } catch (RemoteException e) {
659                 Log.w(TAG, "Discarded onPaused callback: " + reason);
660             }
661         } else if (reason == DATA_RECEIVED) {
662             try {
663                 ki.mCallback.onDataReceived();
664             } catch (RemoteException e) {
665                 Log.w(TAG, "Discarded onDataReceived callback: " + reason);
666             }
667         } else if (reason == ERROR_STOP_REASON_UNINITIALIZED) {
668             throw new IllegalStateException("Unexpected stop reason: " + reason);
669         } else if (reason == ERROR_NO_SUCH_SLOT) {
670             // There are multiple independent reasons a keepalive can stop. Some
671             // are software (e.g. the app stops the keepalive) and some are hardware
672             // (e.g. the SIM card gets removed). Therefore, there is a very low
673             // probability that both of these happen at the same time, which would
674             // result in the first stop attempt returning SUCCESS and the second
675             // stop attempt returning NO_SUCH_SLOT. Such a race condition can be
676             // ignored with a log.
677             // This should still be reported because if it happens with any frequency
678             // it probably means there is a bug where the system server is trying
679             // to use a non-existing hardware slot.
680             // TODO : separate the non-existing hardware slot from the case where
681             // there is no keepalive running on this slot.
682             Log.wtf(TAG, "Keepalive on slot " + slot + " can't be stopped : " + reason);
683             notifyErrorCallback(ki.mCallback, reason);
684         } else {
685             notifyErrorCallback(ki.mCallback, reason);
686         }
687 
688         ki.unlinkDeathRecipient();
689     }
690 
691     /**
692      * Finalize a paused keepalive.
693      *
694      * This will send the appropriate callback after checking that this keepalive is indeed paused,
695      * and free the slot.
696      *
697      * @param ki the keepalive to finalize
698      * @param reason the reason the keepalive is stopped
699      */
finalizePausedKeepalive(@onNull final KeepaliveInfo ki, int reason)700     public void finalizePausedKeepalive(@NonNull final KeepaliveInfo ki, int reason) {
701         if (SUCCESS_PAUSED != ki.mStopReason) {
702             throw new IllegalStateException("Keepalive is not paused");
703         }
704         if (reason == SUCCESS) {
705             try {
706                 ki.mCallback.onStopped();
707             } catch (RemoteException e) {
708                 Log.w(TAG, "Discarded onStopped callback while finalizing paused keepalive");
709             }
710         } else {
711             notifyErrorCallback(ki.mCallback, reason);
712         }
713 
714         final HashMap<Integer, KeepaliveInfo> networkKeepalives = mKeepalives.get(ki.mNai);
715         if (networkKeepalives == null) {
716             Log.e(TAG, "Attempt to finalize keepalive on nonexistent network " + ki.mNai);
717             return;
718         }
719         networkKeepalives.remove(ki.mSlot);
720     }
721 
722     /**
723      * Handle keepalive events from lower layer.
724      *
725      * @return false if the event caused handleStopKeepalive to be called, i.e. the keepalive is
726      *     forced to stop. Otherwise, return true.
727      */
handleEventSocketKeepalive(@onNull NetworkAgentInfo nai, int slot, int reason)728     public boolean handleEventSocketKeepalive(@NonNull NetworkAgentInfo nai, int slot, int reason) {
729         KeepaliveInfo ki = null;
730         try {
731             ki = mKeepalives.get(nai).get(slot);
732         } catch(NullPointerException e) {}
733         if (ki == null) {
734             Log.e(TAG, "Event " + NetworkAgent.EVENT_SOCKET_KEEPALIVE + "," + slot + "," + reason
735                     + " for unknown keepalive " + slot + " on " + nai.toShortString());
736             return true;
737         }
738 
739         // This can be called in a number of situations :
740         // - startedState is STARTING.
741         //   - reason is SUCCESS => go to STARTED.
742         //   - reason isn't SUCCESS => it's an error starting. Go to NOT_STARTED and stop keepalive.
743         // - startedState is STARTED.
744         //   - reason is SUCCESS => it's a success stopping. Go to NOT_STARTED and stop keepalive.
745         //   - reason isn't SUCCESS => it's an error in exec. Go to NOT_STARTED and stop keepalive.
746         // The control is not supposed to ever come here if the state is NOT_STARTED. This is
747         // because in NOT_STARTED state, the code will switch to STARTING before sending messages
748         // to start, and the only way to NOT_STARTED is this function, through the edges outlined
749         // above : in all cases, keepalive gets stopped and can't restart without going into
750         // STARTING as messages are ordered. This also depends on the hardware processing the
751         // messages in order.
752         // TODO : clarify this code and get rid of mStartedState. Using a StateMachine is an
753         // option.
754         if (KeepaliveInfo.STARTING == ki.mStartedState) {
755             if (SUCCESS == reason) {
756                 // Keepalive successfully started.
757                 Log.d(TAG, "Started keepalive " + slot + " on " + nai.toShortString());
758                 ki.mStartedState = KeepaliveInfo.STARTED;
759                 try {
760                     if (ki.mResumed) {
761                         ki.mCallback.onResumed();
762                     } else {
763                         ki.mCallback.onStarted();
764                     }
765                 } catch (RemoteException e) {
766                     Log.w(TAG, "Discarded " + (ki.mResumed ? "onResumed" : "onStarted")
767                             + " callback for slot " + slot);
768                 }
769                 return true;
770             } else {
771                 Log.d(TAG, "Failed to start keepalive " + slot + " on " + nai.toShortString()
772                         + ": " + reason);
773                 // The message indicated some error trying to start: do call handleStopKeepalive.
774                 handleStopKeepalive(nai, slot, reason);
775                 return false;
776             }
777         } else if (KeepaliveInfo.STOPPING == ki.mStartedState) {
778             // The message indicated result of stopping : clean up keepalive slots.
779             Log.d(TAG, "Stopped keepalive " + slot + " on " + nai.toShortString()
780                     + " stopped: " + reason);
781             ki.mStartedState = KeepaliveInfo.NOT_STARTED;
782             cleanupStoppedKeepalive(nai, slot);
783             return true;
784         } else {
785             Log.wtf(TAG, "Event " + NetworkAgent.EVENT_SOCKET_KEEPALIVE + "," + slot + "," + reason
786                     + " for keepalive in wrong state: " + ki.toString());
787             // Although this is an unexpected event, the keepalive is not stopped here.
788             return true;
789         }
790     }
791 
792     /**
793      * Called when requesting that keepalives be started on a IPsec NAT-T socket. See
794      * {@link android.net.SocketKeepalive}.
795      **/
796     @Nullable
makeNattKeepaliveInfo(@ullable NetworkAgentInfo nai, @Nullable FileDescriptor fd, int intervalSeconds, @NonNull ISocketKeepaliveCallback cb, @NonNull String srcAddrString, int srcPort, @NonNull String dstAddrString, int dstPort)797     public KeepaliveInfo makeNattKeepaliveInfo(@Nullable NetworkAgentInfo nai,
798             @Nullable FileDescriptor fd,
799             int intervalSeconds,
800             @NonNull ISocketKeepaliveCallback cb,
801             @NonNull String srcAddrString,
802             int srcPort,
803             @NonNull String dstAddrString,
804             int dstPort) {
805         if (nai == null) {
806             notifyErrorCallback(cb, ERROR_INVALID_NETWORK);
807             return null;
808         }
809 
810         InetAddress srcAddress, dstAddress;
811         try {
812             srcAddress = InetAddresses.parseNumericAddress(srcAddrString);
813             dstAddress = InetAddresses.parseNumericAddress(dstAddrString);
814         } catch (IllegalArgumentException e) {
815             Log.e(TAG, "Fail to construct address", e);
816             notifyErrorCallback(cb, ERROR_INVALID_IP_ADDRESS);
817             return null;
818         }
819 
820         KeepalivePacketData packet;
821         try {
822             packet = NattKeepalivePacketData.nattKeepalivePacket(
823                     srcAddress, srcPort, dstAddress, NATT_PORT);
824         } catch (InvalidPacketException e) {
825             Log.e(TAG, "Fail to construct keepalive packet", e);
826             notifyErrorCallback(cb, e.getError());
827             return null;
828         }
829         KeepaliveInfo ki = null;
830         try {
831             ki = new KeepaliveInfo(cb, nai, packet, intervalSeconds,
832                     KeepaliveInfo.TYPE_NATT, fd);
833         } catch (InvalidSocketException | IllegalArgumentException | SecurityException e) {
834             Log.e(TAG, "Fail to construct keepalive", e);
835             notifyErrorCallback(cb, ERROR_INVALID_SOCKET);
836             return null;
837         }
838         Log.d(TAG, "Created keepalive: " + ki);
839         return ki;
840     }
841 
842     /**
843      * Make a KeepaliveInfo for a TCP socket.
844      *
845      * In order to offload keepalive for application correctly, sequence number, ack number and
846      * other fields are needed to form the keepalive packet. Thus, this function synchronously
847      * puts the socket into repair mode to get the necessary information. After the socket has been
848      * put into repair mode, the application cannot access the socket until reverted to normal.
849      *
850      * See {@link android.net.SocketKeepalive}.
851      **/
852     @Nullable
makeTcpKeepaliveInfo(@ullable NetworkAgentInfo nai, @NonNull FileDescriptor fd, int intervalSeconds, @NonNull ISocketKeepaliveCallback cb)853     public KeepaliveInfo makeTcpKeepaliveInfo(@Nullable NetworkAgentInfo nai,
854             @NonNull FileDescriptor fd,
855             int intervalSeconds,
856             @NonNull ISocketKeepaliveCallback cb) {
857         if (nai == null) {
858             notifyErrorCallback(cb, ERROR_INVALID_NETWORK);
859             return null;
860         }
861 
862         final TcpKeepalivePacketData packet;
863         try {
864             packet = TcpKeepaliveController.getTcpKeepalivePacket(fd);
865         } catch (InvalidSocketException e) {
866             notifyErrorCallback(cb, e.error);
867             return null;
868         } catch (InvalidPacketException e) {
869             notifyErrorCallback(cb, e.getError());
870             return null;
871         }
872         KeepaliveInfo ki = null;
873         try {
874             ki = new KeepaliveInfo(cb, nai, packet, intervalSeconds,
875                     KeepaliveInfo.TYPE_TCP, fd);
876         } catch (InvalidSocketException | IllegalArgumentException | SecurityException e) {
877             Log.e(TAG, "Fail to construct keepalive e=" + e);
878             notifyErrorCallback(cb, ERROR_INVALID_SOCKET);
879             return null;
880         }
881         Log.d(TAG, "Created keepalive: " + ki.toString());
882         return ki;
883     }
884 
885     /**
886      * Make a KeepaliveInfo for an IPSec NAT-T socket.
887      *
888      * This function is identical to {@link #makeNattKeepaliveInfo}, but also takes a
889      * {@code resourceId}, which is the resource index bound to the {@link UdpEncapsulationSocket}
890      * when creating by {@link com.android.server.IpSecService} to verify whether the given
891      * {@link UdpEncapsulationSocket} is legitimate.
892      **/
893     @Nullable
makeNattKeepaliveInfo(@ullable NetworkAgentInfo nai, @Nullable FileDescriptor fd, int resourceId, int intervalSeconds, @NonNull ISocketKeepaliveCallback cb, @NonNull String srcAddrString, @NonNull String dstAddrString, int dstPort)894     public KeepaliveInfo makeNattKeepaliveInfo(@Nullable NetworkAgentInfo nai,
895             @Nullable FileDescriptor fd,
896             int resourceId,
897             int intervalSeconds,
898             @NonNull ISocketKeepaliveCallback cb,
899             @NonNull String srcAddrString,
900             @NonNull String dstAddrString,
901             int dstPort) {
902         // Ensure that the socket is created by IpSecService.
903         if (!isNattKeepaliveSocketValid(fd, resourceId)) {
904             notifyErrorCallback(cb, ERROR_INVALID_SOCKET);
905             return null;
906         }
907 
908         // Get src port to adopt old API.
909         int srcPort = 0;
910         try {
911             final SocketAddress srcSockAddr = Os.getsockname(fd);
912             srcPort = ((InetSocketAddress) srcSockAddr).getPort();
913         } catch (ErrnoException e) {
914             notifyErrorCallback(cb, ERROR_INVALID_SOCKET);
915             return null;
916         }
917 
918         // Forward request to old API.
919         return makeNattKeepaliveInfo(nai, fd, intervalSeconds, cb, srcAddrString, srcPort,
920                 dstAddrString, dstPort);
921     }
922 
923     /**
924      * Verify if the IPsec NAT-T file descriptor and resource Id hold for IPsec keepalive is valid.
925      **/
isNattKeepaliveSocketValid(@ullable FileDescriptor fd, int resourceId)926     public static boolean isNattKeepaliveSocketValid(@Nullable FileDescriptor fd, int resourceId) {
927         // TODO: 1. confirm whether the fd is called from system api or created by IpSecService.
928         //       2. If the fd is created from the system api, check that it's bounded. And
929         //          call dup to keep the fd open.
930         //       3. If the fd is created from IpSecService, check if the resource ID is valid. And
931         //          hold the resource needed in IpSecService.
932         if (null == fd) {
933             return false;
934         }
935         return true;
936     }
937 
938     /**
939      * Dump KeepaliveTracker state.
940      */
dump(IndentingPrintWriter pw)941     public void dump(IndentingPrintWriter pw) {
942         pw.println("Supported Socket keepalives: " + Arrays.toString(mSupportedKeepalives));
943         pw.println("Reserved Privileged keepalives: " + mReservedPrivilegedSlots);
944         pw.println("Allowed Unprivileged keepalives per uid: " + mAllowedUnprivilegedSlotsForUid);
945         pw.println("Socket keepalives:");
946         pw.increaseIndent();
947         for (NetworkAgentInfo nai : mKeepalives.keySet()) {
948             pw.println(nai.toShortString());
949             pw.increaseIndent();
950             for (int slot : mKeepalives.get(nai).keySet()) {
951                 KeepaliveInfo ki = mKeepalives.get(nai).get(slot);
952                 pw.println(slot + ": " + ki.toString());
953             }
954             pw.decreaseIndent();
955         }
956         pw.decreaseIndent();
957     }
958 
959     /**
960      * Dependencies class for testing.
961      */
962     @VisibleForTesting
963     public static class Dependencies {
964         /**
965          * Read supported keepalive count for each transport type from overlay resource. This should
966          * be used to create a local variable store of resource customization, and set as the
967          * input for {@link getSupportedKeepalivesForNetworkCapabilities}.
968          *
969          * @param context The context to read resource from.
970          * @return An array of supported keepalive count for each transport type.
971          */
972         @NonNull
getSupportedKeepalives(@onNull Context context)973         public int[] getSupportedKeepalives(@NonNull Context context) {
974             return KeepaliveResourceUtil.getSupportedKeepalives(context);
975         }
976 
977         /**
978          * Create a new {@link ConnectivityResources}.
979          */
980         @NonNull
createConnectivityResources(@onNull Context context)981         public ConnectivityResources createConnectivityResources(@NonNull Context context) {
982             return new ConnectivityResources(context);
983         }
984 
985         /**
986          * Return if keepalive address translation with clat feature is supported or not.
987          *
988          * This is controlled by both isFeatureSupported() and isFeatureEnabled(). The
989          * isFeatureSupported() checks whether device contains the minimal required module
990          * version for FEATURE_CLAT_ADDRESS_TRANSLATE. The isTetheringFeatureForceDisabled()
991          * checks the DeviceConfig flag that can be updated via DeviceConfig push to control
992          * the overall feature.
993          */
isAddressTranslationEnabled(@onNull Context context)994         public boolean isAddressTranslationEnabled(@NonNull Context context) {
995             return DeviceConfigUtils.isFeatureSupported(context, FEATURE_CLAT_ADDRESS_TRANSLATE)
996                     && DeviceConfigUtils.isTetheringFeatureNotChickenedOut(context,
997                             CONFIG_DISABLE_CLAT_ADDRESS_TRANSLATE);
998         }
999     }
1000 }
1001