1 /* 2 * Copyright (C) 2017 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 android.net.ip; 18 19 import static android.net.RouteInfo.RTN_UNICAST; 20 import static android.net.shared.IpConfigurationParcelableUtil.toStableParcelable; 21 22 import static com.android.server.util.PermissionUtil.checkNetworkStackCallingPermission; 23 24 import android.annotation.NonNull; 25 import android.content.Context; 26 import android.net.ConnectivityManager; 27 import android.net.DhcpResults; 28 import android.net.INetd; 29 import android.net.IpPrefix; 30 import android.net.LinkAddress; 31 import android.net.LinkProperties; 32 import android.net.NattKeepalivePacketDataParcelable; 33 import android.net.NetworkStackIpMemoryStore; 34 import android.net.ProvisioningConfigurationParcelable; 35 import android.net.ProxyInfo; 36 import android.net.RouteInfo; 37 import android.net.TcpKeepalivePacketDataParcelable; 38 import android.net.apf.ApfCapabilities; 39 import android.net.apf.ApfFilter; 40 import android.net.dhcp.DhcpClient; 41 import android.net.metrics.IpConnectivityLog; 42 import android.net.metrics.IpManagerEvent; 43 import android.net.shared.InitialConfiguration; 44 import android.net.shared.ProvisioningConfiguration; 45 import android.net.util.InterfaceParams; 46 import android.net.util.SharedLog; 47 import android.os.ConditionVariable; 48 import android.os.IBinder; 49 import android.os.Message; 50 import android.os.RemoteException; 51 import android.os.SystemClock; 52 import android.text.TextUtils; 53 import android.util.LocalLog; 54 import android.util.Log; 55 import android.util.Pair; 56 import android.util.SparseArray; 57 58 import com.android.internal.annotations.VisibleForTesting; 59 import com.android.internal.util.IState; 60 import com.android.internal.util.IndentingPrintWriter; 61 import com.android.internal.util.MessageUtils; 62 import com.android.internal.util.Preconditions; 63 import com.android.internal.util.State; 64 import com.android.internal.util.StateMachine; 65 import com.android.internal.util.WakeupMessage; 66 import com.android.server.NetworkObserverRegistry; 67 import com.android.server.NetworkStackService.NetworkStackServiceManager; 68 69 import java.io.FileDescriptor; 70 import java.io.PrintWriter; 71 import java.net.InetAddress; 72 import java.util.Collection; 73 import java.util.List; 74 import java.util.Objects; 75 import java.util.Set; 76 import java.util.concurrent.ConcurrentHashMap; 77 import java.util.concurrent.CountDownLatch; 78 import java.util.function.Predicate; 79 import java.util.stream.Collectors; 80 81 82 /** 83 * IpClient 84 * 85 * This class provides the interface to IP-layer provisioning and maintenance 86 * functionality that can be used by transport layers like Wi-Fi, Ethernet, 87 * et cetera. 88 * 89 * [ Lifetime ] 90 * IpClient is designed to be instantiated as soon as the interface name is 91 * known and can be as long-lived as the class containing it (i.e. declaring 92 * it "private final" is okay). 93 * 94 * @hide 95 */ 96 public class IpClient extends StateMachine { 97 private static final boolean DBG = false; 98 99 // For message logging. 100 private static final Class[] sMessageClasses = { IpClient.class, DhcpClient.class }; 101 private static final SparseArray<String> sWhatToString = 102 MessageUtils.findMessageNames(sMessageClasses); 103 // Two static concurrent hashmaps of interface name to logging classes. 104 // One holds StateMachine logs and the other connectivity packet logs. 105 private static final ConcurrentHashMap<String, SharedLog> sSmLogs = new ConcurrentHashMap<>(); 106 private static final ConcurrentHashMap<String, LocalLog> sPktLogs = new ConcurrentHashMap<>(); 107 private final NetworkStackIpMemoryStore mIpMemoryStore; 108 109 /** 110 * Dump all state machine and connectivity packet logs to the specified writer. 111 * @param skippedIfaces Interfaces for which logs should not be dumped. 112 */ dumpAllLogs(PrintWriter writer, Set<String> skippedIfaces)113 public static void dumpAllLogs(PrintWriter writer, Set<String> skippedIfaces) { 114 for (String ifname : sSmLogs.keySet()) { 115 if (skippedIfaces.contains(ifname)) continue; 116 117 writer.println(String.format("--- BEGIN %s ---", ifname)); 118 119 final SharedLog smLog = sSmLogs.get(ifname); 120 if (smLog != null) { 121 writer.println("State machine log:"); 122 smLog.dump(null, writer, null); 123 } 124 125 writer.println(""); 126 127 final LocalLog pktLog = sPktLogs.get(ifname); 128 if (pktLog != null) { 129 writer.println("Connectivity packet log:"); 130 pktLog.readOnlyLocalLog().dump(null, writer, null); 131 } 132 133 writer.println(String.format("--- END %s ---", ifname)); 134 } 135 } 136 137 // Use a wrapper class to log in order to ensure complete and detailed 138 // logging. This method is lighter weight than annotations/reflection 139 // and has the following benefits: 140 // 141 // - No invoked method can be forgotten. 142 // Any new method added to IpClient.Callback must be overridden 143 // here or it will never be called. 144 // 145 // - No invoking call site can be forgotten. 146 // Centralized logging in this way means call sites don't need to 147 // remember to log, and therefore no call site can be forgotten. 148 // 149 // - No variation in log format among call sites. 150 // Encourages logging of any available arguments, and all call sites 151 // are necessarily logged identically. 152 // 153 // NOTE: Log first because passed objects may or may not be thread-safe and 154 // once passed on to the callback they may be modified by another thread. 155 // 156 // TODO: Find an lighter weight approach. 157 public static class IpClientCallbacksWrapper { 158 private static final String PREFIX = "INVOKE "; 159 private final IIpClientCallbacks mCallback; 160 private final SharedLog mLog; 161 162 @VisibleForTesting IpClientCallbacksWrapper(IIpClientCallbacks callback, SharedLog log)163 protected IpClientCallbacksWrapper(IIpClientCallbacks callback, SharedLog log) { 164 mCallback = callback; 165 mLog = log; 166 } 167 log(String msg)168 private void log(String msg) { 169 mLog.log(PREFIX + msg); 170 } 171 log(String msg, Throwable e)172 private void log(String msg, Throwable e) { 173 mLog.e(PREFIX + msg, e); 174 } 175 onPreDhcpAction()176 public void onPreDhcpAction() { 177 log("onPreDhcpAction()"); 178 try { 179 mCallback.onPreDhcpAction(); 180 } catch (RemoteException e) { 181 log("Failed to call onPreDhcpAction", e); 182 } 183 } 184 onPostDhcpAction()185 public void onPostDhcpAction() { 186 log("onPostDhcpAction()"); 187 try { 188 mCallback.onPostDhcpAction(); 189 } catch (RemoteException e) { 190 log("Failed to call onPostDhcpAction", e); 191 } 192 } 193 onNewDhcpResults(DhcpResults dhcpResults)194 public void onNewDhcpResults(DhcpResults dhcpResults) { 195 log("onNewDhcpResults({" + dhcpResults + "})"); 196 try { 197 mCallback.onNewDhcpResults(toStableParcelable(dhcpResults)); 198 } catch (RemoteException e) { 199 log("Failed to call onNewDhcpResults", e); 200 } 201 } 202 onProvisioningSuccess(LinkProperties newLp)203 public void onProvisioningSuccess(LinkProperties newLp) { 204 log("onProvisioningSuccess({" + newLp + "})"); 205 try { 206 mCallback.onProvisioningSuccess(newLp); 207 } catch (RemoteException e) { 208 log("Failed to call onProvisioningSuccess", e); 209 } 210 } 211 onProvisioningFailure(LinkProperties newLp)212 public void onProvisioningFailure(LinkProperties newLp) { 213 log("onProvisioningFailure({" + newLp + "})"); 214 try { 215 mCallback.onProvisioningFailure(newLp); 216 } catch (RemoteException e) { 217 log("Failed to call onProvisioningFailure", e); 218 } 219 } 220 onLinkPropertiesChange(LinkProperties newLp)221 public void onLinkPropertiesChange(LinkProperties newLp) { 222 log("onLinkPropertiesChange({" + newLp + "})"); 223 try { 224 mCallback.onLinkPropertiesChange(newLp); 225 } catch (RemoteException e) { 226 log("Failed to call onLinkPropertiesChange", e); 227 } 228 } 229 onReachabilityLost(String logMsg)230 public void onReachabilityLost(String logMsg) { 231 log("onReachabilityLost(" + logMsg + ")"); 232 try { 233 mCallback.onReachabilityLost(logMsg); 234 } catch (RemoteException e) { 235 log("Failed to call onReachabilityLost", e); 236 } 237 } 238 onQuit()239 public void onQuit() { 240 log("onQuit()"); 241 try { 242 mCallback.onQuit(); 243 } catch (RemoteException e) { 244 log("Failed to call onQuit", e); 245 } 246 } 247 installPacketFilter(byte[] filter)248 public void installPacketFilter(byte[] filter) { 249 log("installPacketFilter(byte[" + filter.length + "])"); 250 try { 251 mCallback.installPacketFilter(filter); 252 } catch (RemoteException e) { 253 log("Failed to call installPacketFilter", e); 254 } 255 } 256 startReadPacketFilter()257 public void startReadPacketFilter() { 258 log("startReadPacketFilter()"); 259 try { 260 mCallback.startReadPacketFilter(); 261 } catch (RemoteException e) { 262 log("Failed to call startReadPacketFilter", e); 263 } 264 } 265 setFallbackMulticastFilter(boolean enabled)266 public void setFallbackMulticastFilter(boolean enabled) { 267 log("setFallbackMulticastFilter(" + enabled + ")"); 268 try { 269 mCallback.setFallbackMulticastFilter(enabled); 270 } catch (RemoteException e) { 271 log("Failed to call setFallbackMulticastFilter", e); 272 } 273 } 274 setNeighborDiscoveryOffload(boolean enable)275 public void setNeighborDiscoveryOffload(boolean enable) { 276 log("setNeighborDiscoveryOffload(" + enable + ")"); 277 try { 278 mCallback.setNeighborDiscoveryOffload(enable); 279 } catch (RemoteException e) { 280 log("Failed to call setNeighborDiscoveryOffload", e); 281 } 282 } 283 } 284 285 public static final String DUMP_ARG_CONFIRM = "confirm"; 286 287 // Below constants are picked up by MessageUtils and exempt from ProGuard optimization. 288 private static final int CMD_TERMINATE_AFTER_STOP = 1; 289 private static final int CMD_STOP = 2; 290 private static final int CMD_START = 3; 291 private static final int CMD_CONFIRM = 4; 292 private static final int EVENT_PRE_DHCP_ACTION_COMPLETE = 5; 293 // Triggered by NetlinkTracker to communicate netlink events. 294 private static final int EVENT_NETLINK_LINKPROPERTIES_CHANGED = 6; 295 private static final int CMD_UPDATE_TCP_BUFFER_SIZES = 7; 296 private static final int CMD_UPDATE_HTTP_PROXY = 8; 297 private static final int CMD_SET_MULTICAST_FILTER = 9; 298 private static final int EVENT_PROVISIONING_TIMEOUT = 10; 299 private static final int EVENT_DHCPACTION_TIMEOUT = 11; 300 private static final int EVENT_READ_PACKET_FILTER_COMPLETE = 12; 301 private static final int CMD_ADD_KEEPALIVE_PACKET_FILTER_TO_APF = 13; 302 private static final int CMD_REMOVE_KEEPALIVE_PACKET_FILTER_FROM_APF = 14; 303 private static final int CMD_UPDATE_L2KEY_GROUPHINT = 15; 304 305 // Internal commands to use instead of trying to call transitionTo() inside 306 // a given State's enter() method. Calling transitionTo() from enter/exit 307 // encounters a Log.wtf() that can cause trouble on eng builds. 308 private static final int CMD_JUMP_STARTED_TO_RUNNING = 100; 309 private static final int CMD_JUMP_RUNNING_TO_STOPPING = 101; 310 private static final int CMD_JUMP_STOPPING_TO_STOPPED = 102; 311 312 // IpClient shares a handler with DhcpClient: commands must not overlap 313 public static final int DHCPCLIENT_CMD_BASE = 1000; 314 315 private static final int MAX_LOG_RECORDS = 500; 316 private static final int MAX_PACKET_RECORDS = 100; 317 318 private static final boolean NO_CALLBACKS = false; 319 private static final boolean SEND_CALLBACKS = true; 320 321 // This must match the interface prefix in clatd.c. 322 // TODO: Revert this hack once IpClient and Nat464Xlat work in concert. 323 private static final String CLAT_PREFIX = "v4-"; 324 325 private static final int IMMEDIATE_FAILURE_DURATION = 0; 326 327 private static final int PROV_CHANGE_STILL_NOT_PROVISIONED = 1; 328 private static final int PROV_CHANGE_LOST_PROVISIONING = 2; 329 private static final int PROV_CHANGE_GAINED_PROVISIONING = 3; 330 private static final int PROV_CHANGE_STILL_PROVISIONED = 4; 331 332 private final State mStoppedState = new StoppedState(); 333 private final State mStoppingState = new StoppingState(); 334 private final State mStartedState = new StartedState(); 335 private final State mRunningState = new RunningState(); 336 337 private final String mTag; 338 private final Context mContext; 339 private final String mInterfaceName; 340 private final String mClatInterfaceName; 341 @VisibleForTesting 342 protected final IpClientCallbacksWrapper mCallback; 343 private final Dependencies mDependencies; 344 private final CountDownLatch mShutdownLatch; 345 private final ConnectivityManager mCm; 346 private final INetd mNetd; 347 private final NetworkObserverRegistry mObserverRegistry; 348 private final IpClientLinkObserver mLinkObserver; 349 private final WakeupMessage mProvisioningTimeoutAlarm; 350 private final WakeupMessage mDhcpActionTimeoutAlarm; 351 private final SharedLog mLog; 352 private final LocalLog mConnectivityPacketLog; 353 private final MessageHandlingLogger mMsgStateLogger; 354 private final IpConnectivityLog mMetricsLog = new IpConnectivityLog(); 355 private final InterfaceController mInterfaceCtrl; 356 357 private InterfaceParams mInterfaceParams; 358 359 /** 360 * Non-final member variables accessed only from within our StateMachine. 361 */ 362 private LinkProperties mLinkProperties; 363 private android.net.shared.ProvisioningConfiguration mConfiguration; 364 private IpReachabilityMonitor mIpReachabilityMonitor; 365 private DhcpClient mDhcpClient; 366 private DhcpResults mDhcpResults; 367 private String mTcpBufferSizes; 368 private ProxyInfo mHttpProxy; 369 private ApfFilter mApfFilter; 370 private String mL2Key; // The L2 key for this network, for writing into the memory store 371 private String mGroupHint; // The group hint for this network, for writing into the memory store 372 private boolean mMulticastFiltering; 373 private long mStartTimeMillis; 374 375 /** 376 * Reading the snapshot is an asynchronous operation initiated by invoking 377 * Callback.startReadPacketFilter() and completed when the WiFi Service responds with an 378 * EVENT_READ_PACKET_FILTER_COMPLETE message. The mApfDataSnapshotComplete condition variable 379 * signals when a new snapshot is ready. 380 */ 381 private final ConditionVariable mApfDataSnapshotComplete = new ConditionVariable(); 382 383 public static class Dependencies { 384 /** 385 * Get interface parameters for the specified interface. 386 */ getInterfaceParams(String ifname)387 public InterfaceParams getInterfaceParams(String ifname) { 388 return InterfaceParams.getByName(ifname); 389 } 390 391 /** 392 * Get a INetd connector. 393 */ getNetd(Context context)394 public INetd getNetd(Context context) { 395 return INetd.Stub.asInterface((IBinder) context.getSystemService(Context.NETD_SERVICE)); 396 } 397 } 398 IpClient(Context context, String ifName, IIpClientCallbacks callback, NetworkObserverRegistry observerRegistry, NetworkStackServiceManager nssManager)399 public IpClient(Context context, String ifName, IIpClientCallbacks callback, 400 NetworkObserverRegistry observerRegistry, NetworkStackServiceManager nssManager) { 401 this(context, ifName, callback, observerRegistry, nssManager, new Dependencies()); 402 } 403 404 @VisibleForTesting IpClient(Context context, String ifName, IIpClientCallbacks callback, NetworkObserverRegistry observerRegistry, NetworkStackServiceManager nssManager, Dependencies deps)405 IpClient(Context context, String ifName, IIpClientCallbacks callback, 406 NetworkObserverRegistry observerRegistry, NetworkStackServiceManager nssManager, 407 Dependencies deps) { 408 super(IpClient.class.getSimpleName() + "." + ifName); 409 Preconditions.checkNotNull(ifName); 410 Preconditions.checkNotNull(callback); 411 412 mTag = getName(); 413 414 mContext = context; 415 mInterfaceName = ifName; 416 mClatInterfaceName = CLAT_PREFIX + ifName; 417 mDependencies = deps; 418 mShutdownLatch = new CountDownLatch(1); 419 mCm = mContext.getSystemService(ConnectivityManager.class); 420 mObserverRegistry = observerRegistry; 421 mIpMemoryStore = 422 new NetworkStackIpMemoryStore(context, nssManager.getIpMemoryStoreService()); 423 424 sSmLogs.putIfAbsent(mInterfaceName, new SharedLog(MAX_LOG_RECORDS, mTag)); 425 mLog = sSmLogs.get(mInterfaceName); 426 sPktLogs.putIfAbsent(mInterfaceName, new LocalLog(MAX_PACKET_RECORDS)); 427 mConnectivityPacketLog = sPktLogs.get(mInterfaceName); 428 mMsgStateLogger = new MessageHandlingLogger(); 429 mCallback = new IpClientCallbacksWrapper(callback, mLog); 430 431 // TODO: Consider creating, constructing, and passing in some kind of 432 // InterfaceController.Dependencies class. 433 mNetd = deps.getNetd(mContext); 434 mInterfaceCtrl = new InterfaceController(mInterfaceName, mNetd, mLog); 435 436 mLinkObserver = new IpClientLinkObserver( 437 mInterfaceName, 438 () -> sendMessage(EVENT_NETLINK_LINKPROPERTIES_CHANGED)) { 439 @Override 440 public void onInterfaceAdded(String iface) { 441 super.onInterfaceAdded(iface); 442 if (mClatInterfaceName.equals(iface)) { 443 mCallback.setNeighborDiscoveryOffload(false); 444 } else if (!mInterfaceName.equals(iface)) { 445 return; 446 } 447 448 final String msg = "interfaceAdded(" + iface + ")"; 449 logMsg(msg); 450 } 451 452 @Override 453 public void onInterfaceRemoved(String iface) { 454 super.onInterfaceRemoved(iface); 455 // TODO: Also observe mInterfaceName going down and take some 456 // kind of appropriate action. 457 if (mClatInterfaceName.equals(iface)) { 458 // TODO: consider sending a message to the IpClient main 459 // StateMachine thread, in case "NDO enabled" state becomes 460 // tied to more things that 464xlat operation. 461 mCallback.setNeighborDiscoveryOffload(true); 462 } else if (!mInterfaceName.equals(iface)) { 463 return; 464 } 465 466 final String msg = "interfaceRemoved(" + iface + ")"; 467 logMsg(msg); 468 } 469 470 private void logMsg(String msg) { 471 Log.d(mTag, msg); 472 getHandler().post(() -> mLog.log("OBSERVED " + msg)); 473 } 474 }; 475 476 mLinkProperties = new LinkProperties(); 477 mLinkProperties.setInterfaceName(mInterfaceName); 478 479 mProvisioningTimeoutAlarm = new WakeupMessage(mContext, getHandler(), 480 mTag + ".EVENT_PROVISIONING_TIMEOUT", EVENT_PROVISIONING_TIMEOUT); 481 mDhcpActionTimeoutAlarm = new WakeupMessage(mContext, getHandler(), 482 mTag + ".EVENT_DHCPACTION_TIMEOUT", EVENT_DHCPACTION_TIMEOUT); 483 484 // Anything the StateMachine may access must have been instantiated 485 // before this point. 486 configureAndStartStateMachine(); 487 488 // Anything that may send messages to the StateMachine must only be 489 // configured to do so after the StateMachine has started (above). 490 startStateMachineUpdaters(); 491 } 492 493 /** 494 * Make a IIpClient connector to communicate with this IpClient. 495 */ makeConnector()496 public IIpClient makeConnector() { 497 return new IpClientConnector(); 498 } 499 500 class IpClientConnector extends IIpClient.Stub { 501 @Override completedPreDhcpAction()502 public void completedPreDhcpAction() { 503 checkNetworkStackCallingPermission(); 504 IpClient.this.completedPreDhcpAction(); 505 } 506 @Override confirmConfiguration()507 public void confirmConfiguration() { 508 checkNetworkStackCallingPermission(); 509 IpClient.this.confirmConfiguration(); 510 } 511 @Override readPacketFilterComplete(byte[] data)512 public void readPacketFilterComplete(byte[] data) { 513 checkNetworkStackCallingPermission(); 514 IpClient.this.readPacketFilterComplete(data); 515 } 516 @Override shutdown()517 public void shutdown() { 518 checkNetworkStackCallingPermission(); 519 IpClient.this.shutdown(); 520 } 521 @Override startProvisioning(ProvisioningConfigurationParcelable req)522 public void startProvisioning(ProvisioningConfigurationParcelable req) { 523 checkNetworkStackCallingPermission(); 524 IpClient.this.startProvisioning(ProvisioningConfiguration.fromStableParcelable(req)); 525 } 526 @Override stop()527 public void stop() { 528 checkNetworkStackCallingPermission(); 529 IpClient.this.stop(); 530 } 531 @Override setL2KeyAndGroupHint(String l2Key, String groupHint)532 public void setL2KeyAndGroupHint(String l2Key, String groupHint) { 533 checkNetworkStackCallingPermission(); 534 IpClient.this.setL2KeyAndGroupHint(l2Key, groupHint); 535 } 536 @Override setTcpBufferSizes(String tcpBufferSizes)537 public void setTcpBufferSizes(String tcpBufferSizes) { 538 checkNetworkStackCallingPermission(); 539 IpClient.this.setTcpBufferSizes(tcpBufferSizes); 540 } 541 @Override setHttpProxy(ProxyInfo proxyInfo)542 public void setHttpProxy(ProxyInfo proxyInfo) { 543 checkNetworkStackCallingPermission(); 544 IpClient.this.setHttpProxy(proxyInfo); 545 } 546 @Override setMulticastFilter(boolean enabled)547 public void setMulticastFilter(boolean enabled) { 548 checkNetworkStackCallingPermission(); 549 IpClient.this.setMulticastFilter(enabled); 550 } 551 @Override addKeepalivePacketFilter(int slot, TcpKeepalivePacketDataParcelable pkt)552 public void addKeepalivePacketFilter(int slot, TcpKeepalivePacketDataParcelable pkt) { 553 checkNetworkStackCallingPermission(); 554 IpClient.this.addKeepalivePacketFilter(slot, pkt); 555 } 556 @Override addNattKeepalivePacketFilter(int slot, NattKeepalivePacketDataParcelable pkt)557 public void addNattKeepalivePacketFilter(int slot, NattKeepalivePacketDataParcelable pkt) { 558 checkNetworkStackCallingPermission(); 559 IpClient.this.addNattKeepalivePacketFilter(slot, pkt); 560 } 561 @Override removeKeepalivePacketFilter(int slot)562 public void removeKeepalivePacketFilter(int slot) { 563 checkNetworkStackCallingPermission(); 564 IpClient.this.removeKeepalivePacketFilter(slot); 565 } 566 567 @Override getInterfaceVersion()568 public int getInterfaceVersion() { 569 return this.VERSION; 570 } 571 } 572 getInterfaceName()573 public String getInterfaceName() { 574 return mInterfaceName; 575 } 576 configureAndStartStateMachine()577 private void configureAndStartStateMachine() { 578 // CHECKSTYLE:OFF IndentationCheck 579 addState(mStoppedState); 580 addState(mStartedState); 581 addState(mRunningState, mStartedState); 582 addState(mStoppingState); 583 // CHECKSTYLE:ON IndentationCheck 584 585 setInitialState(mStoppedState); 586 587 super.start(); 588 } 589 startStateMachineUpdaters()590 private void startStateMachineUpdaters() { 591 mObserverRegistry.registerObserverForNonblockingCallback(mLinkObserver); 592 } 593 stopStateMachineUpdaters()594 private void stopStateMachineUpdaters() { 595 mObserverRegistry.unregisterObserver(mLinkObserver); 596 } 597 598 @Override onQuitting()599 protected void onQuitting() { 600 mCallback.onQuit(); 601 mShutdownLatch.countDown(); 602 } 603 604 /** 605 * Shut down this IpClient instance altogether. 606 */ shutdown()607 public void shutdown() { 608 stop(); 609 sendMessage(CMD_TERMINATE_AFTER_STOP); 610 } 611 612 /** 613 * Start provisioning with the provided parameters. 614 */ startProvisioning(ProvisioningConfiguration req)615 public void startProvisioning(ProvisioningConfiguration req) { 616 if (!req.isValid()) { 617 doImmediateProvisioningFailure(IpManagerEvent.ERROR_INVALID_PROVISIONING); 618 return; 619 } 620 621 mInterfaceParams = mDependencies.getInterfaceParams(mInterfaceName); 622 if (mInterfaceParams == null) { 623 logError("Failed to find InterfaceParams for " + mInterfaceName); 624 doImmediateProvisioningFailure(IpManagerEvent.ERROR_INTERFACE_NOT_FOUND); 625 return; 626 } 627 628 mCallback.setNeighborDiscoveryOffload(true); 629 sendMessage(CMD_START, new android.net.shared.ProvisioningConfiguration(req)); 630 } 631 632 /** 633 * Stop this IpClient. 634 * 635 * <p>This does not shut down the StateMachine itself, which is handled by {@link #shutdown()}. 636 */ stop()637 public void stop() { 638 sendMessage(CMD_STOP); 639 } 640 641 /** 642 * Confirm the provisioning configuration. 643 */ confirmConfiguration()644 public void confirmConfiguration() { 645 sendMessage(CMD_CONFIRM); 646 } 647 648 /** 649 * For clients using {@link ProvisioningConfiguration.Builder#withPreDhcpAction()}, must be 650 * called after {@link IIpClientCallbacks#onPreDhcpAction} to indicate that DHCP is clear to 651 * proceed. 652 */ completedPreDhcpAction()653 public void completedPreDhcpAction() { 654 sendMessage(EVENT_PRE_DHCP_ACTION_COMPLETE); 655 } 656 657 /** 658 * Indicate that packet filter read is complete. 659 */ readPacketFilterComplete(byte[] data)660 public void readPacketFilterComplete(byte[] data) { 661 sendMessage(EVENT_READ_PACKET_FILTER_COMPLETE, data); 662 } 663 664 /** 665 * Set the TCP buffer sizes to use. 666 * 667 * This may be called, repeatedly, at any time before or after a call to 668 * #startProvisioning(). The setting is cleared upon calling #stop(). 669 */ setTcpBufferSizes(String tcpBufferSizes)670 public void setTcpBufferSizes(String tcpBufferSizes) { 671 sendMessage(CMD_UPDATE_TCP_BUFFER_SIZES, tcpBufferSizes); 672 } 673 674 /** 675 * Set the L2 key and group hint for storing info into the memory store. 676 */ setL2KeyAndGroupHint(String l2Key, String groupHint)677 public void setL2KeyAndGroupHint(String l2Key, String groupHint) { 678 sendMessage(CMD_UPDATE_L2KEY_GROUPHINT, new Pair<>(l2Key, groupHint)); 679 } 680 681 /** 682 * Set the HTTP Proxy configuration to use. 683 * 684 * This may be called, repeatedly, at any time before or after a call to 685 * #startProvisioning(). The setting is cleared upon calling #stop(). 686 */ setHttpProxy(ProxyInfo proxyInfo)687 public void setHttpProxy(ProxyInfo proxyInfo) { 688 sendMessage(CMD_UPDATE_HTTP_PROXY, proxyInfo); 689 } 690 691 /** 692 * Enable or disable the multicast filter. Attempts to use APF to accomplish the filtering, 693 * if not, Callback.setFallbackMulticastFilter() is called. 694 */ setMulticastFilter(boolean enabled)695 public void setMulticastFilter(boolean enabled) { 696 sendMessage(CMD_SET_MULTICAST_FILTER, enabled); 697 } 698 699 /** 700 * Called by WifiStateMachine to add TCP keepalive packet filter before setting up 701 * keepalive offload. 702 */ addKeepalivePacketFilter(int slot, @NonNull TcpKeepalivePacketDataParcelable pkt)703 public void addKeepalivePacketFilter(int slot, @NonNull TcpKeepalivePacketDataParcelable pkt) { 704 sendMessage(CMD_ADD_KEEPALIVE_PACKET_FILTER_TO_APF, slot, 0 /* Unused */, pkt); 705 } 706 707 /** 708 * Called by WifiStateMachine to add NATT keepalive packet filter before setting up 709 * keepalive offload. 710 */ addNattKeepalivePacketFilter(int slot, @NonNull NattKeepalivePacketDataParcelable pkt)711 public void addNattKeepalivePacketFilter(int slot, 712 @NonNull NattKeepalivePacketDataParcelable pkt) { 713 sendMessage(CMD_ADD_KEEPALIVE_PACKET_FILTER_TO_APF, slot, 0 /* Unused */ , pkt); 714 } 715 716 /** 717 * Called by WifiStateMachine to remove keepalive packet filter after stopping keepalive 718 * offload. 719 */ removeKeepalivePacketFilter(int slot)720 public void removeKeepalivePacketFilter(int slot) { 721 sendMessage(CMD_REMOVE_KEEPALIVE_PACKET_FILTER_FROM_APF, slot, 0 /* Unused */); 722 } 723 724 /** 725 * Dump logs of this IpClient. 726 */ dump(FileDescriptor fd, PrintWriter writer, String[] args)727 public void dump(FileDescriptor fd, PrintWriter writer, String[] args) { 728 if (args != null && args.length > 0 && DUMP_ARG_CONFIRM.equals(args[0])) { 729 // Execute confirmConfiguration() and take no further action. 730 confirmConfiguration(); 731 return; 732 } 733 734 // Thread-unsafe access to mApfFilter but just used for debugging. 735 final ApfFilter apfFilter = mApfFilter; 736 final android.net.shared.ProvisioningConfiguration provisioningConfig = mConfiguration; 737 final ApfCapabilities apfCapabilities = (provisioningConfig != null) 738 ? provisioningConfig.mApfCapabilities : null; 739 740 IndentingPrintWriter pw = new IndentingPrintWriter(writer, " "); 741 pw.println(mTag + " APF dump:"); 742 pw.increaseIndent(); 743 if (apfFilter != null) { 744 if (apfCapabilities.hasDataAccess()) { 745 // Request a new snapshot, then wait for it. 746 mApfDataSnapshotComplete.close(); 747 mCallback.startReadPacketFilter(); 748 if (!mApfDataSnapshotComplete.block(1000)) { 749 pw.print("TIMEOUT: DUMPING STALE APF SNAPSHOT"); 750 } 751 } 752 apfFilter.dump(pw); 753 754 } else { 755 pw.print("No active ApfFilter; "); 756 if (provisioningConfig == null) { 757 pw.println("IpClient not yet started."); 758 } else if (apfCapabilities == null || apfCapabilities.apfVersionSupported == 0) { 759 pw.println("Hardware does not support APF."); 760 } else { 761 pw.println("ApfFilter not yet started, APF capabilities: " + apfCapabilities); 762 } 763 } 764 pw.decreaseIndent(); 765 pw.println(); 766 pw.println(mTag + " current ProvisioningConfiguration:"); 767 pw.increaseIndent(); 768 pw.println(Objects.toString(provisioningConfig, "N/A")); 769 pw.decreaseIndent(); 770 771 final IpReachabilityMonitor iprm = mIpReachabilityMonitor; 772 if (iprm != null) { 773 pw.println(); 774 pw.println(mTag + " current IpReachabilityMonitor state:"); 775 pw.increaseIndent(); 776 iprm.dump(pw); 777 pw.decreaseIndent(); 778 } 779 780 pw.println(); 781 pw.println(mTag + " StateMachine dump:"); 782 pw.increaseIndent(); 783 mLog.dump(fd, pw, args); 784 pw.decreaseIndent(); 785 786 pw.println(); 787 pw.println(mTag + " connectivity packet log:"); 788 pw.println(); 789 pw.println("Debug with python and scapy via:"); 790 pw.println("shell$ python"); 791 pw.println(">>> from scapy import all as scapy"); 792 pw.println(">>> scapy.Ether(\"<paste_hex_string>\".decode(\"hex\")).show2()"); 793 pw.println(); 794 795 pw.increaseIndent(); 796 mConnectivityPacketLog.readOnlyLocalLog().dump(fd, pw, args); 797 pw.decreaseIndent(); 798 } 799 800 801 /** 802 * Internals. 803 */ 804 805 @Override getWhatToString(int what)806 protected String getWhatToString(int what) { 807 return sWhatToString.get(what, "UNKNOWN: " + Integer.toString(what)); 808 } 809 810 @Override getLogRecString(Message msg)811 protected String getLogRecString(Message msg) { 812 final String logLine = String.format( 813 "%s/%d %d %d %s [%s]", 814 mInterfaceName, (mInterfaceParams == null) ? -1 : mInterfaceParams.index, 815 msg.arg1, msg.arg2, Objects.toString(msg.obj), mMsgStateLogger); 816 817 final String richerLogLine = getWhatToString(msg.what) + " " + logLine; 818 mLog.log(richerLogLine); 819 if (DBG) { 820 Log.d(mTag, richerLogLine); 821 } 822 823 mMsgStateLogger.reset(); 824 return logLine; 825 } 826 827 @Override recordLogRec(Message msg)828 protected boolean recordLogRec(Message msg) { 829 // Don't log EVENT_NETLINK_LINKPROPERTIES_CHANGED. They can be noisy, 830 // and we already log any LinkProperties change that results in an 831 // invocation of IpClient.Callback#onLinkPropertiesChange(). 832 final boolean shouldLog = (msg.what != EVENT_NETLINK_LINKPROPERTIES_CHANGED); 833 if (!shouldLog) { 834 mMsgStateLogger.reset(); 835 } 836 return shouldLog; 837 } 838 logError(String fmt, Object... args)839 private void logError(String fmt, Object... args) { 840 final String msg = "ERROR " + String.format(fmt, args); 841 Log.e(mTag, msg); 842 mLog.log(msg); 843 } 844 845 // This needs to be called with care to ensure that our LinkProperties 846 // are in sync with the actual LinkProperties of the interface. For example, 847 // we should only call this if we know for sure that there are no IP addresses 848 // assigned to the interface, etc. resetLinkProperties()849 private void resetLinkProperties() { 850 mLinkObserver.clearLinkProperties(); 851 mConfiguration = null; 852 mDhcpResults = null; 853 mTcpBufferSizes = ""; 854 mHttpProxy = null; 855 856 mLinkProperties = new LinkProperties(); 857 mLinkProperties.setInterfaceName(mInterfaceName); 858 } 859 recordMetric(final int type)860 private void recordMetric(final int type) { 861 // We may record error metrics prior to starting. 862 // Map this to IMMEDIATE_FAILURE_DURATION. 863 final long duration = (mStartTimeMillis > 0) 864 ? (SystemClock.elapsedRealtime() - mStartTimeMillis) 865 : IMMEDIATE_FAILURE_DURATION; 866 mMetricsLog.log(mInterfaceName, new IpManagerEvent(type, duration)); 867 } 868 869 // For now: use WifiStateMachine's historical notion of provisioned. 870 @VisibleForTesting isProvisioned(LinkProperties lp, InitialConfiguration config)871 static boolean isProvisioned(LinkProperties lp, InitialConfiguration config) { 872 // For historical reasons, we should connect even if all we have is 873 // an IPv4 address and nothing else. 874 if (lp.hasIpv4Address() || lp.isProvisioned()) { 875 return true; 876 } 877 if (config == null) { 878 return false; 879 } 880 881 // When an InitialConfiguration is specified, ignore any difference with previous 882 // properties and instead check if properties observed match the desired properties. 883 return config.isProvisionedBy(lp.getLinkAddresses(), lp.getRoutes()); 884 } 885 886 // TODO: Investigate folding all this into the existing static function 887 // LinkProperties.compareProvisioning() or some other single function that 888 // takes two LinkProperties objects and returns a ProvisioningChange 889 // object that is a correct and complete assessment of what changed, taking 890 // account of the asymmetries described in the comments in this function. 891 // Then switch to using it everywhere (IpReachabilityMonitor, etc.). compareProvisioning(LinkProperties oldLp, LinkProperties newLp)892 private int compareProvisioning(LinkProperties oldLp, LinkProperties newLp) { 893 int delta; 894 InitialConfiguration config = mConfiguration != null ? mConfiguration.mInitialConfig : null; 895 final boolean wasProvisioned = isProvisioned(oldLp, config); 896 final boolean isProvisioned = isProvisioned(newLp, config); 897 898 if (!wasProvisioned && isProvisioned) { 899 delta = PROV_CHANGE_GAINED_PROVISIONING; 900 } else if (wasProvisioned && isProvisioned) { 901 delta = PROV_CHANGE_STILL_PROVISIONED; 902 } else if (!wasProvisioned && !isProvisioned) { 903 delta = PROV_CHANGE_STILL_NOT_PROVISIONED; 904 } else { 905 // (wasProvisioned && !isProvisioned) 906 // 907 // Note that this is true even if we lose a configuration element 908 // (e.g., a default gateway) that would not be required to advance 909 // into provisioned state. This is intended: if we have a default 910 // router and we lose it, that's a sure sign of a problem, but if 911 // we connect to a network with no IPv4 DNS servers, we consider 912 // that to be a network without DNS servers and connect anyway. 913 // 914 // See the comment below. 915 delta = PROV_CHANGE_LOST_PROVISIONING; 916 } 917 918 final boolean lostIPv6 = oldLp.isIpv6Provisioned() && !newLp.isIpv6Provisioned(); 919 final boolean lostIPv4Address = oldLp.hasIpv4Address() && !newLp.hasIpv4Address(); 920 final boolean lostIPv6Router = oldLp.hasIpv6DefaultRoute() && !newLp.hasIpv6DefaultRoute(); 921 922 // If bad wifi avoidance is disabled, then ignore IPv6 loss of 923 // provisioning. Otherwise, when a hotspot that loses Internet 924 // access sends out a 0-lifetime RA to its clients, the clients 925 // will disconnect and then reconnect, avoiding the bad hotspot, 926 // instead of getting stuck on the bad hotspot. http://b/31827713 . 927 // 928 // This is incorrect because if the hotspot then regains Internet 929 // access with a different prefix, TCP connections on the 930 // deprecated addresses will remain stuck. 931 // 932 // Note that we can still be disconnected by IpReachabilityMonitor 933 // if the IPv6 default gateway (but not the IPv6 DNS servers; see 934 // accompanying code in IpReachabilityMonitor) is unreachable. 935 final boolean ignoreIPv6ProvisioningLoss = 936 mConfiguration != null && mConfiguration.mUsingMultinetworkPolicyTracker 937 && mCm.shouldAvoidBadWifi(); 938 939 // Additionally: 940 // 941 // Partial configurations (e.g., only an IPv4 address with no DNS 942 // servers and no default route) are accepted as long as DHCPv4 943 // succeeds. On such a network, isProvisioned() will always return 944 // false, because the configuration is not complete, but we want to 945 // connect anyway. It might be a disconnected network such as a 946 // Chromecast or a wireless printer, for example. 947 // 948 // Because on such a network isProvisioned() will always return false, 949 // delta will never be LOST_PROVISIONING. So check for loss of 950 // provisioning here too. 951 if (lostIPv4Address || (lostIPv6 && !ignoreIPv6ProvisioningLoss)) { 952 delta = PROV_CHANGE_LOST_PROVISIONING; 953 } 954 955 // Additionally: 956 // 957 // If the previous link properties had a global IPv6 address and an 958 // IPv6 default route then also consider the loss of that default route 959 // to be a loss of provisioning. See b/27962810. 960 if (oldLp.hasGlobalIpv6Address() && (lostIPv6Router && !ignoreIPv6ProvisioningLoss)) { 961 delta = PROV_CHANGE_LOST_PROVISIONING; 962 } 963 964 return delta; 965 } 966 dispatchCallback(int delta, LinkProperties newLp)967 private void dispatchCallback(int delta, LinkProperties newLp) { 968 switch (delta) { 969 case PROV_CHANGE_GAINED_PROVISIONING: 970 if (DBG) { 971 Log.d(mTag, "onProvisioningSuccess()"); 972 } 973 recordMetric(IpManagerEvent.PROVISIONING_OK); 974 mCallback.onProvisioningSuccess(newLp); 975 break; 976 977 case PROV_CHANGE_LOST_PROVISIONING: 978 if (DBG) { 979 Log.d(mTag, "onProvisioningFailure()"); 980 } 981 recordMetric(IpManagerEvent.PROVISIONING_FAIL); 982 mCallback.onProvisioningFailure(newLp); 983 break; 984 985 default: 986 if (DBG) { 987 Log.d(mTag, "onLinkPropertiesChange()"); 988 } 989 mCallback.onLinkPropertiesChange(newLp); 990 break; 991 } 992 } 993 994 // Updates all IpClient-related state concerned with LinkProperties. 995 // Returns a ProvisioningChange for possibly notifying other interested 996 // parties that are not fronted by IpClient. setLinkProperties(LinkProperties newLp)997 private int setLinkProperties(LinkProperties newLp) { 998 if (mApfFilter != null) { 999 mApfFilter.setLinkProperties(newLp); 1000 } 1001 if (mIpReachabilityMonitor != null) { 1002 mIpReachabilityMonitor.updateLinkProperties(newLp); 1003 } 1004 1005 int delta = compareProvisioning(mLinkProperties, newLp); 1006 mLinkProperties = new LinkProperties(newLp); 1007 1008 if (delta == PROV_CHANGE_GAINED_PROVISIONING) { 1009 // TODO: Add a proper ProvisionedState and cancel the alarm in 1010 // its enter() method. 1011 mProvisioningTimeoutAlarm.cancel(); 1012 } 1013 1014 return delta; 1015 } 1016 assembleLinkProperties()1017 private LinkProperties assembleLinkProperties() { 1018 // [1] Create a new LinkProperties object to populate. 1019 LinkProperties newLp = new LinkProperties(); 1020 newLp.setInterfaceName(mInterfaceName); 1021 1022 // [2] Pull in data from netlink: 1023 // - IPv4 addresses 1024 // - IPv6 addresses 1025 // - IPv6 routes 1026 // - IPv6 DNS servers 1027 // 1028 // N.B.: this is fundamentally race-prone and should be fixed by 1029 // changing IpClientLinkObserver from a hybrid edge/level model to an 1030 // edge-only model, or by giving IpClient its own netlink socket(s) 1031 // so as to track all required information directly. 1032 LinkProperties netlinkLinkProperties = mLinkObserver.getLinkProperties(); 1033 newLp.setLinkAddresses(netlinkLinkProperties.getLinkAddresses()); 1034 for (RouteInfo route : netlinkLinkProperties.getRoutes()) { 1035 newLp.addRoute(route); 1036 } 1037 addAllReachableDnsServers(newLp, netlinkLinkProperties.getDnsServers()); 1038 1039 // [3] Add in data from DHCPv4, if available. 1040 // 1041 // mDhcpResults is never shared with any other owner so we don't have 1042 // to worry about concurrent modification. 1043 if (mDhcpResults != null) { 1044 final List<RouteInfo> routes = 1045 mDhcpResults.toStaticIpConfiguration().getRoutes(mInterfaceName); 1046 for (RouteInfo route : routes) { 1047 newLp.addRoute(route); 1048 } 1049 addAllReachableDnsServers(newLp, mDhcpResults.dnsServers); 1050 newLp.setDomains(mDhcpResults.domains); 1051 1052 if (mDhcpResults.mtu != 0) { 1053 newLp.setMtu(mDhcpResults.mtu); 1054 } 1055 } 1056 1057 // [4] Add in TCP buffer sizes and HTTP Proxy config, if available. 1058 if (!TextUtils.isEmpty(mTcpBufferSizes)) { 1059 newLp.setTcpBufferSizes(mTcpBufferSizes); 1060 } 1061 if (mHttpProxy != null) { 1062 newLp.setHttpProxy(mHttpProxy); 1063 } 1064 1065 // [5] Add data from InitialConfiguration 1066 if (mConfiguration != null && mConfiguration.mInitialConfig != null) { 1067 InitialConfiguration config = mConfiguration.mInitialConfig; 1068 // Add InitialConfiguration routes and dns server addresses once all addresses 1069 // specified in the InitialConfiguration have been observed with Netlink. 1070 if (config.isProvisionedBy(newLp.getLinkAddresses(), null)) { 1071 for (IpPrefix prefix : config.directlyConnectedRoutes) { 1072 newLp.addRoute(new RouteInfo(prefix, null, mInterfaceName, RTN_UNICAST)); 1073 } 1074 } 1075 addAllReachableDnsServers(newLp, config.dnsServers); 1076 } 1077 final LinkProperties oldLp = mLinkProperties; 1078 if (DBG) { 1079 Log.d(mTag, String.format("Netlink-seen LPs: %s, new LPs: %s; old LPs: %s", 1080 netlinkLinkProperties, newLp, oldLp)); 1081 } 1082 1083 // TODO: also learn via netlink routes specified by an InitialConfiguration and specified 1084 // from a static IP v4 config instead of manually patching them in in steps [3] and [5]. 1085 return newLp; 1086 } 1087 addAllReachableDnsServers( LinkProperties lp, Iterable<InetAddress> dnses)1088 private static void addAllReachableDnsServers( 1089 LinkProperties lp, Iterable<InetAddress> dnses) { 1090 // TODO: Investigate deleting this reachability check. We should be 1091 // able to pass everything down to netd and let netd do evaluation 1092 // and RFC6724-style sorting. 1093 for (InetAddress dns : dnses) { 1094 if (!dns.isAnyLocalAddress() && lp.isReachable(dns)) { 1095 lp.addDnsServer(dns); 1096 } 1097 } 1098 } 1099 1100 // Returns false if we have lost provisioning, true otherwise. handleLinkPropertiesUpdate(boolean sendCallbacks)1101 private boolean handleLinkPropertiesUpdate(boolean sendCallbacks) { 1102 final LinkProperties newLp = assembleLinkProperties(); 1103 if (Objects.equals(newLp, mLinkProperties)) { 1104 return true; 1105 } 1106 final int delta = setLinkProperties(newLp); 1107 // Most of the attributes stored in the memory store are deduced from 1108 // the link properties, therefore when the properties update the memory 1109 // store record should be updated too. 1110 maybeSaveNetworkToIpMemoryStore(); 1111 if (sendCallbacks) { 1112 dispatchCallback(delta, newLp); 1113 } 1114 return (delta != PROV_CHANGE_LOST_PROVISIONING); 1115 } 1116 handleIPv4Success(DhcpResults dhcpResults)1117 private void handleIPv4Success(DhcpResults dhcpResults) { 1118 mDhcpResults = new DhcpResults(dhcpResults); 1119 final LinkProperties newLp = assembleLinkProperties(); 1120 final int delta = setLinkProperties(newLp); 1121 1122 if (DBG) { 1123 Log.d(mTag, "onNewDhcpResults(" + Objects.toString(dhcpResults) + ")"); 1124 } 1125 mCallback.onNewDhcpResults(dhcpResults); 1126 maybeSaveNetworkToIpMemoryStore(); 1127 dispatchCallback(delta, newLp); 1128 } 1129 handleIPv4Failure()1130 private void handleIPv4Failure() { 1131 // TODO: Investigate deleting this clearIPv4Address() call. 1132 // 1133 // DhcpClient will send us CMD_CLEAR_LINKADDRESS in all circumstances 1134 // that could trigger a call to this function. If we missed handling 1135 // that message in StartedState for some reason we would still clear 1136 // any addresses upon entry to StoppedState. 1137 mInterfaceCtrl.clearIPv4Address(); 1138 mDhcpResults = null; 1139 if (DBG) { 1140 Log.d(mTag, "onNewDhcpResults(null)"); 1141 } 1142 mCallback.onNewDhcpResults(null); 1143 1144 handleProvisioningFailure(); 1145 } 1146 handleProvisioningFailure()1147 private void handleProvisioningFailure() { 1148 final LinkProperties newLp = assembleLinkProperties(); 1149 int delta = setLinkProperties(newLp); 1150 // If we've gotten here and we're still not provisioned treat that as 1151 // a total loss of provisioning. 1152 // 1153 // Either (a) static IP configuration failed or (b) DHCPv4 failed AND 1154 // there was no usable IPv6 obtained before a non-zero provisioning 1155 // timeout expired. 1156 // 1157 // Regardless: GAME OVER. 1158 if (delta == PROV_CHANGE_STILL_NOT_PROVISIONED) { 1159 delta = PROV_CHANGE_LOST_PROVISIONING; 1160 } 1161 1162 dispatchCallback(delta, newLp); 1163 if (delta == PROV_CHANGE_LOST_PROVISIONING) { 1164 transitionTo(mStoppingState); 1165 } 1166 } 1167 doImmediateProvisioningFailure(int failureType)1168 private void doImmediateProvisioningFailure(int failureType) { 1169 logError("onProvisioningFailure(): %s", failureType); 1170 recordMetric(failureType); 1171 mCallback.onProvisioningFailure(new LinkProperties(mLinkProperties)); 1172 } 1173 startIPv4()1174 private boolean startIPv4() { 1175 // If we have a StaticIpConfiguration attempt to apply it and 1176 // handle the result accordingly. 1177 if (mConfiguration.mStaticIpConfig != null) { 1178 if (mInterfaceCtrl.setIPv4Address(mConfiguration.mStaticIpConfig.getIpAddress())) { 1179 handleIPv4Success(new DhcpResults(mConfiguration.mStaticIpConfig)); 1180 } else { 1181 return false; 1182 } 1183 } else { 1184 // Start DHCPv4. 1185 mDhcpClient = DhcpClient.makeDhcpClient(mContext, IpClient.this, mInterfaceParams); 1186 mDhcpClient.registerForPreDhcpNotification(); 1187 mDhcpClient.sendMessage(DhcpClient.CMD_START_DHCP); 1188 } 1189 1190 return true; 1191 } 1192 startIPv6()1193 private boolean startIPv6() { 1194 return mInterfaceCtrl.setIPv6PrivacyExtensions(true) 1195 && mInterfaceCtrl.setIPv6AddrGenModeIfSupported(mConfiguration.mIPv6AddrGenMode) 1196 && mInterfaceCtrl.enableIPv6(); 1197 } 1198 applyInitialConfig(InitialConfiguration config)1199 private boolean applyInitialConfig(InitialConfiguration config) { 1200 // TODO: also support specifying a static IPv4 configuration in InitialConfiguration. 1201 for (LinkAddress addr : findAll(config.ipAddresses, LinkAddress::isIpv6)) { 1202 if (!mInterfaceCtrl.addAddress(addr)) return false; 1203 } 1204 1205 return true; 1206 } 1207 startIpReachabilityMonitor()1208 private boolean startIpReachabilityMonitor() { 1209 try { 1210 // TODO: Fetch these parameters from settings, and install a 1211 // settings observer to watch for update and re-program these 1212 // parameters (Q: is this level of dynamic updatability really 1213 // necessary or does reading from settings at startup suffice?). 1214 final int numSolicits = 5; 1215 final int interSolicitIntervalMs = 750; 1216 setNeighborParameters(mNetd, mInterfaceName, numSolicits, interSolicitIntervalMs); 1217 } catch (Exception e) { 1218 mLog.e("Failed to adjust neighbor parameters", e); 1219 // Carry on using the system defaults (currently: 3, 1000); 1220 } 1221 1222 try { 1223 mIpReachabilityMonitor = new IpReachabilityMonitor( 1224 mContext, 1225 mInterfaceParams, 1226 getHandler(), 1227 mLog, 1228 new IpReachabilityMonitor.Callback() { 1229 @Override 1230 public void notifyLost(InetAddress ip, String logMsg) { 1231 mCallback.onReachabilityLost(logMsg); 1232 } 1233 }, 1234 mConfiguration.mUsingMultinetworkPolicyTracker); 1235 } catch (IllegalArgumentException iae) { 1236 // Failed to start IpReachabilityMonitor. Log it and call 1237 // onProvisioningFailure() immediately. 1238 // 1239 // See http://b/31038971. 1240 logError("IpReachabilityMonitor failure: %s", iae); 1241 mIpReachabilityMonitor = null; 1242 } 1243 1244 return (mIpReachabilityMonitor != null); 1245 } 1246 stopAllIP()1247 private void stopAllIP() { 1248 // We don't need to worry about routes, just addresses, because: 1249 // - disableIpv6() will clear autoconf IPv6 routes as well, and 1250 // - we don't get IPv4 routes from netlink 1251 // so we neither react to nor need to wait for changes in either. 1252 1253 mInterfaceCtrl.disableIPv6(); 1254 mInterfaceCtrl.clearAllAddresses(); 1255 } 1256 maybeSaveNetworkToIpMemoryStore()1257 private void maybeSaveNetworkToIpMemoryStore() { 1258 // TODO : implement this 1259 } 1260 1261 class StoppedState extends State { 1262 @Override enter()1263 public void enter() { 1264 stopAllIP(); 1265 1266 resetLinkProperties(); 1267 if (mStartTimeMillis > 0) { 1268 // Completed a life-cycle; send a final empty LinkProperties 1269 // (cleared in resetLinkProperties() above) and record an event. 1270 mCallback.onLinkPropertiesChange(new LinkProperties(mLinkProperties)); 1271 recordMetric(IpManagerEvent.COMPLETE_LIFECYCLE); 1272 mStartTimeMillis = 0; 1273 } 1274 } 1275 1276 @Override processMessage(Message msg)1277 public boolean processMessage(Message msg) { 1278 switch (msg.what) { 1279 case CMD_TERMINATE_AFTER_STOP: 1280 stopStateMachineUpdaters(); 1281 quit(); 1282 break; 1283 1284 case CMD_STOP: 1285 break; 1286 1287 case CMD_START: 1288 mConfiguration = (android.net.shared.ProvisioningConfiguration) msg.obj; 1289 transitionTo(mStartedState); 1290 break; 1291 1292 case EVENT_NETLINK_LINKPROPERTIES_CHANGED: 1293 handleLinkPropertiesUpdate(NO_CALLBACKS); 1294 break; 1295 1296 case CMD_UPDATE_TCP_BUFFER_SIZES: 1297 mTcpBufferSizes = (String) msg.obj; 1298 handleLinkPropertiesUpdate(NO_CALLBACKS); 1299 break; 1300 1301 case CMD_UPDATE_HTTP_PROXY: 1302 mHttpProxy = (ProxyInfo) msg.obj; 1303 handleLinkPropertiesUpdate(NO_CALLBACKS); 1304 break; 1305 1306 case CMD_UPDATE_L2KEY_GROUPHINT: { 1307 final Pair<String, String> args = (Pair<String, String>) msg.obj; 1308 mL2Key = args.first; 1309 mGroupHint = args.second; 1310 break; 1311 } 1312 1313 case CMD_SET_MULTICAST_FILTER: 1314 mMulticastFiltering = (boolean) msg.obj; 1315 break; 1316 1317 case DhcpClient.CMD_ON_QUIT: 1318 // Everything is already stopped. 1319 logError("Unexpected CMD_ON_QUIT (already stopped)."); 1320 break; 1321 1322 default: 1323 return NOT_HANDLED; 1324 } 1325 1326 mMsgStateLogger.handled(this, getCurrentState()); 1327 return HANDLED; 1328 } 1329 } 1330 1331 class StoppingState extends State { 1332 @Override enter()1333 public void enter() { 1334 if (mDhcpClient == null) { 1335 // There's no DHCPv4 for which to wait; proceed to stopped. 1336 deferMessage(obtainMessage(CMD_JUMP_STOPPING_TO_STOPPED)); 1337 } 1338 } 1339 1340 @Override processMessage(Message msg)1341 public boolean processMessage(Message msg) { 1342 switch (msg.what) { 1343 case CMD_JUMP_STOPPING_TO_STOPPED: 1344 transitionTo(mStoppedState); 1345 break; 1346 1347 case CMD_STOP: 1348 break; 1349 1350 case DhcpClient.CMD_CLEAR_LINKADDRESS: 1351 mInterfaceCtrl.clearIPv4Address(); 1352 break; 1353 1354 case DhcpClient.CMD_ON_QUIT: 1355 mDhcpClient = null; 1356 transitionTo(mStoppedState); 1357 break; 1358 1359 default: 1360 deferMessage(msg); 1361 } 1362 1363 mMsgStateLogger.handled(this, getCurrentState()); 1364 return HANDLED; 1365 } 1366 } 1367 1368 class StartedState extends State { 1369 @Override enter()1370 public void enter() { 1371 mStartTimeMillis = SystemClock.elapsedRealtime(); 1372 1373 if (mConfiguration.mProvisioningTimeoutMs > 0) { 1374 final long alarmTime = SystemClock.elapsedRealtime() 1375 + mConfiguration.mProvisioningTimeoutMs; 1376 mProvisioningTimeoutAlarm.schedule(alarmTime); 1377 } 1378 1379 if (readyToProceed()) { 1380 deferMessage(obtainMessage(CMD_JUMP_STARTED_TO_RUNNING)); 1381 } else { 1382 // Clear all IPv4 and IPv6 before proceeding to RunningState. 1383 // Clean up any leftover state from an abnormal exit from 1384 // tethering or during an IpClient restart. 1385 stopAllIP(); 1386 } 1387 } 1388 1389 @Override exit()1390 public void exit() { 1391 mProvisioningTimeoutAlarm.cancel(); 1392 } 1393 1394 @Override processMessage(Message msg)1395 public boolean processMessage(Message msg) { 1396 switch (msg.what) { 1397 case CMD_JUMP_STARTED_TO_RUNNING: 1398 transitionTo(mRunningState); 1399 break; 1400 1401 case CMD_STOP: 1402 transitionTo(mStoppingState); 1403 break; 1404 1405 case EVENT_NETLINK_LINKPROPERTIES_CHANGED: 1406 handleLinkPropertiesUpdate(NO_CALLBACKS); 1407 if (readyToProceed()) { 1408 transitionTo(mRunningState); 1409 } 1410 break; 1411 1412 case CMD_UPDATE_L2KEY_GROUPHINT: { 1413 final Pair<String, String> args = (Pair<String, String>) msg.obj; 1414 mL2Key = args.first; 1415 mGroupHint = args.second; 1416 // TODO : attributes should be saved to the memory store with 1417 // these new values if they differ from the previous ones. 1418 // If the state machine is in pure StartedState, then the values to input 1419 // are not known yet and should be updated when the LinkProperties are updated. 1420 // If the state machine is in RunningState (which is a child of StartedState) 1421 // then the next NUD check should be used to store the new values to avoid 1422 // inputting current values for what may be a different L3 network. 1423 break; 1424 } 1425 1426 case EVENT_PROVISIONING_TIMEOUT: 1427 handleProvisioningFailure(); 1428 break; 1429 1430 default: 1431 // It's safe to process messages out of order because the 1432 // only message that can both 1433 // a) be received at this time and 1434 // b) affect provisioning state 1435 // is EVENT_NETLINK_LINKPROPERTIES_CHANGED (handled above). 1436 deferMessage(msg); 1437 } 1438 1439 mMsgStateLogger.handled(this, getCurrentState()); 1440 return HANDLED; 1441 } 1442 readyToProceed()1443 private boolean readyToProceed() { 1444 return (!mLinkProperties.hasIpv4Address() && !mLinkProperties.hasGlobalIpv6Address()); 1445 } 1446 } 1447 1448 class RunningState extends State { 1449 private ConnectivityPacketTracker mPacketTracker; 1450 private boolean mDhcpActionInFlight; 1451 1452 @Override enter()1453 public void enter() { 1454 ApfFilter.ApfConfiguration apfConfig = new ApfFilter.ApfConfiguration(); 1455 apfConfig.apfCapabilities = mConfiguration.mApfCapabilities; 1456 apfConfig.multicastFilter = mMulticastFiltering; 1457 // Get the Configuration for ApfFilter from Context 1458 apfConfig.ieee802_3Filter = ApfCapabilities.getApfDrop8023Frames(); 1459 apfConfig.ethTypeBlackList = ApfCapabilities.getApfEtherTypeBlackList(); 1460 mApfFilter = ApfFilter.maybeCreate(mContext, apfConfig, mInterfaceParams, mCallback); 1461 // TODO: investigate the effects of any multicast filtering racing/interfering with the 1462 // rest of this IP configuration startup. 1463 if (mApfFilter == null) { 1464 mCallback.setFallbackMulticastFilter(mMulticastFiltering); 1465 } 1466 1467 mPacketTracker = createPacketTracker(); 1468 if (mPacketTracker != null) mPacketTracker.start(mConfiguration.mDisplayName); 1469 1470 if (mConfiguration.mEnableIPv6 && !startIPv6()) { 1471 doImmediateProvisioningFailure(IpManagerEvent.ERROR_STARTING_IPV6); 1472 enqueueJumpToStoppingState(); 1473 return; 1474 } 1475 1476 if (mConfiguration.mEnableIPv4 && !startIPv4()) { 1477 doImmediateProvisioningFailure(IpManagerEvent.ERROR_STARTING_IPV4); 1478 enqueueJumpToStoppingState(); 1479 return; 1480 } 1481 1482 final InitialConfiguration config = mConfiguration.mInitialConfig; 1483 if ((config != null) && !applyInitialConfig(config)) { 1484 // TODO introduce a new IpManagerEvent constant to distinguish this error case. 1485 doImmediateProvisioningFailure(IpManagerEvent.ERROR_INVALID_PROVISIONING); 1486 enqueueJumpToStoppingState(); 1487 return; 1488 } 1489 1490 if (mConfiguration.mUsingIpReachabilityMonitor && !startIpReachabilityMonitor()) { 1491 doImmediateProvisioningFailure( 1492 IpManagerEvent.ERROR_STARTING_IPREACHABILITYMONITOR); 1493 enqueueJumpToStoppingState(); 1494 return; 1495 } 1496 } 1497 1498 @Override exit()1499 public void exit() { 1500 stopDhcpAction(); 1501 1502 if (mIpReachabilityMonitor != null) { 1503 mIpReachabilityMonitor.stop(); 1504 mIpReachabilityMonitor = null; 1505 } 1506 1507 if (mDhcpClient != null) { 1508 mDhcpClient.sendMessage(DhcpClient.CMD_STOP_DHCP); 1509 mDhcpClient.doQuit(); 1510 } 1511 1512 if (mPacketTracker != null) { 1513 mPacketTracker.stop(); 1514 mPacketTracker = null; 1515 } 1516 1517 if (mApfFilter != null) { 1518 mApfFilter.shutdown(); 1519 mApfFilter = null; 1520 } 1521 1522 resetLinkProperties(); 1523 } 1524 enqueueJumpToStoppingState()1525 private void enqueueJumpToStoppingState() { 1526 deferMessage(obtainMessage(CMD_JUMP_RUNNING_TO_STOPPING)); 1527 } 1528 createPacketTracker()1529 private ConnectivityPacketTracker createPacketTracker() { 1530 try { 1531 return new ConnectivityPacketTracker( 1532 getHandler(), mInterfaceParams, mConnectivityPacketLog); 1533 } catch (IllegalArgumentException e) { 1534 return null; 1535 } 1536 } 1537 ensureDhcpAction()1538 private void ensureDhcpAction() { 1539 if (!mDhcpActionInFlight) { 1540 mCallback.onPreDhcpAction(); 1541 mDhcpActionInFlight = true; 1542 final long alarmTime = SystemClock.elapsedRealtime() 1543 + mConfiguration.mRequestedPreDhcpActionMs; 1544 mDhcpActionTimeoutAlarm.schedule(alarmTime); 1545 } 1546 } 1547 stopDhcpAction()1548 private void stopDhcpAction() { 1549 mDhcpActionTimeoutAlarm.cancel(); 1550 if (mDhcpActionInFlight) { 1551 mCallback.onPostDhcpAction(); 1552 mDhcpActionInFlight = false; 1553 } 1554 } 1555 1556 @Override processMessage(Message msg)1557 public boolean processMessage(Message msg) { 1558 switch (msg.what) { 1559 case CMD_JUMP_RUNNING_TO_STOPPING: 1560 case CMD_STOP: 1561 transitionTo(mStoppingState); 1562 break; 1563 1564 case CMD_START: 1565 logError("ALERT: START received in StartedState. Please fix caller."); 1566 break; 1567 1568 case CMD_CONFIRM: 1569 // TODO: Possibly introduce a second type of confirmation 1570 // that both probes (a) on-link neighbors and (b) does 1571 // a DHCPv4 RENEW. We used to do this on Wi-Fi framework 1572 // roams. 1573 if (mIpReachabilityMonitor != null) { 1574 mIpReachabilityMonitor.probeAll(); 1575 } 1576 break; 1577 1578 case EVENT_PRE_DHCP_ACTION_COMPLETE: 1579 // It's possible to reach here if, for example, someone 1580 // calls completedPreDhcpAction() after provisioning with 1581 // a static IP configuration. 1582 if (mDhcpClient != null) { 1583 mDhcpClient.sendMessage(DhcpClient.CMD_PRE_DHCP_ACTION_COMPLETE); 1584 } 1585 break; 1586 1587 case EVENT_NETLINK_LINKPROPERTIES_CHANGED: 1588 if (!handleLinkPropertiesUpdate(SEND_CALLBACKS)) { 1589 transitionTo(mStoppingState); 1590 } 1591 break; 1592 1593 case CMD_UPDATE_TCP_BUFFER_SIZES: 1594 mTcpBufferSizes = (String) msg.obj; 1595 // This cannot possibly change provisioning state. 1596 handleLinkPropertiesUpdate(SEND_CALLBACKS); 1597 break; 1598 1599 case CMD_UPDATE_HTTP_PROXY: 1600 mHttpProxy = (ProxyInfo) msg.obj; 1601 // This cannot possibly change provisioning state. 1602 handleLinkPropertiesUpdate(SEND_CALLBACKS); 1603 break; 1604 1605 case CMD_SET_MULTICAST_FILTER: { 1606 mMulticastFiltering = (boolean) msg.obj; 1607 if (mApfFilter != null) { 1608 mApfFilter.setMulticastFilter(mMulticastFiltering); 1609 } else { 1610 mCallback.setFallbackMulticastFilter(mMulticastFiltering); 1611 } 1612 break; 1613 } 1614 1615 case EVENT_READ_PACKET_FILTER_COMPLETE: { 1616 if (mApfFilter != null) { 1617 mApfFilter.setDataSnapshot((byte[]) msg.obj); 1618 } 1619 mApfDataSnapshotComplete.open(); 1620 break; 1621 } 1622 1623 case CMD_ADD_KEEPALIVE_PACKET_FILTER_TO_APF: { 1624 final int slot = msg.arg1; 1625 1626 if (mApfFilter != null) { 1627 if (msg.obj instanceof NattKeepalivePacketDataParcelable) { 1628 mApfFilter.addNattKeepalivePacketFilter(slot, 1629 (NattKeepalivePacketDataParcelable) msg.obj); 1630 } else if (msg.obj instanceof TcpKeepalivePacketDataParcelable) { 1631 mApfFilter.addTcpKeepalivePacketFilter(slot, 1632 (TcpKeepalivePacketDataParcelable) msg.obj); 1633 } 1634 } 1635 break; 1636 } 1637 1638 case CMD_REMOVE_KEEPALIVE_PACKET_FILTER_FROM_APF: { 1639 final int slot = msg.arg1; 1640 if (mApfFilter != null) { 1641 mApfFilter.removeKeepalivePacketFilter(slot); 1642 } 1643 break; 1644 } 1645 1646 case EVENT_DHCPACTION_TIMEOUT: 1647 stopDhcpAction(); 1648 break; 1649 1650 case DhcpClient.CMD_PRE_DHCP_ACTION: 1651 if (mConfiguration.mRequestedPreDhcpActionMs > 0) { 1652 ensureDhcpAction(); 1653 } else { 1654 sendMessage(EVENT_PRE_DHCP_ACTION_COMPLETE); 1655 } 1656 break; 1657 1658 case DhcpClient.CMD_CLEAR_LINKADDRESS: 1659 mInterfaceCtrl.clearIPv4Address(); 1660 break; 1661 1662 case DhcpClient.CMD_CONFIGURE_LINKADDRESS: { 1663 final LinkAddress ipAddress = (LinkAddress) msg.obj; 1664 if (mInterfaceCtrl.setIPv4Address(ipAddress)) { 1665 mDhcpClient.sendMessage(DhcpClient.EVENT_LINKADDRESS_CONFIGURED); 1666 } else { 1667 logError("Failed to set IPv4 address."); 1668 dispatchCallback(PROV_CHANGE_LOST_PROVISIONING, 1669 new LinkProperties(mLinkProperties)); 1670 transitionTo(mStoppingState); 1671 } 1672 break; 1673 } 1674 1675 // This message is only received when: 1676 // 1677 // a) initial address acquisition succeeds, 1678 // b) renew succeeds or is NAK'd, 1679 // c) rebind succeeds or is NAK'd, or 1680 // c) the lease expires, 1681 // 1682 // but never when initial address acquisition fails. The latter 1683 // condition is now governed by the provisioning timeout. 1684 case DhcpClient.CMD_POST_DHCP_ACTION: 1685 stopDhcpAction(); 1686 1687 switch (msg.arg1) { 1688 case DhcpClient.DHCP_SUCCESS: 1689 handleIPv4Success((DhcpResults) msg.obj); 1690 break; 1691 case DhcpClient.DHCP_FAILURE: 1692 handleIPv4Failure(); 1693 break; 1694 default: 1695 logError("Unknown CMD_POST_DHCP_ACTION status: %s", msg.arg1); 1696 } 1697 break; 1698 1699 case DhcpClient.CMD_ON_QUIT: 1700 // DHCPv4 quit early for some reason. 1701 logError("Unexpected CMD_ON_QUIT."); 1702 mDhcpClient = null; 1703 break; 1704 1705 default: 1706 return NOT_HANDLED; 1707 } 1708 1709 mMsgStateLogger.handled(this, getCurrentState()); 1710 return HANDLED; 1711 } 1712 } 1713 1714 private static class MessageHandlingLogger { 1715 public String processedInState; 1716 public String receivedInState; 1717 reset()1718 public void reset() { 1719 processedInState = null; 1720 receivedInState = null; 1721 } 1722 handled(State processedIn, IState receivedIn)1723 public void handled(State processedIn, IState receivedIn) { 1724 processedInState = processedIn.getClass().getSimpleName(); 1725 receivedInState = receivedIn.getName(); 1726 } 1727 toString()1728 public String toString() { 1729 return String.format("rcvd_in=%s, proc_in=%s", 1730 receivedInState, processedInState); 1731 } 1732 } 1733 setNeighborParameters( INetd netd, String ifName, int numSolicits, int interSolicitIntervalMs)1734 private static void setNeighborParameters( 1735 INetd netd, String ifName, int numSolicits, int interSolicitIntervalMs) 1736 throws RemoteException, IllegalArgumentException { 1737 Preconditions.checkNotNull(netd); 1738 Preconditions.checkArgument(!TextUtils.isEmpty(ifName)); 1739 Preconditions.checkArgument(numSolicits > 0); 1740 Preconditions.checkArgument(interSolicitIntervalMs > 0); 1741 1742 for (int family : new Integer[]{INetd.IPV4, INetd.IPV6}) { 1743 netd.setProcSysNet(family, INetd.NEIGH, ifName, "retrans_time_ms", 1744 Integer.toString(interSolicitIntervalMs)); 1745 netd.setProcSysNet(family, INetd.NEIGH, ifName, "ucast_solicit", 1746 Integer.toString(numSolicits)); 1747 } 1748 } 1749 1750 // TODO: extract out into CollectionUtils. any(Iterable<T> coll, Predicate<T> fn)1751 static <T> boolean any(Iterable<T> coll, Predicate<T> fn) { 1752 for (T t : coll) { 1753 if (fn.test(t)) { 1754 return true; 1755 } 1756 } 1757 return false; 1758 } 1759 all(Iterable<T> coll, Predicate<T> fn)1760 static <T> boolean all(Iterable<T> coll, Predicate<T> fn) { 1761 return !any(coll, not(fn)); 1762 } 1763 not(Predicate<T> fn)1764 static <T> Predicate<T> not(Predicate<T> fn) { 1765 return (t) -> !fn.test(t); 1766 } 1767 join(String delimiter, Collection<T> coll)1768 static <T> String join(String delimiter, Collection<T> coll) { 1769 return coll.stream().map(Object::toString).collect(Collectors.joining(delimiter)); 1770 } 1771 find(Iterable<T> coll, Predicate<T> fn)1772 static <T> T find(Iterable<T> coll, Predicate<T> fn) { 1773 for (T t: coll) { 1774 if (fn.test(t)) { 1775 return t; 1776 } 1777 } 1778 return null; 1779 } 1780 findAll(Collection<T> coll, Predicate<T> fn)1781 static <T> List<T> findAll(Collection<T> coll, Predicate<T> fn) { 1782 return coll.stream().filter(fn).collect(Collectors.toList()); 1783 } 1784 } 1785