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