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