1/*
2 * Copyright (C) 2016 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.server.connectivity.tethering;
18
19import static android.net.util.NetworkConstants.asByte;
20import static android.net.util.NetworkConstants.FF;
21import static android.net.util.NetworkConstants.RFC7421_PREFIX_LENGTH;
22
23import android.net.ConnectivityManager;
24import android.net.INetd;
25import android.net.INetworkStatsService;
26import android.net.InterfaceConfiguration;
27import android.net.IpPrefix;
28import android.net.LinkAddress;
29import android.net.LinkProperties;
30import android.net.NetworkUtils;
31import android.net.RouteInfo;
32import android.net.ip.InterfaceController;
33import android.net.ip.RouterAdvertisementDaemon;
34import android.net.ip.RouterAdvertisementDaemon.RaParams;
35import android.net.util.InterfaceParams;
36import android.net.util.InterfaceSet;
37import android.net.util.SharedLog;
38import android.os.INetworkManagementService;
39import android.os.Looper;
40import android.os.Message;
41import android.os.RemoteException;
42import android.os.ServiceSpecificException;
43import android.util.Log;
44import android.util.Slog;
45import android.util.SparseArray;
46
47import com.android.internal.util.MessageUtils;
48import com.android.internal.util.Protocol;
49import com.android.internal.util.State;
50import com.android.internal.util.StateMachine;
51
52import java.net.Inet6Address;
53import java.net.InetAddress;
54import java.net.UnknownHostException;
55import java.util.ArrayList;
56import java.util.HashSet;
57import java.util.Objects;
58import java.util.Random;
59import java.util.Set;
60
61/**
62 * Provides the interface to IP-layer serving functionality for a given network
63 * interface, e.g. for tethering or "local-only hotspot" mode.
64 *
65 * @hide
66 */
67public class TetherInterfaceStateMachine extends StateMachine {
68    private static final IpPrefix LINK_LOCAL_PREFIX = new IpPrefix("fe80::/64");
69    private static final byte DOUG_ADAMS = (byte) 42;
70
71    private static final String USB_NEAR_IFACE_ADDR = "192.168.42.129";
72    private static final int USB_PREFIX_LENGTH = 24;
73    private static final String WIFI_HOST_IFACE_ADDR = "192.168.43.1";
74    private static final int WIFI_HOST_IFACE_PREFIX_LENGTH = 24;
75
76    private final static String TAG = "TetherInterfaceSM";
77    private final static boolean DBG = false;
78    private final static boolean VDBG = false;
79    private static final Class[] messageClasses = {
80            TetherInterfaceStateMachine.class
81    };
82    private static final SparseArray<String> sMagicDecoderRing =
83            MessageUtils.findMessageNames(messageClasses);
84
85    private static final int BASE_IFACE              = Protocol.BASE_TETHERING + 100;
86    // request from the user that it wants to tether
87    public static final int CMD_TETHER_REQUESTED            = BASE_IFACE + 2;
88    // request from the user that it wants to untether
89    public static final int CMD_TETHER_UNREQUESTED          = BASE_IFACE + 3;
90    // notification that this interface is down
91    public static final int CMD_INTERFACE_DOWN              = BASE_IFACE + 4;
92    // notification from the master SM that it had trouble enabling IP Forwarding
93    public static final int CMD_IP_FORWARDING_ENABLE_ERROR  = BASE_IFACE + 7;
94    // notification from the master SM that it had trouble disabling IP Forwarding
95    public static final int CMD_IP_FORWARDING_DISABLE_ERROR = BASE_IFACE + 8;
96    // notification from the master SM that it had trouble starting tethering
97    public static final int CMD_START_TETHERING_ERROR       = BASE_IFACE + 9;
98    // notification from the master SM that it had trouble stopping tethering
99    public static final int CMD_STOP_TETHERING_ERROR        = BASE_IFACE + 10;
100    // notification from the master SM that it had trouble setting the DNS forwarders
101    public static final int CMD_SET_DNS_FORWARDERS_ERROR    = BASE_IFACE + 11;
102    // the upstream connection has changed
103    public static final int CMD_TETHER_CONNECTION_CHANGED   = BASE_IFACE + 12;
104    // new IPv6 tethering parameters need to be processed
105    public static final int CMD_IPV6_TETHER_UPDATE          = BASE_IFACE + 13;
106
107    private final State mInitialState;
108    private final State mLocalHotspotState;
109    private final State mTetheredState;
110    private final State mUnavailableState;
111
112    private final SharedLog mLog;
113    private final INetworkManagementService mNMService;
114    private final INetd mNetd;
115    private final INetworkStatsService mStatsService;
116    private final IControlsTethering mTetherController;
117    private final InterfaceController mInterfaceCtrl;
118
119    private final String mIfaceName;
120    private final int mInterfaceType;
121    private final LinkProperties mLinkProperties;
122
123    private final TetheringDependencies mDeps;
124
125    private int mLastError;
126    private int mServingMode;
127    private InterfaceSet mUpstreamIfaceSet;  // may change over time
128    private InterfaceParams mInterfaceParams;
129    // TODO: De-duplicate this with mLinkProperties above. Currently, these link
130    // properties are those selected by the IPv6TetheringCoordinator and relayed
131    // to us. By comparison, mLinkProperties contains the addresses and directly
132    // connected routes that have been formed from these properties iff. we have
133    // succeeded in configuring them and are able to announce them within Router
134    // Advertisements (otherwise, we do not add them to mLinkProperties at all).
135    private LinkProperties mLastIPv6LinkProperties;
136    private RouterAdvertisementDaemon mRaDaemon;
137    private RaParams mLastRaParams;
138
139    public TetherInterfaceStateMachine(
140            String ifaceName, Looper looper, int interfaceType, SharedLog log,
141            INetworkManagementService nMService, INetworkStatsService statsService,
142            IControlsTethering tetherController,
143            TetheringDependencies deps) {
144        super(ifaceName, looper);
145        mLog = log.forSubComponent(ifaceName);
146        mNMService = nMService;
147        mNetd = deps.getNetdService();
148        mStatsService = statsService;
149        mTetherController = tetherController;
150        mInterfaceCtrl = new InterfaceController(ifaceName, nMService, mNetd, mLog);
151        mIfaceName = ifaceName;
152        mInterfaceType = interfaceType;
153        mLinkProperties = new LinkProperties();
154        mDeps = deps;
155        resetLinkProperties();
156        mLastError = ConnectivityManager.TETHER_ERROR_NO_ERROR;
157        mServingMode = IControlsTethering.STATE_AVAILABLE;
158
159        mInitialState = new InitialState();
160        mLocalHotspotState = new LocalHotspotState();
161        mTetheredState = new TetheredState();
162        mUnavailableState = new UnavailableState();
163        addState(mInitialState);
164        addState(mLocalHotspotState);
165        addState(mTetheredState);
166        addState(mUnavailableState);
167
168        setInitialState(mInitialState);
169    }
170
171    public String interfaceName() { return mIfaceName; }
172
173    public int interfaceType() { return mInterfaceType; }
174
175    public int lastError() { return mLastError; }
176
177    public int servingMode() { return mServingMode; }
178
179    public LinkProperties linkProperties() { return new LinkProperties(mLinkProperties); }
180
181    public void stop() { sendMessage(CMD_INTERFACE_DOWN); }
182
183    public void unwanted() { sendMessage(CMD_TETHER_UNREQUESTED); }
184
185    /**
186     * Internals.
187     */
188
189    private boolean startIPv4() { return configureIPv4(true); }
190
191    private void stopIPv4() {
192        configureIPv4(false);
193        // NOTE: All of configureIPv4() will be refactored out of existence
194        // into calls to InterfaceController, shared with startIPv4().
195        mInterfaceCtrl.clearIPv4Address();
196    }
197
198    // TODO: Refactor this in terms of calls to InterfaceController.
199    private boolean configureIPv4(boolean enabled) {
200        if (VDBG) Log.d(TAG, "configureIPv4(" + enabled + ")");
201
202        // TODO: Replace this hard-coded information with dynamically selected
203        // config passed down to us by a higher layer IP-coordinating element.
204        String ipAsString = null;
205        int prefixLen = 0;
206        if (mInterfaceType == ConnectivityManager.TETHERING_USB) {
207            ipAsString = USB_NEAR_IFACE_ADDR;
208            prefixLen = USB_PREFIX_LENGTH;
209        } else if (mInterfaceType == ConnectivityManager.TETHERING_WIFI) {
210            ipAsString = getRandomWifiIPv4Address();
211            prefixLen = WIFI_HOST_IFACE_PREFIX_LENGTH;
212        } else {
213            // Nothing to do, BT does this elsewhere.
214            return true;
215        }
216
217        final LinkAddress linkAddr;
218        try {
219            final InterfaceConfiguration ifcg = mNMService.getInterfaceConfig(mIfaceName);
220            if (ifcg == null) {
221                mLog.e("Received null interface config");
222                return false;
223            }
224
225            InetAddress addr = NetworkUtils.numericToInetAddress(ipAsString);
226            linkAddr = new LinkAddress(addr, prefixLen);
227            ifcg.setLinkAddress(linkAddr);
228            if (mInterfaceType == ConnectivityManager.TETHERING_WIFI) {
229                // The WiFi stack has ownership of the interface up/down state.
230                // It is unclear whether the Bluetooth or USB stacks will manage their own
231                // state.
232                ifcg.ignoreInterfaceUpDownStatus();
233            } else {
234                if (enabled) {
235                    ifcg.setInterfaceUp();
236                } else {
237                    ifcg.setInterfaceDown();
238                }
239            }
240            ifcg.clearFlag("running");
241            mNMService.setInterfaceConfig(mIfaceName, ifcg);
242        } catch (Exception e) {
243            mLog.e("Error configuring interface " + e);
244            return false;
245        }
246
247        // Directly-connected route.
248        final RouteInfo route = new RouteInfo(linkAddr);
249        if (enabled) {
250            mLinkProperties.addLinkAddress(linkAddr);
251            mLinkProperties.addRoute(route);
252        } else {
253            mLinkProperties.removeLinkAddress(linkAddr);
254            mLinkProperties.removeRoute(route);
255        }
256        return true;
257    }
258
259    private String getRandomWifiIPv4Address() {
260        try {
261            byte[] bytes = NetworkUtils.numericToInetAddress(WIFI_HOST_IFACE_ADDR).getAddress();
262            bytes[3] = getRandomSanitizedByte(DOUG_ADAMS, asByte(0), asByte(1), FF);
263            return InetAddress.getByAddress(bytes).getHostAddress();
264        } catch (Exception e) {
265            return WIFI_HOST_IFACE_ADDR;
266        }
267    }
268
269    private boolean startIPv6() {
270        mInterfaceParams = mDeps.getInterfaceParams(mIfaceName);
271        if (mInterfaceParams == null) {
272            mLog.e("Failed to find InterfaceParams");
273            stopIPv6();
274            return false;
275        }
276
277        mRaDaemon = mDeps.getRouterAdvertisementDaemon(mInterfaceParams);
278        if (!mRaDaemon.start()) {
279            stopIPv6();
280            return false;
281        }
282
283        return true;
284    }
285
286    private void stopIPv6() {
287        mInterfaceParams = null;
288        setRaParams(null);
289
290        if (mRaDaemon != null) {
291            mRaDaemon.stop();
292            mRaDaemon = null;
293        }
294    }
295
296    // IPv6TetheringCoordinator sends updates with carefully curated IPv6-only
297    // LinkProperties. These have extraneous data filtered out and only the
298    // necessary prefixes included (per its prefix distribution policy).
299    //
300    // TODO: Evaluate using a data structure than is more directly suited to
301    // communicating only the relevant information.
302    private void updateUpstreamIPv6LinkProperties(LinkProperties v6only) {
303        if (mRaDaemon == null) return;
304
305        // Avoid unnecessary work on spurious updates.
306        if (Objects.equals(mLastIPv6LinkProperties, v6only)) {
307            return;
308        }
309
310        RaParams params = null;
311
312        if (v6only != null) {
313            params = new RaParams();
314            params.mtu = v6only.getMtu();
315            params.hasDefaultRoute = v6only.hasIPv6DefaultRoute();
316
317            for (LinkAddress linkAddr : v6only.getLinkAddresses()) {
318                if (linkAddr.getPrefixLength() != RFC7421_PREFIX_LENGTH) continue;
319
320                final IpPrefix prefix = new IpPrefix(
321                        linkAddr.getAddress(), linkAddr.getPrefixLength());
322                params.prefixes.add(prefix);
323
324                final Inet6Address dnsServer = getLocalDnsIpFor(prefix);
325                if (dnsServer != null) {
326                    params.dnses.add(dnsServer);
327                }
328            }
329        }
330        // If v6only is null, we pass in null to setRaParams(), which handles
331        // deprecation of any existing RA data.
332
333        setRaParams(params);
334        mLastIPv6LinkProperties = v6only;
335    }
336
337    private void configureLocalIPv6Routes(
338            HashSet<IpPrefix> deprecatedPrefixes, HashSet<IpPrefix> newPrefixes) {
339        // [1] Remove the routes that are deprecated.
340        if (!deprecatedPrefixes.isEmpty()) {
341            final ArrayList<RouteInfo> toBeRemoved =
342                    getLocalRoutesFor(mIfaceName, deprecatedPrefixes);
343            try {
344                final int removalFailures = mNMService.removeRoutesFromLocalNetwork(toBeRemoved);
345                if (removalFailures > 0) {
346                    mLog.e(String.format("Failed to remove %d IPv6 routes from local table.",
347                            removalFailures));
348                }
349            } catch (RemoteException e) {
350                mLog.e("Failed to remove IPv6 routes from local table: " + e);
351            }
352
353            for (RouteInfo route : toBeRemoved) mLinkProperties.removeRoute(route);
354        }
355
356        // [2] Add only the routes that have not previously been added.
357        if (newPrefixes != null && !newPrefixes.isEmpty()) {
358            HashSet<IpPrefix> addedPrefixes = (HashSet) newPrefixes.clone();
359            if (mLastRaParams != null) {
360                addedPrefixes.removeAll(mLastRaParams.prefixes);
361            }
362
363            if (mLastRaParams == null || mLastRaParams.prefixes.isEmpty()) {
364                // We need to be able to send unicast RAs, and clients might
365                // like to ping the default router's link-local address.  Note
366                // that we never remove the link-local route from the network
367                // until Tethering disables tethering on the interface. We
368                // only need to add the link-local prefix once, but in the
369                // event we add it more than once netd silently ignores EEXIST.
370                addedPrefixes.add(LINK_LOCAL_PREFIX);
371            }
372
373            if (!addedPrefixes.isEmpty()) {
374                final ArrayList<RouteInfo> toBeAdded =
375                        getLocalRoutesFor(mIfaceName, addedPrefixes);
376                try {
377                    // It's safe to call addInterfaceToLocalNetwork() even if
378                    // the interface is already in the local_network. Note also
379                    // that adding routes that already exist does not cause an
380                    // error (EEXIST is silently ignored).
381                    mNMService.addInterfaceToLocalNetwork(mIfaceName, toBeAdded);
382                } catch (RemoteException e) {
383                    mLog.e("Failed to add IPv6 routes to local table: " + e);
384                }
385
386                for (RouteInfo route : toBeAdded) mLinkProperties.addRoute(route);
387            }
388        }
389    }
390
391    private void configureLocalIPv6Dns(
392            HashSet<Inet6Address> deprecatedDnses, HashSet<Inet6Address> newDnses) {
393        // TODO: Is this really necessary? Can we not fail earlier if INetd cannot be located?
394        if (mNetd == null) {
395            if (newDnses != null) newDnses.clear();
396            mLog.e("No netd service instance available; not setting local IPv6 addresses");
397            return;
398        }
399
400        // [1] Remove deprecated local DNS IP addresses.
401        if (!deprecatedDnses.isEmpty()) {
402            for (Inet6Address dns : deprecatedDnses) {
403                if (!mInterfaceCtrl.removeAddress(dns, RFC7421_PREFIX_LENGTH)) {
404                    mLog.e("Failed to remove local dns IP " + dns);
405                }
406
407                mLinkProperties.removeLinkAddress(new LinkAddress(dns, RFC7421_PREFIX_LENGTH));
408            }
409        }
410
411        // [2] Add only the local DNS IP addresses that have not previously been added.
412        if (newDnses != null && !newDnses.isEmpty()) {
413            final HashSet<Inet6Address> addedDnses = (HashSet) newDnses.clone();
414            if (mLastRaParams != null) {
415                addedDnses.removeAll(mLastRaParams.dnses);
416            }
417
418            for (Inet6Address dns : addedDnses) {
419                if (!mInterfaceCtrl.addAddress(dns, RFC7421_PREFIX_LENGTH)) {
420                    mLog.e("Failed to add local dns IP " + dns);
421                    newDnses.remove(dns);
422                }
423
424                mLinkProperties.addLinkAddress(new LinkAddress(dns, RFC7421_PREFIX_LENGTH));
425            }
426        }
427
428        try {
429            mNetd.tetherApplyDnsInterfaces();
430        } catch (ServiceSpecificException | RemoteException e) {
431            mLog.e("Failed to update local DNS caching server");
432            if (newDnses != null) newDnses.clear();
433        }
434    }
435
436    private void setRaParams(RaParams newParams) {
437        if (mRaDaemon != null) {
438            final RaParams deprecatedParams =
439                    RaParams.getDeprecatedRaParams(mLastRaParams, newParams);
440
441            configureLocalIPv6Routes(deprecatedParams.prefixes,
442                    (newParams != null) ? newParams.prefixes : null);
443
444            configureLocalIPv6Dns(deprecatedParams.dnses,
445                    (newParams != null) ? newParams.dnses : null);
446
447            mRaDaemon.buildNewRa(deprecatedParams, newParams);
448        }
449
450        mLastRaParams = newParams;
451    }
452
453    private void logMessage(State state, int what) {
454        mLog.log(state.getName() + " got " + sMagicDecoderRing.get(what, Integer.toString(what)));
455    }
456
457    private void sendInterfaceState(int newInterfaceState) {
458        mServingMode = newInterfaceState;
459        mTetherController.updateInterfaceState(
460                TetherInterfaceStateMachine.this, newInterfaceState, mLastError);
461        sendLinkProperties();
462    }
463
464    private void sendLinkProperties() {
465        mTetherController.updateLinkProperties(
466                TetherInterfaceStateMachine.this, new LinkProperties(mLinkProperties));
467    }
468
469    private void resetLinkProperties() {
470        mLinkProperties.clear();
471        mLinkProperties.setInterfaceName(mIfaceName);
472    }
473
474    class InitialState extends State {
475        @Override
476        public void enter() {
477            sendInterfaceState(IControlsTethering.STATE_AVAILABLE);
478        }
479
480        @Override
481        public boolean processMessage(Message message) {
482            logMessage(this, message.what);
483            switch (message.what) {
484                case CMD_TETHER_REQUESTED:
485                    mLastError = ConnectivityManager.TETHER_ERROR_NO_ERROR;
486                    switch (message.arg1) {
487                        case IControlsTethering.STATE_LOCAL_ONLY:
488                            transitionTo(mLocalHotspotState);
489                            break;
490                        case IControlsTethering.STATE_TETHERED:
491                            transitionTo(mTetheredState);
492                            break;
493                        default:
494                            mLog.e("Invalid tethering interface serving state specified.");
495                    }
496                    break;
497                case CMD_INTERFACE_DOWN:
498                    transitionTo(mUnavailableState);
499                    break;
500                case CMD_IPV6_TETHER_UPDATE:
501                    updateUpstreamIPv6LinkProperties((LinkProperties) message.obj);
502                    break;
503                default:
504                    return NOT_HANDLED;
505            }
506            return HANDLED;
507        }
508    }
509
510    class BaseServingState extends State {
511        @Override
512        public void enter() {
513            if (!startIPv4()) {
514                mLastError = ConnectivityManager.TETHER_ERROR_IFACE_CFG_ERROR;
515                return;
516            }
517
518            try {
519                mNMService.tetherInterface(mIfaceName);
520            } catch (Exception e) {
521                mLog.e("Error Tethering: " + e);
522                mLastError = ConnectivityManager.TETHER_ERROR_TETHER_IFACE_ERROR;
523                return;
524            }
525
526            if (!startIPv6()) {
527                mLog.e("Failed to startIPv6");
528                // TODO: Make this a fatal error once Bluetooth IPv6 is sorted.
529                return;
530            }
531        }
532
533        @Override
534        public void exit() {
535            // Note that at this point, we're leaving the tethered state.  We can fail any
536            // of these operations, but it doesn't really change that we have to try them
537            // all in sequence.
538            stopIPv6();
539
540            try {
541                mNMService.untetherInterface(mIfaceName);
542            } catch (Exception e) {
543                mLastError = ConnectivityManager.TETHER_ERROR_UNTETHER_IFACE_ERROR;
544                mLog.e("Failed to untether interface: " + e);
545            }
546
547            stopIPv4();
548
549            resetLinkProperties();
550        }
551
552        @Override
553        public boolean processMessage(Message message) {
554            logMessage(this, message.what);
555            switch (message.what) {
556                case CMD_TETHER_UNREQUESTED:
557                    transitionTo(mInitialState);
558                    if (DBG) Log.d(TAG, "Untethered (unrequested)" + mIfaceName);
559                    break;
560                case CMD_INTERFACE_DOWN:
561                    transitionTo(mUnavailableState);
562                    if (DBG) Log.d(TAG, "Untethered (ifdown)" + mIfaceName);
563                    break;
564                case CMD_IPV6_TETHER_UPDATE:
565                    updateUpstreamIPv6LinkProperties((LinkProperties) message.obj);
566                    sendLinkProperties();
567                    break;
568                case CMD_IP_FORWARDING_ENABLE_ERROR:
569                case CMD_IP_FORWARDING_DISABLE_ERROR:
570                case CMD_START_TETHERING_ERROR:
571                case CMD_STOP_TETHERING_ERROR:
572                case CMD_SET_DNS_FORWARDERS_ERROR:
573                    mLastError = ConnectivityManager.TETHER_ERROR_MASTER_ERROR;
574                    transitionTo(mInitialState);
575                    break;
576                default:
577                    return false;
578            }
579            return true;
580        }
581    }
582
583    // Handling errors in BaseServingState.enter() by transitioning is
584    // problematic because transitioning during a multi-state jump yields
585    // a Log.wtf(). Ultimately, there should be only one ServingState,
586    // and forwarding and NAT rules should be handled by a coordinating
587    // functional element outside of TetherInterfaceStateMachine.
588    class LocalHotspotState extends BaseServingState {
589        @Override
590        public void enter() {
591            super.enter();
592            if (mLastError != ConnectivityManager.TETHER_ERROR_NO_ERROR) {
593                transitionTo(mInitialState);
594            }
595
596            if (DBG) Log.d(TAG, "Local hotspot " + mIfaceName);
597            sendInterfaceState(IControlsTethering.STATE_LOCAL_ONLY);
598        }
599
600        @Override
601        public boolean processMessage(Message message) {
602            if (super.processMessage(message)) return true;
603
604            logMessage(this, message.what);
605            switch (message.what) {
606                case CMD_TETHER_REQUESTED:
607                    mLog.e("CMD_TETHER_REQUESTED while in local-only hotspot mode.");
608                    break;
609                case CMD_TETHER_CONNECTION_CHANGED:
610                    // Ignored in local hotspot state.
611                    break;
612                default:
613                    return false;
614            }
615            return true;
616        }
617    }
618
619    // Handling errors in BaseServingState.enter() by transitioning is
620    // problematic because transitioning during a multi-state jump yields
621    // a Log.wtf(). Ultimately, there should be only one ServingState,
622    // and forwarding and NAT rules should be handled by a coordinating
623    // functional element outside of TetherInterfaceStateMachine.
624    class TetheredState extends BaseServingState {
625        @Override
626        public void enter() {
627            super.enter();
628            if (mLastError != ConnectivityManager.TETHER_ERROR_NO_ERROR) {
629                transitionTo(mInitialState);
630            }
631
632            if (DBG) Log.d(TAG, "Tethered " + mIfaceName);
633            sendInterfaceState(IControlsTethering.STATE_TETHERED);
634        }
635
636        @Override
637        public void exit() {
638            cleanupUpstream();
639            super.exit();
640        }
641
642        private void cleanupUpstream() {
643            if (mUpstreamIfaceSet == null) return;
644
645            for (String ifname : mUpstreamIfaceSet.ifnames) cleanupUpstreamInterface(ifname);
646            mUpstreamIfaceSet = null;
647        }
648
649        private void cleanupUpstreamInterface(String upstreamIface) {
650            // Note that we don't care about errors here.
651            // Sometimes interfaces are gone before we get
652            // to remove their rules, which generates errors.
653            // Just do the best we can.
654            try {
655                // About to tear down NAT; gather remaining statistics.
656                mStatsService.forceUpdate();
657            } catch (Exception e) {
658                if (VDBG) Log.e(TAG, "Exception in forceUpdate: " + e.toString());
659            }
660            try {
661                mNMService.stopInterfaceForwarding(mIfaceName, upstreamIface);
662            } catch (Exception e) {
663                if (VDBG) Log.e(TAG, "Exception in removeInterfaceForward: " + e.toString());
664            }
665            try {
666                mNMService.disableNat(mIfaceName, upstreamIface);
667            } catch (Exception e) {
668                if (VDBG) Log.e(TAG, "Exception in disableNat: " + e.toString());
669            }
670        }
671
672        @Override
673        public boolean processMessage(Message message) {
674            if (super.processMessage(message)) return true;
675
676            logMessage(this, message.what);
677            switch (message.what) {
678                case CMD_TETHER_REQUESTED:
679                    mLog.e("CMD_TETHER_REQUESTED while already tethering.");
680                    break;
681                case CMD_TETHER_CONNECTION_CHANGED:
682                    final InterfaceSet newUpstreamIfaceSet = (InterfaceSet) message.obj;
683                    if (noChangeInUpstreamIfaceSet(newUpstreamIfaceSet)) {
684                        if (VDBG) Log.d(TAG, "Connection changed noop - dropping");
685                        break;
686                    }
687
688                    if (newUpstreamIfaceSet == null) {
689                        cleanupUpstream();
690                        break;
691                    }
692
693                    for (String removed : upstreamInterfacesRemoved(newUpstreamIfaceSet)) {
694                        cleanupUpstreamInterface(removed);
695                    }
696
697                    final Set<String> added = upstreamInterfacesAdd(newUpstreamIfaceSet);
698                    // This makes the call to cleanupUpstream() in the error
699                    // path for any interface neatly cleanup all the interfaces.
700                    mUpstreamIfaceSet = newUpstreamIfaceSet;
701
702                    for (String ifname : added) {
703                        try {
704                            mNMService.enableNat(mIfaceName, ifname);
705                            mNMService.startInterfaceForwarding(mIfaceName, ifname);
706                        } catch (Exception e) {
707                            mLog.e("Exception enabling NAT: " + e);
708                            cleanupUpstream();
709                            mLastError = ConnectivityManager.TETHER_ERROR_ENABLE_NAT_ERROR;
710                            transitionTo(mInitialState);
711                            return true;
712                        }
713                    }
714                    break;
715                default:
716                    return false;
717            }
718            return true;
719        }
720
721        private boolean noChangeInUpstreamIfaceSet(InterfaceSet newIfaces) {
722            if (mUpstreamIfaceSet == null && newIfaces == null) return true;
723            if (mUpstreamIfaceSet != null && newIfaces != null) {
724                return mUpstreamIfaceSet.equals(newIfaces);
725            }
726            return false;
727        }
728
729        private Set<String> upstreamInterfacesRemoved(InterfaceSet newIfaces) {
730            if (mUpstreamIfaceSet == null) return new HashSet<>();
731
732            final HashSet<String> removed = new HashSet<>(mUpstreamIfaceSet.ifnames);
733            removed.removeAll(newIfaces.ifnames);
734            return removed;
735        }
736
737        private Set<String> upstreamInterfacesAdd(InterfaceSet newIfaces) {
738            final HashSet<String> added = new HashSet<>(newIfaces.ifnames);
739            if (mUpstreamIfaceSet != null) added.removeAll(mUpstreamIfaceSet.ifnames);
740            return added;
741        }
742    }
743
744    /**
745     * This state is terminal for the per interface state machine.  At this
746     * point, the master state machine should have removed this interface
747     * specific state machine from its list of possible recipients of
748     * tethering requests.  The state machine itself will hang around until
749     * the garbage collector finds it.
750     */
751    class UnavailableState extends State {
752        @Override
753        public void enter() {
754            mLastError = ConnectivityManager.TETHER_ERROR_NO_ERROR;
755            sendInterfaceState(IControlsTethering.STATE_UNAVAILABLE);
756        }
757    }
758
759    // Accumulate routes representing "prefixes to be assigned to the local
760    // interface", for subsequent modification of local_network routing.
761    private static ArrayList<RouteInfo> getLocalRoutesFor(
762            String ifname, HashSet<IpPrefix> prefixes) {
763        final ArrayList<RouteInfo> localRoutes = new ArrayList<RouteInfo>();
764        for (IpPrefix ipp : prefixes) {
765            localRoutes.add(new RouteInfo(ipp, null, ifname));
766        }
767        return localRoutes;
768    }
769
770    // Given a prefix like 2001:db8::/64 return an address like 2001:db8::1.
771    private static Inet6Address getLocalDnsIpFor(IpPrefix localPrefix) {
772        final byte[] dnsBytes = localPrefix.getRawAddress();
773        dnsBytes[dnsBytes.length - 1] = getRandomSanitizedByte(DOUG_ADAMS, asByte(0), asByte(1));
774        try {
775            return Inet6Address.getByAddress(null, dnsBytes, 0);
776        } catch (UnknownHostException e) {
777            Slog.wtf(TAG, "Failed to construct Inet6Address from: " + localPrefix);
778            return null;
779        }
780    }
781
782    private static byte getRandomSanitizedByte(byte dflt, byte... excluded) {
783        final byte random = (byte) (new Random()).nextInt();
784        for (int value : excluded) {
785            if (random == value) return dflt;
786        }
787        return random;
788    }
789}
790