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