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