1 /* 2 * Copyright (C) 2016 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 com.android.internal.util.MessageUtils; 20 import com.android.internal.util.WakeupMessage; 21 22 import android.content.Context; 23 import android.net.apf.ApfCapabilities; 24 import android.net.apf.ApfFilter; 25 import android.net.DhcpResults; 26 import android.net.InterfaceConfiguration; 27 import android.net.LinkAddress; 28 import android.net.LinkProperties; 29 import android.net.LinkProperties.ProvisioningChange; 30 import android.net.ProxyInfo; 31 import android.net.RouteInfo; 32 import android.net.StaticIpConfiguration; 33 import android.net.dhcp.DhcpClient; 34 import android.net.metrics.IpConnectivityLog; 35 import android.net.metrics.IpManagerEvent; 36 import android.os.INetworkManagementService; 37 import android.os.Message; 38 import android.os.RemoteException; 39 import android.os.ServiceManager; 40 import android.os.SystemClock; 41 import android.text.TextUtils; 42 import android.util.LocalLog; 43 import android.util.Log; 44 import android.util.SparseArray; 45 46 import com.android.internal.annotations.VisibleForTesting; 47 import com.android.internal.util.IndentingPrintWriter; 48 import com.android.internal.util.State; 49 import com.android.internal.util.StateMachine; 50 import com.android.server.net.NetlinkTracker; 51 52 import java.io.FileDescriptor; 53 import java.io.PrintWriter; 54 import java.net.InetAddress; 55 import java.net.NetworkInterface; 56 import java.net.SocketException; 57 import java.util.Objects; 58 import java.util.StringJoiner; 59 60 61 /** 62 * IpManager 63 * 64 * This class provides the interface to IP-layer provisioning and maintenance 65 * functionality that can be used by transport layers like Wi-Fi, Ethernet, 66 * et cetera. 67 * 68 * [ Lifetime ] 69 * IpManager is designed to be instantiated as soon as the interface name is 70 * known and can be as long-lived as the class containing it (i.e. declaring 71 * it "private final" is okay). 72 * 73 * @hide 74 */ 75 public class IpManager extends StateMachine { 76 private static final boolean DBG = false; 77 private static final boolean VDBG = false; 78 79 // For message logging. 80 private static final Class[] sMessageClasses = { IpManager.class, DhcpClient.class }; 81 private static final SparseArray<String> sWhatToString = 82 MessageUtils.findMessageNames(sMessageClasses); 83 84 /** 85 * Callbacks for handling IpManager events. 86 */ 87 public static class Callback { 88 // In order to receive onPreDhcpAction(), call #withPreDhcpAction() 89 // when constructing a ProvisioningConfiguration. 90 // 91 // Implementations of onPreDhcpAction() must call 92 // IpManager#completedPreDhcpAction() to indicate that DHCP is clear 93 // to proceed. 94 public void onPreDhcpAction() {} 95 public void onPostDhcpAction() {} 96 97 // This is purely advisory and not an indication of provisioning 98 // success or failure. This is only here for callers that want to 99 // expose DHCPv4 results to other APIs (e.g., WifiInfo#setInetAddress). 100 // DHCPv4 or static IPv4 configuration failure or success can be 101 // determined by whether or not the passed-in DhcpResults object is 102 // null or not. 103 public void onNewDhcpResults(DhcpResults dhcpResults) {} 104 105 public void onProvisioningSuccess(LinkProperties newLp) {} 106 public void onProvisioningFailure(LinkProperties newLp) {} 107 108 // Invoked on LinkProperties changes. 109 public void onLinkPropertiesChange(LinkProperties newLp) {} 110 111 // Called when the internal IpReachabilityMonitor (if enabled) has 112 // detected the loss of a critical number of required neighbors. 113 public void onReachabilityLost(String logMsg) {} 114 115 // Called when the IpManager state machine terminates. 116 public void onQuit() {} 117 118 // Install an APF program to filter incoming packets. 119 public void installPacketFilter(byte[] filter) {} 120 121 // If multicast filtering cannot be accomplished with APF, this function will be called to 122 // actuate multicast filtering using another means. 123 public void setFallbackMulticastFilter(boolean enabled) {} 124 125 // Enabled/disable Neighbor Discover offload functionality. This is 126 // called, for example, whenever 464xlat is being started or stopped. 127 public void setNeighborDiscoveryOffload(boolean enable) {} 128 } 129 130 public static class WaitForProvisioningCallback extends Callback { 131 private LinkProperties mCallbackLinkProperties; 132 133 public LinkProperties waitForProvisioning() { 134 synchronized (this) { 135 try { 136 wait(); 137 } catch (InterruptedException e) {} 138 return mCallbackLinkProperties; 139 } 140 } 141 142 @Override 143 public void onProvisioningSuccess(LinkProperties newLp) { 144 synchronized (this) { 145 mCallbackLinkProperties = newLp; 146 notify(); 147 } 148 } 149 150 @Override 151 public void onProvisioningFailure(LinkProperties newLp) { 152 synchronized (this) { 153 mCallbackLinkProperties = null; 154 notify(); 155 } 156 } 157 } 158 159 // Use a wrapper class to log in order to ensure complete and detailed 160 // logging. This method is lighter weight than annotations/reflection 161 // and has the following benefits: 162 // 163 // - No invoked method can be forgotten. 164 // Any new method added to IpManager.Callback must be overridden 165 // here or it will never be called. 166 // 167 // - No invoking call site can be forgotten. 168 // Centralized logging in this way means call sites don't need to 169 // remember to log, and therefore no call site can be forgotten. 170 // 171 // - No variation in log format among call sites. 172 // Encourages logging of any available arguments, and all call sites 173 // are necessarily logged identically. 174 // 175 // TODO: Find an lighter weight approach. 176 private class LoggingCallbackWrapper extends Callback { 177 private static final String PREFIX = "INVOKE "; 178 private Callback mCallback; 179 180 public LoggingCallbackWrapper(Callback callback) { 181 mCallback = callback; 182 } 183 184 private void log(String msg) { 185 mLocalLog.log(PREFIX + msg); 186 } 187 188 @Override 189 public void onPreDhcpAction() { 190 mCallback.onPreDhcpAction(); 191 log("onPreDhcpAction()"); 192 } 193 @Override 194 public void onPostDhcpAction() { 195 mCallback.onPostDhcpAction(); 196 log("onPostDhcpAction()"); 197 } 198 @Override 199 public void onNewDhcpResults(DhcpResults dhcpResults) { 200 mCallback.onNewDhcpResults(dhcpResults); 201 log("onNewDhcpResults({" + dhcpResults + "})"); 202 } 203 @Override 204 public void onProvisioningSuccess(LinkProperties newLp) { 205 mCallback.onProvisioningSuccess(newLp); 206 log("onProvisioningSuccess({" + newLp + "})"); 207 } 208 @Override 209 public void onProvisioningFailure(LinkProperties newLp) { 210 mCallback.onProvisioningFailure(newLp); 211 log("onProvisioningFailure({" + newLp + "})"); 212 } 213 @Override 214 public void onLinkPropertiesChange(LinkProperties newLp) { 215 mCallback.onLinkPropertiesChange(newLp); 216 log("onLinkPropertiesChange({" + newLp + "})"); 217 } 218 @Override 219 public void onReachabilityLost(String logMsg) { 220 mCallback.onReachabilityLost(logMsg); 221 log("onReachabilityLost(" + logMsg + ")"); 222 } 223 @Override 224 public void onQuit() { 225 mCallback.onQuit(); 226 log("onQuit()"); 227 } 228 @Override 229 public void installPacketFilter(byte[] filter) { 230 mCallback.installPacketFilter(filter); 231 log("installPacketFilter(byte[" + filter.length + "])"); 232 } 233 @Override 234 public void setFallbackMulticastFilter(boolean enabled) { 235 mCallback.setFallbackMulticastFilter(enabled); 236 log("setFallbackMulticastFilter(" + enabled + ")"); 237 } 238 @Override 239 public void setNeighborDiscoveryOffload(boolean enable) { 240 mCallback.setNeighborDiscoveryOffload(enable); 241 log("setNeighborDiscoveryOffload(" + enable + ")"); 242 } 243 } 244 245 /** 246 * This class encapsulates parameters to be passed to 247 * IpManager#startProvisioning(). A defensive copy is made by IpManager 248 * and the values specified herein are in force until IpManager#stop() 249 * is called. 250 * 251 * Example use: 252 * 253 * final ProvisioningConfiguration config = 254 * mIpManager.buildProvisioningConfiguration() 255 * .withPreDhcpAction() 256 * .withProvisioningTimeoutMs(36 * 1000) 257 * .build(); 258 * mIpManager.startProvisioning(config); 259 * ... 260 * mIpManager.stop(); 261 * 262 * The specified provisioning configuration will only be active until 263 * IpManager#stop() is called. Future calls to IpManager#startProvisioning() 264 * must specify the configuration again. 265 */ 266 public static class ProvisioningConfiguration { 267 // TODO: Delete this default timeout once those callers that care are 268 // fixed to pass in their preferred timeout. 269 // 270 // We pick 36 seconds so we can send DHCP requests at 271 // 272 // t=0, t=2, t=6, t=14, t=30 273 // 274 // allowing for 10% jitter. 275 private static final int DEFAULT_TIMEOUT_MS = 36 * 1000; 276 277 public static class Builder { 278 private ProvisioningConfiguration mConfig = new ProvisioningConfiguration(); 279 280 public Builder withoutIPv4() { 281 mConfig.mEnableIPv4 = false; 282 return this; 283 } 284 285 public Builder withoutIPv6() { 286 mConfig.mEnableIPv6 = false; 287 return this; 288 } 289 290 public Builder withoutIpReachabilityMonitor() { 291 mConfig.mUsingIpReachabilityMonitor = false; 292 return this; 293 } 294 295 public Builder withPreDhcpAction() { 296 mConfig.mRequestedPreDhcpActionMs = DEFAULT_TIMEOUT_MS; 297 return this; 298 } 299 300 public Builder withPreDhcpAction(int dhcpActionTimeoutMs) { 301 mConfig.mRequestedPreDhcpActionMs = dhcpActionTimeoutMs; 302 return this; 303 } 304 305 public Builder withStaticConfiguration(StaticIpConfiguration staticConfig) { 306 mConfig.mStaticIpConfig = staticConfig; 307 return this; 308 } 309 310 public Builder withApfCapabilities(ApfCapabilities apfCapabilities) { 311 mConfig.mApfCapabilities = apfCapabilities; 312 return this; 313 } 314 315 public Builder withProvisioningTimeoutMs(int timeoutMs) { 316 mConfig.mProvisioningTimeoutMs = timeoutMs; 317 return this; 318 } 319 320 public ProvisioningConfiguration build() { 321 return new ProvisioningConfiguration(mConfig); 322 } 323 } 324 325 /* package */ boolean mEnableIPv4 = true; 326 /* package */ boolean mEnableIPv6 = true; 327 /* package */ boolean mUsingIpReachabilityMonitor = true; 328 /* package */ int mRequestedPreDhcpActionMs; 329 /* package */ StaticIpConfiguration mStaticIpConfig; 330 /* package */ ApfCapabilities mApfCapabilities; 331 /* package */ int mProvisioningTimeoutMs = DEFAULT_TIMEOUT_MS; 332 333 public ProvisioningConfiguration() {} 334 335 public ProvisioningConfiguration(ProvisioningConfiguration other) { 336 mEnableIPv4 = other.mEnableIPv4; 337 mEnableIPv6 = other.mEnableIPv6; 338 mUsingIpReachabilityMonitor = other.mUsingIpReachabilityMonitor; 339 mRequestedPreDhcpActionMs = other.mRequestedPreDhcpActionMs; 340 mStaticIpConfig = other.mStaticIpConfig; 341 mApfCapabilities = other.mApfCapabilities; 342 mProvisioningTimeoutMs = other.mProvisioningTimeoutMs; 343 } 344 345 @Override 346 public String toString() { 347 return new StringJoiner(", ", getClass().getSimpleName() + "{", "}") 348 .add("mEnableIPv4: " + mEnableIPv4) 349 .add("mEnableIPv6: " + mEnableIPv6) 350 .add("mUsingIpReachabilityMonitor: " + mUsingIpReachabilityMonitor) 351 .add("mRequestedPreDhcpActionMs: " + mRequestedPreDhcpActionMs) 352 .add("mStaticIpConfig: " + mStaticIpConfig) 353 .add("mApfCapabilities: " + mApfCapabilities) 354 .add("mProvisioningTimeoutMs: " + mProvisioningTimeoutMs) 355 .toString(); 356 } 357 } 358 359 public static final String DUMP_ARG = "ipmanager"; 360 361 private static final int CMD_STOP = 1; 362 private static final int CMD_START = 2; 363 private static final int CMD_CONFIRM = 3; 364 private static final int EVENT_PRE_DHCP_ACTION_COMPLETE = 4; 365 // Sent by NetlinkTracker to communicate netlink events. 366 private static final int EVENT_NETLINK_LINKPROPERTIES_CHANGED = 5; 367 private static final int CMD_UPDATE_TCP_BUFFER_SIZES = 6; 368 private static final int CMD_UPDATE_HTTP_PROXY = 7; 369 private static final int CMD_SET_MULTICAST_FILTER = 8; 370 private static final int EVENT_PROVISIONING_TIMEOUT = 9; 371 private static final int EVENT_DHCPACTION_TIMEOUT = 10; 372 373 private static final int MAX_LOG_RECORDS = 500; 374 375 private static final boolean NO_CALLBACKS = false; 376 private static final boolean SEND_CALLBACKS = true; 377 378 // This must match the interface prefix in clatd.c. 379 // TODO: Revert this hack once IpManager and Nat464Xlat work in concert. 380 private static final String CLAT_PREFIX = "v4-"; 381 382 private final State mStoppedState = new StoppedState(); 383 private final State mStoppingState = new StoppingState(); 384 private final State mStartedState = new StartedState(); 385 386 private final String mTag; 387 private final Context mContext; 388 private final String mInterfaceName; 389 private final String mClatInterfaceName; 390 @VisibleForTesting 391 protected final Callback mCallback; 392 private final INetworkManagementService mNwService; 393 private final NetlinkTracker mNetlinkTracker; 394 private final WakeupMessage mProvisioningTimeoutAlarm; 395 private final WakeupMessage mDhcpActionTimeoutAlarm; 396 private final LocalLog mLocalLog; 397 private final IpConnectivityLog mMetricsLog = new IpConnectivityLog(); 398 399 private NetworkInterface mNetworkInterface; 400 401 /** 402 * Non-final member variables accessed only from within our StateMachine. 403 */ 404 private LinkProperties mLinkProperties; 405 private ProvisioningConfiguration mConfiguration; 406 private IpReachabilityMonitor mIpReachabilityMonitor; 407 private DhcpClient mDhcpClient; 408 private DhcpResults mDhcpResults; 409 private String mTcpBufferSizes; 410 private ProxyInfo mHttpProxy; 411 private ApfFilter mApfFilter; 412 private boolean mMulticastFiltering; 413 private long mStartTimeMillis; 414 415 public IpManager(Context context, String ifName, Callback callback) 416 throws IllegalArgumentException { 417 this(context, ifName, callback, INetworkManagementService.Stub.asInterface( 418 ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE))); 419 } 420 421 /** 422 * An expanded constructor, useful for dependency injection. 423 */ 424 public IpManager(Context context, String ifName, Callback callback, 425 INetworkManagementService nwService) throws IllegalArgumentException { 426 super(IpManager.class.getSimpleName() + "." + ifName); 427 mTag = getName(); 428 429 mContext = context; 430 mInterfaceName = ifName; 431 mClatInterfaceName = CLAT_PREFIX + ifName; 432 mCallback = new LoggingCallbackWrapper(callback); 433 mNwService = nwService; 434 435 mNetlinkTracker = new NetlinkTracker( 436 mInterfaceName, 437 new NetlinkTracker.Callback() { 438 @Override 439 public void update() { 440 sendMessage(EVENT_NETLINK_LINKPROPERTIES_CHANGED); 441 } 442 }) { 443 @Override 444 public void interfaceAdded(String iface) { 445 super.interfaceAdded(iface); 446 if (mClatInterfaceName.equals(iface)) { 447 mCallback.setNeighborDiscoveryOffload(false); 448 } 449 } 450 451 @Override 452 public void interfaceRemoved(String iface) { 453 super.interfaceRemoved(iface); 454 if (mClatInterfaceName.equals(iface)) { 455 // TODO: consider sending a message to the IpManager main 456 // StateMachine thread, in case "NDO enabled" state becomes 457 // tied to more things that 464xlat operation. 458 mCallback.setNeighborDiscoveryOffload(true); 459 } 460 } 461 }; 462 463 try { 464 mNwService.registerObserver(mNetlinkTracker); 465 } catch (RemoteException e) { 466 Log.e(mTag, "Couldn't register NetlinkTracker: " + e.toString()); 467 } 468 469 resetLinkProperties(); 470 471 mProvisioningTimeoutAlarm = new WakeupMessage(mContext, getHandler(), 472 mTag + ".EVENT_PROVISIONING_TIMEOUT", EVENT_PROVISIONING_TIMEOUT); 473 mDhcpActionTimeoutAlarm = new WakeupMessage(mContext, getHandler(), 474 mTag + ".EVENT_DHCPACTION_TIMEOUT", EVENT_DHCPACTION_TIMEOUT); 475 476 // Super simple StateMachine. 477 addState(mStoppedState); 478 addState(mStartedState); 479 addState(mStoppingState); 480 481 setInitialState(mStoppedState); 482 mLocalLog = new LocalLog(MAX_LOG_RECORDS); 483 super.start(); 484 } 485 486 @Override 487 protected void onQuitting() { 488 mCallback.onQuit(); 489 } 490 491 // Shut down this IpManager instance altogether. 492 public void shutdown() { 493 stop(); 494 quit(); 495 } 496 497 public static ProvisioningConfiguration.Builder buildProvisioningConfiguration() { 498 return new ProvisioningConfiguration.Builder(); 499 } 500 501 public void startProvisioning(ProvisioningConfiguration req) { 502 getNetworkInterface(); 503 504 mCallback.setNeighborDiscoveryOffload(true); 505 sendMessage(CMD_START, new ProvisioningConfiguration(req)); 506 } 507 508 // TODO: Delete this. 509 public void startProvisioning(StaticIpConfiguration staticIpConfig) { 510 startProvisioning(buildProvisioningConfiguration() 511 .withStaticConfiguration(staticIpConfig) 512 .build()); 513 } 514 515 public void startProvisioning() { 516 startProvisioning(new ProvisioningConfiguration()); 517 } 518 519 public void stop() { 520 sendMessage(CMD_STOP); 521 } 522 523 public void confirmConfiguration() { 524 sendMessage(CMD_CONFIRM); 525 } 526 527 public void completedPreDhcpAction() { 528 sendMessage(EVENT_PRE_DHCP_ACTION_COMPLETE); 529 } 530 531 /** 532 * Set the TCP buffer sizes to use. 533 * 534 * This may be called, repeatedly, at any time before or after a call to 535 * #startProvisioning(). The setting is cleared upon calling #stop(). 536 */ 537 public void setTcpBufferSizes(String tcpBufferSizes) { 538 sendMessage(CMD_UPDATE_TCP_BUFFER_SIZES, tcpBufferSizes); 539 } 540 541 /** 542 * Set the HTTP Proxy configuration to use. 543 * 544 * This may be called, repeatedly, at any time before or after a call to 545 * #startProvisioning(). The setting is cleared upon calling #stop(). 546 */ 547 public void setHttpProxy(ProxyInfo proxyInfo) { 548 sendMessage(CMD_UPDATE_HTTP_PROXY, proxyInfo); 549 } 550 551 /** 552 * Enable or disable the multicast filter. Attempts to use APF to accomplish the filtering, 553 * if not, Callback.setFallbackMulticastFilter() is called. 554 */ 555 public void setMulticastFilter(boolean enabled) { 556 sendMessage(CMD_SET_MULTICAST_FILTER, enabled); 557 } 558 559 public void dump(FileDescriptor fd, PrintWriter writer, String[] args) { 560 IndentingPrintWriter pw = new IndentingPrintWriter(writer, " "); 561 pw.println("APF dump:"); 562 pw.increaseIndent(); 563 // Thread-unsafe access to mApfFilter but just used for debugging. 564 ApfFilter apfFilter = mApfFilter; 565 if (apfFilter != null) { 566 apfFilter.dump(pw); 567 } else { 568 pw.println("No apf support"); 569 } 570 pw.decreaseIndent(); 571 572 pw.println(); 573 pw.println("StateMachine dump:"); 574 pw.increaseIndent(); 575 mLocalLog.readOnlyLocalLog().dump(fd, pw, args); 576 pw.decreaseIndent(); 577 } 578 579 580 /** 581 * Internals. 582 */ 583 584 @Override 585 protected String getWhatToString(int what) { 586 return sWhatToString.get(what, "UNKNOWN: " + Integer.toString(what)); 587 } 588 589 @Override 590 protected String getLogRecString(Message msg) { 591 final String logLine = String.format( 592 "%s/%d %d %d %s", 593 mInterfaceName, mNetworkInterface == null ? -1 : mNetworkInterface.getIndex(), 594 msg.arg1, msg.arg2, Objects.toString(msg.obj)); 595 596 final String richerLogLine = getWhatToString(msg.what) + " " + logLine; 597 mLocalLog.log(richerLogLine); 598 if (VDBG) { 599 Log.d(mTag, richerLogLine); 600 } 601 602 return logLine; 603 } 604 605 @Override 606 protected boolean recordLogRec(Message msg) { 607 // Don't log EVENT_NETLINK_LINKPROPERTIES_CHANGED. They can be noisy, 608 // and we already log any LinkProperties change that results in an 609 // invocation of IpManager.Callback#onLinkPropertiesChange(). 610 return (msg.what != EVENT_NETLINK_LINKPROPERTIES_CHANGED); 611 } 612 613 private void getNetworkInterface() { 614 try { 615 mNetworkInterface = NetworkInterface.getByName(mInterfaceName); 616 } catch (SocketException | NullPointerException e) { 617 // TODO: throw new IllegalStateException. 618 Log.e(mTag, "ALERT: Failed to get interface object: ", e); 619 } 620 } 621 622 // This needs to be called with care to ensure that our LinkProperties 623 // are in sync with the actual LinkProperties of the interface. For example, 624 // we should only call this if we know for sure that there are no IP addresses 625 // assigned to the interface, etc. 626 private void resetLinkProperties() { 627 mNetlinkTracker.clearLinkProperties(); 628 mConfiguration = null; 629 mDhcpResults = null; 630 mTcpBufferSizes = ""; 631 mHttpProxy = null; 632 633 mLinkProperties = new LinkProperties(); 634 mLinkProperties.setInterfaceName(mInterfaceName); 635 } 636 637 private void recordMetric(final int type) { 638 if (mStartTimeMillis <= 0) { Log.wtf(mTag, "Start time undefined!"); } 639 final long duration = SystemClock.elapsedRealtime() - mStartTimeMillis; 640 mMetricsLog.log(new IpManagerEvent(mInterfaceName, type, duration)); 641 } 642 643 // For now: use WifiStateMachine's historical notion of provisioned. 644 private static boolean isProvisioned(LinkProperties lp) { 645 // For historical reasons, we should connect even if all we have is 646 // an IPv4 address and nothing else. 647 return lp.isProvisioned() || lp.hasIPv4Address(); 648 } 649 650 // TODO: Investigate folding all this into the existing static function 651 // LinkProperties.compareProvisioning() or some other single function that 652 // takes two LinkProperties objects and returns a ProvisioningChange 653 // object that is a correct and complete assessment of what changed, taking 654 // account of the asymmetries described in the comments in this function. 655 // Then switch to using it everywhere (IpReachabilityMonitor, etc.). 656 private static ProvisioningChange compareProvisioning( 657 LinkProperties oldLp, LinkProperties newLp) { 658 ProvisioningChange delta; 659 660 final boolean wasProvisioned = isProvisioned(oldLp); 661 final boolean isProvisioned = isProvisioned(newLp); 662 663 if (!wasProvisioned && isProvisioned) { 664 delta = ProvisioningChange.GAINED_PROVISIONING; 665 } else if (wasProvisioned && isProvisioned) { 666 delta = ProvisioningChange.STILL_PROVISIONED; 667 } else if (!wasProvisioned && !isProvisioned) { 668 delta = ProvisioningChange.STILL_NOT_PROVISIONED; 669 } else { 670 // (wasProvisioned && !isProvisioned) 671 // 672 // Note that this is true even if we lose a configuration element 673 // (e.g., a default gateway) that would not be required to advance 674 // into provisioned state. This is intended: if we have a default 675 // router and we lose it, that's a sure sign of a problem, but if 676 // we connect to a network with no IPv4 DNS servers, we consider 677 // that to be a network without DNS servers and connect anyway. 678 // 679 // See the comment below. 680 delta = ProvisioningChange.LOST_PROVISIONING; 681 } 682 683 // Additionally: 684 // 685 // Partial configurations (e.g., only an IPv4 address with no DNS 686 // servers and no default route) are accepted as long as DHCPv4 687 // succeeds. On such a network, isProvisioned() will always return 688 // false, because the configuration is not complete, but we want to 689 // connect anyway. It might be a disconnected network such as a 690 // Chromecast or a wireless printer, for example. 691 // 692 // Because on such a network isProvisioned() will always return false, 693 // delta will never be LOST_PROVISIONING. So check for loss of 694 // provisioning here too. 695 if ((oldLp.hasIPv4Address() && !newLp.hasIPv4Address()) || 696 (oldLp.isIPv6Provisioned() && !newLp.isIPv6Provisioned())) { 697 delta = ProvisioningChange.LOST_PROVISIONING; 698 } 699 700 // Additionally: 701 // 702 // If the previous link properties had a global IPv6 address and an 703 // IPv6 default route then also consider the loss of that default route 704 // to be a loss of provisioning. See b/27962810. 705 if (oldLp.hasGlobalIPv6Address() && oldLp.hasIPv6DefaultRoute() && 706 !newLp.hasIPv6DefaultRoute()) { 707 delta = ProvisioningChange.LOST_PROVISIONING; 708 } 709 710 return delta; 711 } 712 713 private void dispatchCallback(ProvisioningChange delta, LinkProperties newLp) { 714 switch (delta) { 715 case GAINED_PROVISIONING: 716 if (VDBG) { Log.d(mTag, "onProvisioningSuccess()"); } 717 recordMetric(IpManagerEvent.PROVISIONING_OK); 718 mCallback.onProvisioningSuccess(newLp); 719 break; 720 721 case LOST_PROVISIONING: 722 if (VDBG) { Log.d(mTag, "onProvisioningFailure()"); } 723 recordMetric(IpManagerEvent.PROVISIONING_FAIL); 724 mCallback.onProvisioningFailure(newLp); 725 break; 726 727 default: 728 if (VDBG) { Log.d(mTag, "onLinkPropertiesChange()"); } 729 mCallback.onLinkPropertiesChange(newLp); 730 break; 731 } 732 } 733 734 // Updates all IpManager-related state concerned with LinkProperties. 735 // Returns a ProvisioningChange for possibly notifying other interested 736 // parties that are not fronted by IpManager. 737 private ProvisioningChange setLinkProperties(LinkProperties newLp) { 738 if (mApfFilter != null) { 739 mApfFilter.setLinkProperties(newLp); 740 } 741 if (mIpReachabilityMonitor != null) { 742 mIpReachabilityMonitor.updateLinkProperties(newLp); 743 } 744 745 ProvisioningChange delta = compareProvisioning(mLinkProperties, newLp); 746 mLinkProperties = new LinkProperties(newLp); 747 748 if (delta == ProvisioningChange.GAINED_PROVISIONING) { 749 // TODO: Add a proper ProvisionedState and cancel the alarm in 750 // its enter() method. 751 mProvisioningTimeoutAlarm.cancel(); 752 } 753 754 return delta; 755 } 756 757 private boolean linkPropertiesUnchanged(LinkProperties newLp) { 758 return Objects.equals(newLp, mLinkProperties); 759 } 760 761 private LinkProperties assembleLinkProperties() { 762 // [1] Create a new LinkProperties object to populate. 763 LinkProperties newLp = new LinkProperties(); 764 newLp.setInterfaceName(mInterfaceName); 765 766 // [2] Pull in data from netlink: 767 // - IPv4 addresses 768 // - IPv6 addresses 769 // - IPv6 routes 770 // - IPv6 DNS servers 771 LinkProperties netlinkLinkProperties = mNetlinkTracker.getLinkProperties(); 772 newLp.setLinkAddresses(netlinkLinkProperties.getLinkAddresses()); 773 for (RouteInfo route : netlinkLinkProperties.getRoutes()) { 774 newLp.addRoute(route); 775 } 776 for (InetAddress dns : netlinkLinkProperties.getDnsServers()) { 777 // Only add likely reachable DNS servers. 778 // TODO: investigate deleting this. 779 if (newLp.isReachable(dns)) { 780 newLp.addDnsServer(dns); 781 } 782 } 783 784 // [3] Add in data from DHCPv4, if available. 785 // 786 // mDhcpResults is never shared with any other owner so we don't have 787 // to worry about concurrent modification. 788 if (mDhcpResults != null) { 789 for (RouteInfo route : mDhcpResults.getRoutes(mInterfaceName)) { 790 newLp.addRoute(route); 791 } 792 for (InetAddress dns : mDhcpResults.dnsServers) { 793 // Only add likely reachable DNS servers. 794 // TODO: investigate deleting this. 795 if (newLp.isReachable(dns)) { 796 newLp.addDnsServer(dns); 797 } 798 } 799 newLp.setDomains(mDhcpResults.domains); 800 801 if (mDhcpResults.mtu != 0) { 802 newLp.setMtu(mDhcpResults.mtu); 803 } 804 } 805 806 // [4] Add in TCP buffer sizes and HTTP Proxy config, if available. 807 if (!TextUtils.isEmpty(mTcpBufferSizes)) { 808 newLp.setTcpBufferSizes(mTcpBufferSizes); 809 } 810 if (mHttpProxy != null) { 811 newLp.setHttpProxy(mHttpProxy); 812 } 813 814 if (VDBG) { 815 Log.d(mTag, "newLp{" + newLp + "}"); 816 } 817 return newLp; 818 } 819 820 // Returns false if we have lost provisioning, true otherwise. 821 private boolean handleLinkPropertiesUpdate(boolean sendCallbacks) { 822 final LinkProperties newLp = assembleLinkProperties(); 823 if (linkPropertiesUnchanged(newLp)) { 824 return true; 825 } 826 final ProvisioningChange delta = setLinkProperties(newLp); 827 if (sendCallbacks) { 828 dispatchCallback(delta, newLp); 829 } 830 return (delta != ProvisioningChange.LOST_PROVISIONING); 831 } 832 833 private boolean setIPv4Address(LinkAddress address) { 834 final InterfaceConfiguration ifcg = new InterfaceConfiguration(); 835 ifcg.setLinkAddress(address); 836 try { 837 mNwService.setInterfaceConfig(mInterfaceName, ifcg); 838 if (VDBG) Log.d(mTag, "IPv4 configuration succeeded"); 839 } catch (IllegalStateException | RemoteException e) { 840 Log.e(mTag, "IPv4 configuration failed: ", e); 841 return false; 842 } 843 return true; 844 } 845 846 private void clearIPv4Address() { 847 try { 848 final InterfaceConfiguration ifcg = new InterfaceConfiguration(); 849 ifcg.setLinkAddress(new LinkAddress("0.0.0.0/0")); 850 mNwService.setInterfaceConfig(mInterfaceName, ifcg); 851 } catch (IllegalStateException | RemoteException e) { 852 Log.e(mTag, "ALERT: Failed to clear IPv4 address on interface " + mInterfaceName, e); 853 } 854 } 855 856 private void handleIPv4Success(DhcpResults dhcpResults) { 857 mDhcpResults = new DhcpResults(dhcpResults); 858 final LinkProperties newLp = assembleLinkProperties(); 859 final ProvisioningChange delta = setLinkProperties(newLp); 860 861 if (VDBG) { 862 Log.d(mTag, "onNewDhcpResults(" + Objects.toString(dhcpResults) + ")"); 863 } 864 mCallback.onNewDhcpResults(dhcpResults); 865 dispatchCallback(delta, newLp); 866 } 867 868 private void handleIPv4Failure() { 869 // TODO: Investigate deleting this clearIPv4Address() call. 870 // 871 // DhcpClient will send us CMD_CLEAR_LINKADDRESS in all circumstances 872 // that could trigger a call to this function. If we missed handling 873 // that message in StartedState for some reason we would still clear 874 // any addresses upon entry to StoppedState. 875 clearIPv4Address(); 876 mDhcpResults = null; 877 if (VDBG) { Log.d(mTag, "onNewDhcpResults(null)"); } 878 mCallback.onNewDhcpResults(null); 879 880 handleProvisioningFailure(); 881 } 882 883 private void handleProvisioningFailure() { 884 final LinkProperties newLp = assembleLinkProperties(); 885 ProvisioningChange delta = setLinkProperties(newLp); 886 // If we've gotten here and we're still not provisioned treat that as 887 // a total loss of provisioning. 888 // 889 // Either (a) static IP configuration failed or (b) DHCPv4 failed AND 890 // there was no usable IPv6 obtained before a non-zero provisioning 891 // timeout expired. 892 // 893 // Regardless: GAME OVER. 894 if (delta == ProvisioningChange.STILL_NOT_PROVISIONED) { 895 delta = ProvisioningChange.LOST_PROVISIONING; 896 } 897 898 dispatchCallback(delta, newLp); 899 if (delta == ProvisioningChange.LOST_PROVISIONING) { 900 transitionTo(mStoppingState); 901 } 902 } 903 904 private boolean startIPv4() { 905 // If we have a StaticIpConfiguration attempt to apply it and 906 // handle the result accordingly. 907 if (mConfiguration.mStaticIpConfig != null) { 908 if (setIPv4Address(mConfiguration.mStaticIpConfig.ipAddress)) { 909 handleIPv4Success(new DhcpResults(mConfiguration.mStaticIpConfig)); 910 } else { 911 if (VDBG) { Log.d(mTag, "onProvisioningFailure()"); } 912 recordMetric(IpManagerEvent.PROVISIONING_FAIL); 913 mCallback.onProvisioningFailure(new LinkProperties(mLinkProperties)); 914 return false; 915 } 916 } else { 917 // Start DHCPv4. 918 mDhcpClient = DhcpClient.makeDhcpClient(mContext, IpManager.this, mInterfaceName); 919 mDhcpClient.registerForPreDhcpNotification(); 920 mDhcpClient.sendMessage(DhcpClient.CMD_START_DHCP); 921 } 922 923 return true; 924 } 925 926 private boolean startIPv6() { 927 // Set privacy extensions. 928 try { 929 mNwService.setInterfaceIpv6PrivacyExtensions(mInterfaceName, true); 930 mNwService.enableIpv6(mInterfaceName); 931 } catch (RemoteException re) { 932 Log.e(mTag, "Unable to change interface settings: " + re); 933 return false; 934 } catch (IllegalStateException ie) { 935 Log.e(mTag, "Unable to change interface settings: " + ie); 936 return false; 937 } 938 939 return true; 940 } 941 942 943 class StoppedState extends State { 944 @Override 945 public void enter() { 946 try { 947 mNwService.disableIpv6(mInterfaceName); 948 mNwService.clearInterfaceAddresses(mInterfaceName); 949 } catch (Exception e) { 950 Log.e(mTag, "Failed to clear addresses or disable IPv6" + e); 951 } 952 953 resetLinkProperties(); 954 if (mStartTimeMillis > 0) { 955 recordMetric(IpManagerEvent.COMPLETE_LIFECYCLE); 956 mStartTimeMillis = 0; 957 } 958 } 959 960 @Override 961 public boolean processMessage(Message msg) { 962 switch (msg.what) { 963 case CMD_STOP: 964 break; 965 966 case CMD_START: 967 mConfiguration = (ProvisioningConfiguration) msg.obj; 968 transitionTo(mStartedState); 969 break; 970 971 case EVENT_NETLINK_LINKPROPERTIES_CHANGED: 972 handleLinkPropertiesUpdate(NO_CALLBACKS); 973 break; 974 975 case CMD_UPDATE_TCP_BUFFER_SIZES: 976 mTcpBufferSizes = (String) msg.obj; 977 handleLinkPropertiesUpdate(NO_CALLBACKS); 978 break; 979 980 case CMD_UPDATE_HTTP_PROXY: 981 mHttpProxy = (ProxyInfo) msg.obj; 982 handleLinkPropertiesUpdate(NO_CALLBACKS); 983 break; 984 985 case CMD_SET_MULTICAST_FILTER: 986 mMulticastFiltering = (boolean) msg.obj; 987 break; 988 989 case DhcpClient.CMD_ON_QUIT: 990 // Everything is already stopped. 991 Log.e(mTag, "Unexpected CMD_ON_QUIT (already stopped)."); 992 break; 993 994 default: 995 return NOT_HANDLED; 996 } 997 return HANDLED; 998 } 999 } 1000 1001 class StoppingState extends State { 1002 @Override 1003 public void enter() { 1004 if (mDhcpClient == null) { 1005 // There's no DHCPv4 for which to wait; proceed to stopped. 1006 transitionTo(mStoppedState); 1007 } 1008 } 1009 1010 @Override 1011 public boolean processMessage(Message msg) { 1012 switch (msg.what) { 1013 case DhcpClient.CMD_ON_QUIT: 1014 mDhcpClient = null; 1015 transitionTo(mStoppedState); 1016 break; 1017 1018 default: 1019 deferMessage(msg); 1020 } 1021 return HANDLED; 1022 } 1023 } 1024 1025 class StartedState extends State { 1026 private boolean mDhcpActionInFlight; 1027 1028 @Override 1029 public void enter() { 1030 mStartTimeMillis = SystemClock.elapsedRealtime(); 1031 1032 mApfFilter = ApfFilter.maybeCreate(mConfiguration.mApfCapabilities, mNetworkInterface, 1033 mCallback, mMulticastFiltering); 1034 // TODO: investigate the effects of any multicast filtering racing/interfering with the 1035 // rest of this IP configuration startup. 1036 if (mApfFilter == null) { 1037 mCallback.setFallbackMulticastFilter(mMulticastFiltering); 1038 } 1039 1040 if (mConfiguration.mProvisioningTimeoutMs > 0) { 1041 final long alarmTime = SystemClock.elapsedRealtime() + 1042 mConfiguration.mProvisioningTimeoutMs; 1043 mProvisioningTimeoutAlarm.schedule(alarmTime); 1044 } 1045 1046 if (mConfiguration.mEnableIPv6) { 1047 // TODO: Consider transitionTo(mStoppingState) if this fails. 1048 startIPv6(); 1049 } 1050 1051 if (mConfiguration.mEnableIPv4) { 1052 if (!startIPv4()) { 1053 transitionTo(mStoppingState); 1054 return; 1055 } 1056 } 1057 1058 if (mConfiguration.mUsingIpReachabilityMonitor) { 1059 mIpReachabilityMonitor = new IpReachabilityMonitor( 1060 mContext, 1061 mInterfaceName, 1062 new IpReachabilityMonitor.Callback() { 1063 @Override 1064 public void notifyLost(InetAddress ip, String logMsg) { 1065 mCallback.onReachabilityLost(logMsg); 1066 } 1067 }); 1068 } 1069 } 1070 1071 @Override 1072 public void exit() { 1073 mProvisioningTimeoutAlarm.cancel(); 1074 stopDhcpAction(); 1075 1076 if (mIpReachabilityMonitor != null) { 1077 mIpReachabilityMonitor.stop(); 1078 mIpReachabilityMonitor = null; 1079 } 1080 1081 if (mDhcpClient != null) { 1082 mDhcpClient.sendMessage(DhcpClient.CMD_STOP_DHCP); 1083 mDhcpClient.doQuit(); 1084 } 1085 1086 if (mApfFilter != null) { 1087 mApfFilter.shutdown(); 1088 mApfFilter = null; 1089 } 1090 1091 resetLinkProperties(); 1092 } 1093 1094 private void ensureDhcpAction() { 1095 if (!mDhcpActionInFlight) { 1096 mCallback.onPreDhcpAction(); 1097 mDhcpActionInFlight = true; 1098 final long alarmTime = SystemClock.elapsedRealtime() + 1099 mConfiguration.mRequestedPreDhcpActionMs; 1100 mDhcpActionTimeoutAlarm.schedule(alarmTime); 1101 } 1102 } 1103 1104 private void stopDhcpAction() { 1105 mDhcpActionTimeoutAlarm.cancel(); 1106 if (mDhcpActionInFlight) { 1107 mCallback.onPostDhcpAction(); 1108 mDhcpActionInFlight = false; 1109 } 1110 } 1111 1112 @Override 1113 public boolean processMessage(Message msg) { 1114 switch (msg.what) { 1115 case CMD_STOP: 1116 transitionTo(mStoppingState); 1117 break; 1118 1119 case CMD_START: 1120 Log.e(mTag, "ALERT: START received in StartedState. Please fix caller."); 1121 break; 1122 1123 case CMD_CONFIRM: 1124 // TODO: Possibly introduce a second type of confirmation 1125 // that both probes (a) on-link neighbors and (b) does 1126 // a DHCPv4 RENEW. We used to do this on Wi-Fi framework 1127 // roams. 1128 if (mIpReachabilityMonitor != null) { 1129 mIpReachabilityMonitor.probeAll(); 1130 } 1131 break; 1132 1133 case EVENT_PRE_DHCP_ACTION_COMPLETE: 1134 // It's possible to reach here if, for example, someone 1135 // calls completedPreDhcpAction() after provisioning with 1136 // a static IP configuration. 1137 if (mDhcpClient != null) { 1138 mDhcpClient.sendMessage(DhcpClient.CMD_PRE_DHCP_ACTION_COMPLETE); 1139 } 1140 break; 1141 1142 case EVENT_NETLINK_LINKPROPERTIES_CHANGED: 1143 if (!handleLinkPropertiesUpdate(SEND_CALLBACKS)) { 1144 transitionTo(mStoppingState); 1145 } 1146 break; 1147 1148 case CMD_UPDATE_TCP_BUFFER_SIZES: 1149 mTcpBufferSizes = (String) msg.obj; 1150 // This cannot possibly change provisioning state. 1151 handleLinkPropertiesUpdate(SEND_CALLBACKS); 1152 break; 1153 1154 case CMD_UPDATE_HTTP_PROXY: 1155 mHttpProxy = (ProxyInfo) msg.obj; 1156 // This cannot possibly change provisioning state. 1157 handleLinkPropertiesUpdate(SEND_CALLBACKS); 1158 break; 1159 1160 case CMD_SET_MULTICAST_FILTER: { 1161 mMulticastFiltering = (boolean) msg.obj; 1162 if (mApfFilter != null) { 1163 mApfFilter.setMulticastFilter(mMulticastFiltering); 1164 } else { 1165 mCallback.setFallbackMulticastFilter(mMulticastFiltering); 1166 } 1167 break; 1168 } 1169 1170 case EVENT_PROVISIONING_TIMEOUT: 1171 handleProvisioningFailure(); 1172 break; 1173 1174 case EVENT_DHCPACTION_TIMEOUT: 1175 stopDhcpAction(); 1176 break; 1177 1178 case DhcpClient.CMD_PRE_DHCP_ACTION: 1179 if (mConfiguration.mRequestedPreDhcpActionMs > 0) { 1180 ensureDhcpAction(); 1181 } else { 1182 sendMessage(EVENT_PRE_DHCP_ACTION_COMPLETE); 1183 } 1184 break; 1185 1186 case DhcpClient.CMD_CLEAR_LINKADDRESS: 1187 clearIPv4Address(); 1188 break; 1189 1190 case DhcpClient.CMD_CONFIGURE_LINKADDRESS: { 1191 final LinkAddress ipAddress = (LinkAddress) msg.obj; 1192 if (setIPv4Address(ipAddress)) { 1193 mDhcpClient.sendMessage(DhcpClient.EVENT_LINKADDRESS_CONFIGURED); 1194 } else { 1195 Log.e(mTag, "Failed to set IPv4 address!"); 1196 dispatchCallback(ProvisioningChange.LOST_PROVISIONING, 1197 new LinkProperties(mLinkProperties)); 1198 transitionTo(mStoppingState); 1199 } 1200 break; 1201 } 1202 1203 // This message is only received when: 1204 // 1205 // a) initial address acquisition succeeds, 1206 // b) renew succeeds or is NAK'd, 1207 // c) rebind succeeds or is NAK'd, or 1208 // c) the lease expires, 1209 // 1210 // but never when initial address acquisition fails. The latter 1211 // condition is now governed by the provisioning timeout. 1212 case DhcpClient.CMD_POST_DHCP_ACTION: 1213 stopDhcpAction(); 1214 1215 switch (msg.arg1) { 1216 case DhcpClient.DHCP_SUCCESS: 1217 handleIPv4Success((DhcpResults) msg.obj); 1218 break; 1219 case DhcpClient.DHCP_FAILURE: 1220 handleIPv4Failure(); 1221 break; 1222 default: 1223 Log.e(mTag, "Unknown CMD_POST_DHCP_ACTION status:" + msg.arg1); 1224 } 1225 break; 1226 1227 case DhcpClient.CMD_ON_QUIT: 1228 // DHCPv4 quit early for some reason. 1229 Log.e(mTag, "Unexpected CMD_ON_QUIT."); 1230 mDhcpClient = null; 1231 break; 1232 1233 default: 1234 return NOT_HANDLED; 1235 } 1236 return HANDLED; 1237 } 1238 } 1239 } 1240