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.NetworkAgent.CMD_ADD_KEEPALIVE_PACKET_FILTER; 22 import static android.net.NetworkAgent.CMD_REMOVE_KEEPALIVE_PACKET_FILTER; 23 import static android.net.NetworkAgent.CMD_START_SOCKET_KEEPALIVE; 24 import static android.net.NetworkAgent.CMD_STOP_SOCKET_KEEPALIVE; 25 import static android.net.SocketKeepalive.BINDER_DIED; 26 import static android.net.SocketKeepalive.DATA_RECEIVED; 27 import static android.net.SocketKeepalive.ERROR_INSUFFICIENT_RESOURCES; 28 import static android.net.SocketKeepalive.ERROR_INVALID_INTERVAL; 29 import static android.net.SocketKeepalive.ERROR_INVALID_IP_ADDRESS; 30 import static android.net.SocketKeepalive.ERROR_INVALID_NETWORK; 31 import static android.net.SocketKeepalive.ERROR_INVALID_SOCKET; 32 import static android.net.SocketKeepalive.ERROR_UNSUPPORTED; 33 import static android.net.SocketKeepalive.MAX_INTERVAL_SEC; 34 import static android.net.SocketKeepalive.MIN_INTERVAL_SEC; 35 import static android.net.SocketKeepalive.NO_KEEPALIVE; 36 import static android.net.SocketKeepalive.SUCCESS; 37 38 import android.annotation.NonNull; 39 import android.annotation.Nullable; 40 import android.content.Context; 41 import android.net.ISocketKeepaliveCallback; 42 import android.net.InvalidPacketException; 43 import android.net.KeepalivePacketData; 44 import android.net.NattKeepalivePacketData; 45 import android.net.NetworkAgent; 46 import android.net.NetworkUtils; 47 import android.net.SocketKeepalive.InvalidSocketException; 48 import android.net.TcpKeepalivePacketData; 49 import android.net.util.IpUtils; 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.Message; 55 import android.os.Process; 56 import android.os.RemoteException; 57 import android.system.ErrnoException; 58 import android.system.Os; 59 import android.util.Log; 60 import android.util.Pair; 61 62 import com.android.internal.R; 63 import com.android.internal.util.HexDump; 64 import com.android.internal.util.IndentingPrintWriter; 65 66 import java.io.FileDescriptor; 67 import java.net.InetAddress; 68 import java.net.InetSocketAddress; 69 import java.net.SocketAddress; 70 import java.util.ArrayList; 71 import java.util.Arrays; 72 import java.util.HashMap; 73 74 /** 75 * Manages socket keepalive requests. 76 * 77 * Provides methods to stop and start keepalive requests, and keeps track of keepalives across all 78 * networks. This class is tightly coupled to ConnectivityService. It is not thread-safe and its 79 * handle* methods must be called only from the ConnectivityService handler thread. 80 */ 81 public class KeepaliveTracker { 82 83 private static final String TAG = "KeepaliveTracker"; 84 private static final boolean DBG = false; 85 86 public static final String PERMISSION = android.Manifest.permission.PACKET_KEEPALIVE_OFFLOAD; 87 88 /** Keeps track of keepalive requests. */ 89 private final HashMap <NetworkAgentInfo, HashMap<Integer, KeepaliveInfo>> mKeepalives = 90 new HashMap<> (); 91 private final Handler mConnectivityServiceHandler; 92 @NonNull 93 private final TcpKeepaliveController mTcpController; 94 @NonNull 95 private final Context mContext; 96 97 // Supported keepalive count for each transport type, can be configured through 98 // config_networkSupportedKeepaliveCount. For better error handling, use 99 // {@link getSupportedKeepalivesForNetworkCapabilities} instead of direct access. 100 @NonNull 101 private final int[] mSupportedKeepalives; 102 103 // Reserved privileged keepalive slots per transport. Caller's permission will be enforced if 104 // the number of remaining keepalive slots is less than or equal to the threshold. 105 private final int mReservedPrivilegedSlots; 106 107 // Allowed unprivileged keepalive slots per uid. Caller's permission will be enforced if 108 // the number of remaining keepalive slots is less than or equal to the threshold. 109 private final int mAllowedUnprivilegedSlotsForUid; 110 KeepaliveTracker(Context context, Handler handler)111 public KeepaliveTracker(Context context, Handler handler) { 112 mConnectivityServiceHandler = handler; 113 mTcpController = new TcpKeepaliveController(handler); 114 mContext = context; 115 mSupportedKeepalives = KeepaliveUtils.getSupportedKeepalives(mContext); 116 mReservedPrivilegedSlots = mContext.getResources().getInteger( 117 R.integer.config_reservedPrivilegedKeepaliveSlots); 118 mAllowedUnprivilegedSlotsForUid = mContext.getResources().getInteger( 119 R.integer.config_allowedUnprivilegedKeepalivePerUid); 120 } 121 122 /** 123 * Tracks information about a socket keepalive. 124 * 125 * All information about this keepalive is known at construction time except the slot number, 126 * which is only returned when the hardware has successfully started the keepalive. 127 */ 128 class KeepaliveInfo implements IBinder.DeathRecipient { 129 // Bookkeeping data. 130 private final ISocketKeepaliveCallback mCallback; 131 private final int mUid; 132 private final int mPid; 133 private final boolean mPrivileged; 134 private final NetworkAgentInfo mNai; 135 private final int mType; 136 private final FileDescriptor mFd; 137 138 public static final int TYPE_NATT = 1; 139 public static final int TYPE_TCP = 2; 140 141 // Keepalive slot. A small integer that identifies this keepalive among the ones handled 142 // by this network. 143 private int mSlot = NO_KEEPALIVE; 144 145 // Packet data. 146 private final KeepalivePacketData mPacket; 147 private final int mInterval; 148 149 // Whether the keepalive is started or not. The initial state is NOT_STARTED. 150 private static final int NOT_STARTED = 1; 151 private static final int STARTING = 2; 152 private static final int STARTED = 3; 153 private static final int STOPPING = 4; 154 private int mStartedState = NOT_STARTED; 155 KeepaliveInfo(@onNull ISocketKeepaliveCallback callback, @NonNull NetworkAgentInfo nai, @NonNull KeepalivePacketData packet, int interval, int type, @Nullable FileDescriptor fd)156 KeepaliveInfo(@NonNull ISocketKeepaliveCallback callback, 157 @NonNull NetworkAgentInfo nai, 158 @NonNull KeepalivePacketData packet, 159 int interval, 160 int type, 161 @Nullable FileDescriptor fd) throws InvalidSocketException { 162 mCallback = callback; 163 mPid = Binder.getCallingPid(); 164 mUid = Binder.getCallingUid(); 165 mPrivileged = (PERMISSION_GRANTED == mContext.checkPermission(PERMISSION, mPid, mUid)); 166 167 mNai = nai; 168 mPacket = packet; 169 mInterval = interval; 170 mType = type; 171 172 // For SocketKeepalive, a dup of fd is kept in mFd so the source port from which the 173 // keepalives are sent cannot be reused by another app even if the fd gets closed by 174 // the user. A null is acceptable here for backward compatibility of PacketKeepalive 175 // API. 176 try { 177 if (fd != null) { 178 mFd = Os.dup(fd); 179 } else { 180 Log.d(TAG, toString() + " calls with null fd"); 181 if (!mPrivileged) { 182 throw new SecurityException( 183 "null fd is not allowed for unprivileged access."); 184 } 185 if (mType == TYPE_TCP) { 186 throw new IllegalArgumentException( 187 "null fd is not allowed for tcp socket keepalives."); 188 } 189 mFd = null; 190 } 191 } catch (ErrnoException e) { 192 Log.e(TAG, "Cannot dup fd: ", e); 193 throw new InvalidSocketException(ERROR_INVALID_SOCKET, e); 194 } 195 196 try { 197 mCallback.asBinder().linkToDeath(this, 0); 198 } catch (RemoteException e) { 199 binderDied(); 200 } 201 } 202 getNai()203 public NetworkAgentInfo getNai() { 204 return mNai; 205 } 206 startedStateString(final int state)207 private String startedStateString(final int state) { 208 switch (state) { 209 case NOT_STARTED : return "NOT_STARTED"; 210 case STARTING : return "STARTING"; 211 case STARTED : return "STARTED"; 212 case STOPPING : return "STOPPING"; 213 } 214 throw new IllegalArgumentException("Unknown state"); 215 } 216 toString()217 public String toString() { 218 return "KeepaliveInfo [" 219 + " type=" + mType 220 + " network=" + mNai.network 221 + " startedState=" + startedStateString(mStartedState) 222 + " " 223 + IpUtils.addressAndPortToString(mPacket.getSrcAddress(), mPacket.getSrcPort()) 224 + "->" 225 + IpUtils.addressAndPortToString(mPacket.getDstAddress(), mPacket.getDstPort()) 226 + " interval=" + mInterval 227 + " uid=" + mUid + " pid=" + mPid + " privileged=" + mPrivileged 228 + " packetData=" + HexDump.toHexString(mPacket.getPacket()) 229 + " ]"; 230 } 231 232 /** Called when the application process is killed. */ binderDied()233 public void binderDied() { 234 stop(BINDER_DIED); 235 } 236 unlinkDeathRecipient()237 void unlinkDeathRecipient() { 238 if (mCallback != null) { 239 mCallback.asBinder().unlinkToDeath(this, 0); 240 } 241 } 242 checkNetworkConnected()243 private int checkNetworkConnected() { 244 if (!mNai.networkInfo.isConnectedOrConnecting()) { 245 return ERROR_INVALID_NETWORK; 246 } 247 return SUCCESS; 248 } 249 checkSourceAddress()250 private int checkSourceAddress() { 251 // Check that we have the source address. 252 for (InetAddress address : mNai.linkProperties.getAddresses()) { 253 if (address.equals(mPacket.getSrcAddress())) { 254 return SUCCESS; 255 } 256 } 257 return ERROR_INVALID_IP_ADDRESS; 258 } 259 checkInterval()260 private int checkInterval() { 261 if (mInterval < MIN_INTERVAL_SEC || mInterval > MAX_INTERVAL_SEC) { 262 return ERROR_INVALID_INTERVAL; 263 } 264 return SUCCESS; 265 } 266 checkPermission()267 private int checkPermission() { 268 final HashMap<Integer, KeepaliveInfo> networkKeepalives = mKeepalives.get(mNai); 269 if (networkKeepalives == null) { 270 return ERROR_INVALID_NETWORK; 271 } 272 273 if (mPrivileged) return SUCCESS; 274 275 final int supported = KeepaliveUtils.getSupportedKeepalivesForNetworkCapabilities( 276 mSupportedKeepalives, mNai.networkCapabilities); 277 278 int takenUnprivilegedSlots = 0; 279 for (final KeepaliveInfo ki : networkKeepalives.values()) { 280 if (!ki.mPrivileged) ++takenUnprivilegedSlots; 281 } 282 if (takenUnprivilegedSlots > supported - mReservedPrivilegedSlots) { 283 return ERROR_INSUFFICIENT_RESOURCES; 284 } 285 286 // Count unprivileged keepalives for the same uid across networks. 287 int unprivilegedCountSameUid = 0; 288 for (final HashMap<Integer, KeepaliveInfo> kaForNetwork : mKeepalives.values()) { 289 for (final KeepaliveInfo ki : kaForNetwork.values()) { 290 if (ki.mUid == mUid) { 291 unprivilegedCountSameUid++; 292 } 293 } 294 } 295 if (unprivilegedCountSameUid > mAllowedUnprivilegedSlotsForUid) { 296 return ERROR_INSUFFICIENT_RESOURCES; 297 } 298 return SUCCESS; 299 } 300 checkLimit()301 private int checkLimit() { 302 final HashMap<Integer, KeepaliveInfo> networkKeepalives = mKeepalives.get(mNai); 303 if (networkKeepalives == null) { 304 return ERROR_INVALID_NETWORK; 305 } 306 final int supported = KeepaliveUtils.getSupportedKeepalivesForNetworkCapabilities( 307 mSupportedKeepalives, mNai.networkCapabilities); 308 if (supported == 0) return ERROR_UNSUPPORTED; 309 if (networkKeepalives.size() > supported) return ERROR_INSUFFICIENT_RESOURCES; 310 return SUCCESS; 311 } 312 isValid()313 private int isValid() { 314 synchronized (mNai) { 315 int error = checkInterval(); 316 if (error == SUCCESS) error = checkLimit(); 317 if (error == SUCCESS) error = checkPermission(); 318 if (error == SUCCESS) error = checkNetworkConnected(); 319 if (error == SUCCESS) error = checkSourceAddress(); 320 return error; 321 } 322 } 323 start(int slot)324 void start(int slot) { 325 mSlot = slot; 326 int error = isValid(); 327 if (error == SUCCESS) { 328 Log.d(TAG, "Starting keepalive " + mSlot + " on " + mNai.toShortString()); 329 switch (mType) { 330 case TYPE_NATT: 331 mNai.asyncChannel.sendMessage( 332 CMD_ADD_KEEPALIVE_PACKET_FILTER, slot, 0 /* Unused */, mPacket); 333 mNai.asyncChannel 334 .sendMessage(CMD_START_SOCKET_KEEPALIVE, slot, mInterval, mPacket); 335 break; 336 case TYPE_TCP: 337 try { 338 mTcpController.startSocketMonitor(mFd, this, mSlot); 339 } catch (InvalidSocketException e) { 340 handleStopKeepalive(mNai, mSlot, ERROR_INVALID_SOCKET); 341 return; 342 } 343 mNai.asyncChannel.sendMessage( 344 CMD_ADD_KEEPALIVE_PACKET_FILTER, slot, 0 /* Unused */, mPacket); 345 // TODO: check result from apf and notify of failure as needed. 346 mNai.asyncChannel 347 .sendMessage(CMD_START_SOCKET_KEEPALIVE, slot, mInterval, mPacket); 348 break; 349 default: 350 Log.wtf(TAG, "Starting keepalive with unknown type: " + mType); 351 handleStopKeepalive(mNai, mSlot, error); 352 return; 353 } 354 mStartedState = STARTING; 355 } else { 356 handleStopKeepalive(mNai, mSlot, error); 357 return; 358 } 359 } 360 stop(int reason)361 void stop(int reason) { 362 int uid = Binder.getCallingUid(); 363 if (uid != mUid && uid != Process.SYSTEM_UID) { 364 if (DBG) { 365 Log.e(TAG, "Cannot stop unowned keepalive " + mSlot + " on " + mNai.network); 366 } 367 } 368 Log.d(TAG, "Stopping keepalive " + mSlot + " on " + mNai.toShortString() 369 + ": " + reason); 370 switch (mStartedState) { 371 case NOT_STARTED: 372 // Remove the reference of the keepalive that meet error before starting, 373 // e.g. invalid parameter. 374 cleanupStoppedKeepalive(mNai, mSlot); 375 break; 376 case STOPPING: 377 // Keepalive is already in stopping state, ignore. 378 return; 379 default: 380 mStartedState = STOPPING; 381 switch (mType) { 382 case TYPE_TCP: 383 mTcpController.stopSocketMonitor(mSlot); 384 // fall through 385 case TYPE_NATT: 386 mNai.asyncChannel.sendMessage(CMD_STOP_SOCKET_KEEPALIVE, mSlot); 387 mNai.asyncChannel.sendMessage(CMD_REMOVE_KEEPALIVE_PACKET_FILTER, 388 mSlot); 389 break; 390 default: 391 Log.wtf(TAG, "Stopping keepalive with unknown type: " + mType); 392 } 393 } 394 395 // Close the duplicated fd that maintains the lifecycle of socket whenever 396 // keepalive is running. 397 if (mFd != null) { 398 try { 399 Os.close(mFd); 400 } catch (ErrnoException e) { 401 // This should not happen since system server controls the lifecycle of fd when 402 // keepalive offload is running. 403 Log.wtf(TAG, "Error closing fd for keepalive " + mSlot + ": " + e); 404 } 405 } 406 407 if (reason == SUCCESS) { 408 try { 409 mCallback.onStopped(); 410 } catch (RemoteException e) { 411 Log.w(TAG, "Discarded onStop callback: " + reason); 412 } 413 } else if (reason == DATA_RECEIVED) { 414 try { 415 mCallback.onDataReceived(); 416 } catch (RemoteException e) { 417 Log.w(TAG, "Discarded onDataReceived callback: " + reason); 418 } 419 } else { 420 notifyErrorCallback(mCallback, reason); 421 } 422 423 unlinkDeathRecipient(); 424 } 425 onFileDescriptorInitiatedStop(final int socketKeepaliveReason)426 void onFileDescriptorInitiatedStop(final int socketKeepaliveReason) { 427 handleStopKeepalive(mNai, mSlot, socketKeepaliveReason); 428 } 429 } 430 notifyErrorCallback(ISocketKeepaliveCallback cb, int error)431 void notifyErrorCallback(ISocketKeepaliveCallback cb, int error) { 432 if (DBG) Log.w(TAG, "Sending onError(" + error + ") callback"); 433 try { 434 cb.onError(error); 435 } catch (RemoteException e) { 436 Log.w(TAG, "Discarded onError(" + error + ") callback"); 437 } 438 } 439 findFirstFreeSlot(NetworkAgentInfo nai)440 private int findFirstFreeSlot(NetworkAgentInfo nai) { 441 HashMap networkKeepalives = mKeepalives.get(nai); 442 if (networkKeepalives == null) { 443 networkKeepalives = new HashMap<Integer, KeepaliveInfo>(); 444 mKeepalives.put(nai, networkKeepalives); 445 } 446 447 // Find the lowest-numbered free slot. Slot numbers start from 1, because that's what two 448 // separate chipset implementations independently came up with. 449 int slot; 450 for (slot = 1; slot <= networkKeepalives.size(); slot++) { 451 if (networkKeepalives.get(slot) == null) { 452 return slot; 453 } 454 } 455 return slot; 456 } 457 handleStartKeepalive(Message message)458 public void handleStartKeepalive(Message message) { 459 KeepaliveInfo ki = (KeepaliveInfo) message.obj; 460 NetworkAgentInfo nai = ki.getNai(); 461 int slot = findFirstFreeSlot(nai); 462 mKeepalives.get(nai).put(slot, ki); 463 ki.start(slot); 464 } 465 handleStopAllKeepalives(NetworkAgentInfo nai, int reason)466 public void handleStopAllKeepalives(NetworkAgentInfo nai, int reason) { 467 final HashMap<Integer, KeepaliveInfo> networkKeepalives = mKeepalives.get(nai); 468 if (networkKeepalives != null) { 469 final ArrayList<KeepaliveInfo> kalist = new ArrayList(networkKeepalives.values()); 470 for (KeepaliveInfo ki : kalist) { 471 ki.stop(reason); 472 // Clean up keepalives since the network agent is disconnected and unable to pass 473 // back asynchronous result of stop(). 474 cleanupStoppedKeepalive(nai, ki.mSlot); 475 } 476 } 477 } 478 handleStopKeepalive(NetworkAgentInfo nai, int slot, int reason)479 public void handleStopKeepalive(NetworkAgentInfo nai, int slot, int reason) { 480 final String networkName = NetworkAgentInfo.toShortString(nai); 481 HashMap <Integer, KeepaliveInfo> networkKeepalives = mKeepalives.get(nai); 482 if (networkKeepalives == null) { 483 Log.e(TAG, "Attempt to stop keepalive on nonexistent network " + networkName); 484 return; 485 } 486 KeepaliveInfo ki = networkKeepalives.get(slot); 487 if (ki == null) { 488 Log.e(TAG, "Attempt to stop nonexistent keepalive " + slot + " on " + networkName); 489 return; 490 } 491 ki.stop(reason); 492 // Clean up keepalives will be done as a result of calling ki.stop() after the slots are 493 // freed. 494 } 495 cleanupStoppedKeepalive(NetworkAgentInfo nai, int slot)496 private void cleanupStoppedKeepalive(NetworkAgentInfo nai, int slot) { 497 final String networkName = NetworkAgentInfo.toShortString(nai); 498 HashMap<Integer, KeepaliveInfo> networkKeepalives = mKeepalives.get(nai); 499 if (networkKeepalives == null) { 500 Log.e(TAG, "Attempt to remove keepalive on nonexistent network " + networkName); 501 return; 502 } 503 KeepaliveInfo ki = networkKeepalives.get(slot); 504 if (ki == null) { 505 Log.e(TAG, "Attempt to remove nonexistent keepalive " + slot + " on " + networkName); 506 return; 507 } 508 networkKeepalives.remove(slot); 509 Log.d(TAG, "Remove keepalive " + slot + " on " + networkName + ", " 510 + networkKeepalives.size() + " remains."); 511 if (networkKeepalives.isEmpty()) { 512 mKeepalives.remove(nai); 513 } 514 } 515 handleCheckKeepalivesStillValid(NetworkAgentInfo nai)516 public void handleCheckKeepalivesStillValid(NetworkAgentInfo nai) { 517 HashMap <Integer, KeepaliveInfo> networkKeepalives = mKeepalives.get(nai); 518 if (networkKeepalives != null) { 519 ArrayList<Pair<Integer, Integer>> invalidKeepalives = new ArrayList<>(); 520 for (int slot : networkKeepalives.keySet()) { 521 int error = networkKeepalives.get(slot).isValid(); 522 if (error != SUCCESS) { 523 invalidKeepalives.add(Pair.create(slot, error)); 524 } 525 } 526 for (Pair<Integer, Integer> slotAndError: invalidKeepalives) { 527 handleStopKeepalive(nai, slotAndError.first, slotAndError.second); 528 } 529 } 530 } 531 532 /** Handle keepalive events from lower layer. */ handleEventSocketKeepalive(@onNull NetworkAgentInfo nai, @NonNull Message message)533 public void handleEventSocketKeepalive(@NonNull NetworkAgentInfo nai, 534 @NonNull Message message) { 535 int slot = message.arg1; 536 int reason = message.arg2; 537 538 KeepaliveInfo ki = null; 539 try { 540 ki = mKeepalives.get(nai).get(slot); 541 } catch(NullPointerException e) {} 542 if (ki == null) { 543 Log.e(TAG, "Event " + message.what + "," + slot + "," + reason 544 + " for unknown keepalive " + slot + " on " + nai.toShortString()); 545 return; 546 } 547 548 // This can be called in a number of situations : 549 // - startedState is STARTING. 550 // - reason is SUCCESS => go to STARTED. 551 // - reason isn't SUCCESS => it's an error starting. Go to NOT_STARTED and stop keepalive. 552 // - startedState is STARTED. 553 // - reason is SUCCESS => it's a success stopping. Go to NOT_STARTED and stop keepalive. 554 // - reason isn't SUCCESS => it's an error in exec. Go to NOT_STARTED and stop keepalive. 555 // The control is not supposed to ever come here if the state is NOT_STARTED. This is 556 // because in NOT_STARTED state, the code will switch to STARTING before sending messages 557 // to start, and the only way to NOT_STARTED is this function, through the edges outlined 558 // above : in all cases, keepalive gets stopped and can't restart without going into 559 // STARTING as messages are ordered. This also depends on the hardware processing the 560 // messages in order. 561 // TODO : clarify this code and get rid of mStartedState. Using a StateMachine is an 562 // option. 563 if (KeepaliveInfo.STARTING == ki.mStartedState) { 564 if (SUCCESS == reason) { 565 // Keepalive successfully started. 566 Log.d(TAG, "Started keepalive " + slot + " on " + nai.toShortString()); 567 ki.mStartedState = KeepaliveInfo.STARTED; 568 try { 569 ki.mCallback.onStarted(slot); 570 } catch (RemoteException e) { 571 Log.w(TAG, "Discarded onStarted(" + slot + ") callback"); 572 } 573 } else { 574 Log.d(TAG, "Failed to start keepalive " + slot + " on " + nai.toShortString() 575 + ": " + reason); 576 // The message indicated some error trying to start: do call handleStopKeepalive. 577 handleStopKeepalive(nai, slot, reason); 578 } 579 } else if (KeepaliveInfo.STOPPING == ki.mStartedState) { 580 // The message indicated result of stopping : clean up keepalive slots. 581 Log.d(TAG, "Stopped keepalive " + slot + " on " + nai.toShortString() 582 + " stopped: " + reason); 583 ki.mStartedState = KeepaliveInfo.NOT_STARTED; 584 cleanupStoppedKeepalive(nai, slot); 585 } else { 586 Log.wtf(TAG, "Event " + message.what + "," + slot + "," + reason 587 + " for keepalive in wrong state: " + ki.toString()); 588 } 589 } 590 591 /** 592 * Called when requesting that keepalives be started on a IPsec NAT-T socket. See 593 * {@link android.net.SocketKeepalive}. 594 **/ startNattKeepalive(@ullable NetworkAgentInfo nai, @Nullable FileDescriptor fd, int intervalSeconds, @NonNull ISocketKeepaliveCallback cb, @NonNull String srcAddrString, int srcPort, @NonNull String dstAddrString, int dstPort)595 public void startNattKeepalive(@Nullable NetworkAgentInfo nai, 596 @Nullable FileDescriptor fd, 597 int intervalSeconds, 598 @NonNull ISocketKeepaliveCallback cb, 599 @NonNull String srcAddrString, 600 int srcPort, 601 @NonNull String dstAddrString, 602 int dstPort) { 603 if (nai == null) { 604 notifyErrorCallback(cb, ERROR_INVALID_NETWORK); 605 return; 606 } 607 608 InetAddress srcAddress, dstAddress; 609 try { 610 srcAddress = NetworkUtils.numericToInetAddress(srcAddrString); 611 dstAddress = NetworkUtils.numericToInetAddress(dstAddrString); 612 } catch (IllegalArgumentException e) { 613 notifyErrorCallback(cb, ERROR_INVALID_IP_ADDRESS); 614 return; 615 } 616 617 KeepalivePacketData packet; 618 try { 619 packet = NattKeepalivePacketData.nattKeepalivePacket( 620 srcAddress, srcPort, dstAddress, NATT_PORT); 621 } catch (InvalidPacketException e) { 622 notifyErrorCallback(cb, e.getError()); 623 return; 624 } 625 KeepaliveInfo ki = null; 626 try { 627 ki = new KeepaliveInfo(cb, nai, packet, intervalSeconds, 628 KeepaliveInfo.TYPE_NATT, fd); 629 } catch (InvalidSocketException | IllegalArgumentException | SecurityException e) { 630 Log.e(TAG, "Fail to construct keepalive", e); 631 notifyErrorCallback(cb, ERROR_INVALID_SOCKET); 632 return; 633 } 634 Log.d(TAG, "Created keepalive: " + ki.toString()); 635 mConnectivityServiceHandler.obtainMessage( 636 NetworkAgent.CMD_START_SOCKET_KEEPALIVE, ki).sendToTarget(); 637 } 638 639 /** 640 * Called by ConnectivityService to start TCP keepalive on a file descriptor. 641 * 642 * In order to offload keepalive for application correctly, sequence number, ack number and 643 * other fields are needed to form the keepalive packet. Thus, this function synchronously 644 * puts the socket into repair mode to get the necessary information. After the socket has been 645 * put into repair mode, the application cannot access the socket until reverted to normal. 646 * 647 * See {@link android.net.SocketKeepalive}. 648 **/ startTcpKeepalive(@ullable NetworkAgentInfo nai, @NonNull FileDescriptor fd, int intervalSeconds, @NonNull ISocketKeepaliveCallback cb)649 public void startTcpKeepalive(@Nullable NetworkAgentInfo nai, 650 @NonNull FileDescriptor fd, 651 int intervalSeconds, 652 @NonNull ISocketKeepaliveCallback cb) { 653 if (nai == null) { 654 notifyErrorCallback(cb, ERROR_INVALID_NETWORK); 655 return; 656 } 657 658 final TcpKeepalivePacketData packet; 659 try { 660 packet = TcpKeepaliveController.getTcpKeepalivePacket(fd); 661 } catch (InvalidSocketException e) { 662 notifyErrorCallback(cb, e.error); 663 return; 664 } catch (InvalidPacketException e) { 665 notifyErrorCallback(cb, e.getError()); 666 return; 667 } 668 KeepaliveInfo ki = null; 669 try { 670 ki = new KeepaliveInfo(cb, nai, packet, intervalSeconds, 671 KeepaliveInfo.TYPE_TCP, fd); 672 } catch (InvalidSocketException | IllegalArgumentException | SecurityException e) { 673 Log.e(TAG, "Fail to construct keepalive e=" + e); 674 notifyErrorCallback(cb, ERROR_INVALID_SOCKET); 675 return; 676 } 677 Log.d(TAG, "Created keepalive: " + ki.toString()); 678 mConnectivityServiceHandler.obtainMessage(CMD_START_SOCKET_KEEPALIVE, ki).sendToTarget(); 679 } 680 681 /** 682 * Called when requesting that keepalives be started on a IPsec NAT-T socket. This function is 683 * identical to {@link #startNattKeepalive}, but also takes a {@code resourceId}, which is the 684 * resource index bound to the {@link UdpEncapsulationSocket} when creating by 685 * {@link com.android.server.IpSecService} to verify whether the given 686 * {@link UdpEncapsulationSocket} is legitimate. 687 **/ startNattKeepalive(@ullable NetworkAgentInfo nai, @Nullable FileDescriptor fd, int resourceId, int intervalSeconds, @NonNull ISocketKeepaliveCallback cb, @NonNull String srcAddrString, @NonNull String dstAddrString, int dstPort)688 public void startNattKeepalive(@Nullable NetworkAgentInfo nai, 689 @Nullable FileDescriptor fd, 690 int resourceId, 691 int intervalSeconds, 692 @NonNull ISocketKeepaliveCallback cb, 693 @NonNull String srcAddrString, 694 @NonNull String dstAddrString, 695 int dstPort) { 696 // Ensure that the socket is created by IpSecService. 697 if (!isNattKeepaliveSocketValid(fd, resourceId)) { 698 notifyErrorCallback(cb, ERROR_INVALID_SOCKET); 699 } 700 701 // Get src port to adopt old API. 702 int srcPort = 0; 703 try { 704 final SocketAddress srcSockAddr = Os.getsockname(fd); 705 srcPort = ((InetSocketAddress) srcSockAddr).getPort(); 706 } catch (ErrnoException e) { 707 notifyErrorCallback(cb, ERROR_INVALID_SOCKET); 708 } 709 710 // Forward request to old API. 711 startNattKeepalive(nai, fd, intervalSeconds, cb, srcAddrString, srcPort, 712 dstAddrString, dstPort); 713 } 714 715 /** 716 * Verify if the IPsec NAT-T file descriptor and resource Id hold for IPsec keepalive is valid. 717 **/ isNattKeepaliveSocketValid(@ullable FileDescriptor fd, int resourceId)718 public static boolean isNattKeepaliveSocketValid(@Nullable FileDescriptor fd, int resourceId) { 719 // TODO: 1. confirm whether the fd is called from system api or created by IpSecService. 720 // 2. If the fd is created from the system api, check that it's bounded. And 721 // call dup to keep the fd open. 722 // 3. If the fd is created from IpSecService, check if the resource ID is valid. And 723 // hold the resource needed in IpSecService. 724 if (null == fd) { 725 return false; 726 } 727 return true; 728 } 729 dump(IndentingPrintWriter pw)730 public void dump(IndentingPrintWriter pw) { 731 pw.println("Supported Socket keepalives: " + Arrays.toString(mSupportedKeepalives)); 732 pw.println("Reserved Privileged keepalives: " + mReservedPrivilegedSlots); 733 pw.println("Allowed Unprivileged keepalives per uid: " + mAllowedUnprivilegedSlotsForUid); 734 pw.println("Socket keepalives:"); 735 pw.increaseIndent(); 736 for (NetworkAgentInfo nai : mKeepalives.keySet()) { 737 pw.println(nai.toShortString()); 738 pw.increaseIndent(); 739 for (int slot : mKeepalives.get(nai).keySet()) { 740 KeepaliveInfo ki = mKeepalives.get(nai).get(slot); 741 pw.println(slot + ": " + ki.toString()); 742 } 743 pw.decreaseIndent(); 744 } 745 pw.decreaseIndent(); 746 } 747 } 748