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