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 com.android.internal.util.HexDump; 20 import com.android.internal.util.IndentingPrintWriter; 21 import com.android.server.connectivity.KeepalivePacketData; 22 import com.android.server.connectivity.NetworkAgentInfo; 23 import android.net.ConnectivityManager; 24 import android.net.ConnectivityManager.PacketKeepalive; 25 import android.net.LinkAddress; 26 import android.net.NetworkAgent; 27 import android.net.NetworkUtils; 28 import android.net.util.IpUtils; 29 import android.os.Binder; 30 import android.os.IBinder; 31 import android.os.Handler; 32 import android.os.Message; 33 import android.os.Messenger; 34 import android.os.Process; 35 import android.os.RemoteException; 36 import android.system.OsConstants; 37 import android.util.Log; 38 import android.util.Pair; 39 40 import java.nio.ByteBuffer; 41 import java.nio.ByteOrder; 42 import java.net.Inet4Address; 43 import java.net.Inet6Address; 44 import java.net.InetAddress; 45 import java.util.ArrayList; 46 import java.util.HashMap; 47 48 import static android.net.ConnectivityManager.PacketKeepalive.*; 49 import static android.net.NetworkAgent.CMD_START_PACKET_KEEPALIVE; 50 import static android.net.NetworkAgent.CMD_STOP_PACKET_KEEPALIVE; 51 import static android.net.NetworkAgent.EVENT_PACKET_KEEPALIVE; 52 53 /** 54 * Manages packet keepalive requests. 55 * 56 * Provides methods to stop and start keepalive requests, and keeps track of keepalives across all 57 * networks. This class is tightly coupled to ConnectivityService. It is not thread-safe and its 58 * methods must be called only from the ConnectivityService handler thread. 59 */ 60 public class KeepaliveTracker { 61 62 private static final String TAG = "KeepaliveTracker"; 63 private static final boolean DBG = false; 64 65 public static final String PERMISSION = android.Manifest.permission.PACKET_KEEPALIVE_OFFLOAD; 66 67 /** Keeps track of keepalive requests. */ 68 private final HashMap <NetworkAgentInfo, HashMap<Integer, KeepaliveInfo>> mKeepalives = 69 new HashMap<> (); 70 private final Handler mConnectivityServiceHandler; 71 KeepaliveTracker(Handler handler)72 public KeepaliveTracker(Handler handler) { 73 mConnectivityServiceHandler = handler; 74 } 75 76 /** 77 * Tracks information about a packet keepalive. 78 * 79 * All information about this keepalive is known at construction time except the slot number, 80 * which is only returned when the hardware has successfully started the keepalive. 81 */ 82 class KeepaliveInfo implements IBinder.DeathRecipient { 83 // Bookkeping data. 84 private final Messenger mMessenger; 85 private final IBinder mBinder; 86 private final int mUid; 87 private final int mPid; 88 private final NetworkAgentInfo mNai; 89 90 /** Keepalive slot. A small integer that identifies this keepalive among the ones handled 91 * by this network. */ 92 private int mSlot = PacketKeepalive.NO_KEEPALIVE; 93 94 // Packet data. 95 private final KeepalivePacketData mPacket; 96 private final int mInterval; 97 98 // Whether the keepalive is started or not. 99 public boolean isStarted; 100 KeepaliveInfo(Messenger messenger, IBinder binder, NetworkAgentInfo nai, KeepalivePacketData packet, int interval)101 public KeepaliveInfo(Messenger messenger, IBinder binder, NetworkAgentInfo nai, 102 KeepalivePacketData packet, int interval) { 103 mMessenger = messenger; 104 mBinder = binder; 105 mPid = Binder.getCallingPid(); 106 mUid = Binder.getCallingUid(); 107 108 mNai = nai; 109 mPacket = packet; 110 mInterval = interval; 111 112 try { 113 mBinder.linkToDeath(this, 0); 114 } catch (RemoteException e) { 115 binderDied(); 116 } 117 } 118 getNai()119 public NetworkAgentInfo getNai() { 120 return mNai; 121 } 122 toString()123 public String toString() { 124 return new StringBuffer("KeepaliveInfo [") 125 .append(" network=").append(mNai.network) 126 .append(" isStarted=").append(isStarted) 127 .append(" ") 128 .append(IpUtils.addressAndPortToString(mPacket.srcAddress, mPacket.srcPort)) 129 .append("->") 130 .append(IpUtils.addressAndPortToString(mPacket.dstAddress, mPacket.dstPort)) 131 .append(" interval=" + mInterval) 132 .append(" data=" + HexDump.toHexString(mPacket.data)) 133 .append(" uid=").append(mUid).append(" pid=").append(mPid) 134 .append(" ]") 135 .toString(); 136 } 137 138 /** Sends a message back to the application via its PacketKeepalive.Callback. */ notifyMessenger(int slot, int err)139 void notifyMessenger(int slot, int err) { 140 KeepaliveTracker.this.notifyMessenger(mMessenger, slot, err); 141 } 142 143 /** Called when the application process is killed. */ binderDied()144 public void binderDied() { 145 // Not called from ConnectivityService handler thread, so send it a message. 146 mConnectivityServiceHandler.obtainMessage( 147 NetworkAgent.CMD_STOP_PACKET_KEEPALIVE, 148 mSlot, PacketKeepalive.BINDER_DIED, mNai.network).sendToTarget(); 149 } 150 unlinkDeathRecipient()151 void unlinkDeathRecipient() { 152 if (mBinder != null) { 153 mBinder.unlinkToDeath(this, 0); 154 } 155 } 156 checkNetworkConnected()157 private int checkNetworkConnected() { 158 if (!mNai.networkInfo.isConnectedOrConnecting()) { 159 return ERROR_INVALID_NETWORK; 160 } 161 return SUCCESS; 162 } 163 checkSourceAddress()164 private int checkSourceAddress() { 165 // Check that we have the source address. 166 for (InetAddress address : mNai.linkProperties.getAddresses()) { 167 if (address.equals(mPacket.srcAddress)) { 168 return SUCCESS; 169 } 170 } 171 return ERROR_INVALID_IP_ADDRESS; 172 } 173 checkInterval()174 private int checkInterval() { 175 return mInterval >= 20 ? SUCCESS : ERROR_INVALID_INTERVAL; 176 } 177 isValid()178 private int isValid() { 179 synchronized (mNai) { 180 int error = checkInterval(); 181 if (error == SUCCESS) error = checkNetworkConnected(); 182 if (error == SUCCESS) error = checkSourceAddress(); 183 return error; 184 } 185 } 186 start(int slot)187 void start(int slot) { 188 int error = isValid(); 189 if (error == SUCCESS) { 190 mSlot = slot; 191 Log.d(TAG, "Starting keepalive " + mSlot + " on " + mNai.name()); 192 mNai.asyncChannel.sendMessage(CMD_START_PACKET_KEEPALIVE, slot, mInterval, mPacket); 193 } else { 194 notifyMessenger(NO_KEEPALIVE, error); 195 return; 196 } 197 } 198 stop(int reason)199 void stop(int reason) { 200 int uid = Binder.getCallingUid(); 201 if (uid != mUid && uid != Process.SYSTEM_UID) { 202 if (DBG) { 203 Log.e(TAG, "Cannot stop unowned keepalive " + mSlot + " on " + mNai.network); 204 } 205 } 206 if (isStarted) { 207 Log.d(TAG, "Stopping keepalive " + mSlot + " on " + mNai.name()); 208 mNai.asyncChannel.sendMessage(CMD_STOP_PACKET_KEEPALIVE, mSlot); 209 } 210 // TODO: at the moment we unconditionally return failure here. In cases where the 211 // NetworkAgent is alive, should we ask it to reply, so it can return failure? 212 notifyMessenger(mSlot, reason); 213 unlinkDeathRecipient(); 214 } 215 } 216 notifyMessenger(Messenger messenger, int slot, int err)217 void notifyMessenger(Messenger messenger, int slot, int err) { 218 Message message = Message.obtain(); 219 message.what = EVENT_PACKET_KEEPALIVE; 220 message.arg1 = slot; 221 message.arg2 = err; 222 message.obj = null; 223 try { 224 messenger.send(message); 225 } catch (RemoteException e) { 226 // Process died? 227 } 228 } 229 findFirstFreeSlot(NetworkAgentInfo nai)230 private int findFirstFreeSlot(NetworkAgentInfo nai) { 231 HashMap networkKeepalives = mKeepalives.get(nai); 232 if (networkKeepalives == null) { 233 networkKeepalives = new HashMap<Integer, KeepaliveInfo>(); 234 mKeepalives.put(nai, networkKeepalives); 235 } 236 237 // Find the lowest-numbered free slot. Slot numbers start from 1, because that's what two 238 // separate chipset implementations independently came up with. 239 int slot; 240 for (slot = 1; slot <= networkKeepalives.size(); slot++) { 241 if (networkKeepalives.get(slot) == null) { 242 return slot; 243 } 244 } 245 return slot; 246 } 247 handleStartKeepalive(Message message)248 public void handleStartKeepalive(Message message) { 249 KeepaliveInfo ki = (KeepaliveInfo) message.obj; 250 NetworkAgentInfo nai = ki.getNai(); 251 int slot = findFirstFreeSlot(nai); 252 mKeepalives.get(nai).put(slot, ki); 253 ki.start(slot); 254 } 255 handleStopAllKeepalives(NetworkAgentInfo nai, int reason)256 public void handleStopAllKeepalives(NetworkAgentInfo nai, int reason) { 257 HashMap <Integer, KeepaliveInfo> networkKeepalives = mKeepalives.get(nai); 258 if (networkKeepalives != null) { 259 for (KeepaliveInfo ki : networkKeepalives.values()) { 260 ki.stop(reason); 261 } 262 networkKeepalives.clear(); 263 mKeepalives.remove(nai); 264 } 265 } 266 handleStopKeepalive(NetworkAgentInfo nai, int slot, int reason)267 public void handleStopKeepalive(NetworkAgentInfo nai, int slot, int reason) { 268 String networkName = (nai == null) ? "(null)" : nai.name(); 269 HashMap <Integer, KeepaliveInfo> networkKeepalives = mKeepalives.get(nai); 270 if (networkKeepalives == null) { 271 Log.e(TAG, "Attempt to stop keepalive on nonexistent network " + networkName); 272 return; 273 } 274 KeepaliveInfo ki = networkKeepalives.get(slot); 275 if (ki == null) { 276 Log.e(TAG, "Attempt to stop nonexistent keepalive " + slot + " on " + networkName); 277 return; 278 } 279 ki.stop(reason); 280 networkKeepalives.remove(slot); 281 if (networkKeepalives.isEmpty()) { 282 mKeepalives.remove(nai); 283 } 284 } 285 handleCheckKeepalivesStillValid(NetworkAgentInfo nai)286 public void handleCheckKeepalivesStillValid(NetworkAgentInfo nai) { 287 HashMap <Integer, KeepaliveInfo> networkKeepalives = mKeepalives.get(nai); 288 if (networkKeepalives != null) { 289 ArrayList<Pair<Integer, Integer>> invalidKeepalives = new ArrayList<>(); 290 for (int slot : networkKeepalives.keySet()) { 291 int error = networkKeepalives.get(slot).isValid(); 292 if (error != SUCCESS) { 293 invalidKeepalives.add(Pair.create(slot, error)); 294 } 295 } 296 for (Pair<Integer, Integer> slotAndError: invalidKeepalives) { 297 handleStopKeepalive(nai, slotAndError.first, slotAndError.second); 298 } 299 } 300 } 301 handleEventPacketKeepalive(NetworkAgentInfo nai, Message message)302 public void handleEventPacketKeepalive(NetworkAgentInfo nai, Message message) { 303 int slot = message.arg1; 304 int reason = message.arg2; 305 306 KeepaliveInfo ki = null; 307 try { 308 ki = mKeepalives.get(nai).get(slot); 309 } catch(NullPointerException e) {} 310 if (ki == null) { 311 Log.e(TAG, "Event for unknown keepalive " + slot + " on " + nai.name()); 312 return; 313 } 314 315 if (reason == SUCCESS && !ki.isStarted) { 316 // Keepalive successfully started. 317 if (DBG) Log.d(TAG, "Started keepalive " + slot + " on " + nai.name()); 318 ki.isStarted = true; 319 ki.notifyMessenger(slot, reason); 320 } else { 321 // Keepalive successfully stopped, or error. 322 ki.isStarted = false; 323 if (reason == SUCCESS) { 324 if (DBG) Log.d(TAG, "Successfully stopped keepalive " + slot + " on " + nai.name()); 325 } else { 326 if (DBG) Log.d(TAG, "Keepalive " + slot + " on " + nai.name() + " error " + reason); 327 } 328 handleStopKeepalive(nai, slot, reason); 329 } 330 } 331 startNattKeepalive(NetworkAgentInfo nai, int intervalSeconds, Messenger messenger, IBinder binder, String srcAddrString, int srcPort, String dstAddrString, int dstPort)332 public void startNattKeepalive(NetworkAgentInfo nai, int intervalSeconds, Messenger messenger, 333 IBinder binder, String srcAddrString, int srcPort, String dstAddrString, int dstPort) { 334 if (nai == null) { 335 notifyMessenger(messenger, NO_KEEPALIVE, ERROR_INVALID_NETWORK); 336 return; 337 } 338 339 InetAddress srcAddress, dstAddress; 340 try { 341 srcAddress = NetworkUtils.numericToInetAddress(srcAddrString); 342 dstAddress = NetworkUtils.numericToInetAddress(dstAddrString); 343 } catch (IllegalArgumentException e) { 344 notifyMessenger(messenger, NO_KEEPALIVE, ERROR_INVALID_IP_ADDRESS); 345 return; 346 } 347 348 KeepalivePacketData packet; 349 try { 350 packet = KeepalivePacketData.nattKeepalivePacket( 351 srcAddress, srcPort, dstAddress, NATT_PORT); 352 } catch (KeepalivePacketData.InvalidPacketException e) { 353 notifyMessenger(messenger, NO_KEEPALIVE, e.error); 354 return; 355 } 356 KeepaliveInfo ki = new KeepaliveInfo(messenger, binder, nai, packet, intervalSeconds); 357 Log.d(TAG, "Created keepalive: " + ki.toString()); 358 mConnectivityServiceHandler.obtainMessage( 359 NetworkAgent.CMD_START_PACKET_KEEPALIVE, ki).sendToTarget(); 360 } 361 dump(IndentingPrintWriter pw)362 public void dump(IndentingPrintWriter pw) { 363 pw.println("Packet keepalives:"); 364 pw.increaseIndent(); 365 for (NetworkAgentInfo nai : mKeepalives.keySet()) { 366 pw.println(nai.name()); 367 pw.increaseIndent(); 368 for (int slot : mKeepalives.get(nai).keySet()) { 369 KeepaliveInfo ki = mKeepalives.get(nai).get(slot); 370 pw.println(slot + ": " + ki.toString()); 371 } 372 pw.decreaseIndent(); 373 } 374 pw.decreaseIndent(); 375 } 376 } 377