• Home
  • History
  • Annotate
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2016 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.server.connectivity.tethering;
18 
19 import android.net.ConnectivityManager;
20 import android.net.INetworkStatsService;
21 import android.net.InterfaceConfiguration;
22 import android.net.LinkAddress;
23 import android.net.LinkProperties;
24 import android.net.NetworkUtils;
25 import android.net.util.SharedLog;
26 import android.os.INetworkManagementService;
27 import android.os.Looper;
28 import android.os.Message;
29 import android.util.Log;
30 import android.util.SparseArray;
31 
32 import com.android.internal.util.MessageUtils;
33 import com.android.internal.util.Protocol;
34 import com.android.internal.util.State;
35 import com.android.internal.util.StateMachine;
36 
37 import java.net.InetAddress;
38 
39 /**
40  * @hide
41  *
42  * Tracks the eligibility of a given network interface for tethering.
43  */
44 public class TetherInterfaceStateMachine extends StateMachine {
45     private static final String USB_NEAR_IFACE_ADDR = "192.168.42.129";
46     private static final int USB_PREFIX_LENGTH = 24;
47     private static final String WIFI_HOST_IFACE_ADDR = "192.168.43.1";
48     private static final int WIFI_HOST_IFACE_PREFIX_LENGTH = 24;
49 
50     private final static String TAG = "TetherInterfaceSM";
51     private final static boolean DBG = false;
52     private final static boolean VDBG = false;
53     private static final Class[] messageClasses = {
54             TetherInterfaceStateMachine.class
55     };
56     private static final SparseArray<String> sMagicDecoderRing =
57             MessageUtils.findMessageNames(messageClasses);
58 
59     private static final int BASE_IFACE              = Protocol.BASE_TETHERING + 100;
60     // request from the user that it wants to tether
61     public static final int CMD_TETHER_REQUESTED            = BASE_IFACE + 2;
62     // request from the user that it wants to untether
63     public static final int CMD_TETHER_UNREQUESTED          = BASE_IFACE + 3;
64     // notification that this interface is down
65     public static final int CMD_INTERFACE_DOWN              = BASE_IFACE + 4;
66     // notification from the master SM that it had trouble enabling IP Forwarding
67     public static final int CMD_IP_FORWARDING_ENABLE_ERROR  = BASE_IFACE + 7;
68     // notification from the master SM that it had trouble disabling IP Forwarding
69     public static final int CMD_IP_FORWARDING_DISABLE_ERROR = BASE_IFACE + 8;
70     // notification from the master SM that it had trouble starting tethering
71     public static final int CMD_START_TETHERING_ERROR       = BASE_IFACE + 9;
72     // notification from the master SM that it had trouble stopping tethering
73     public static final int CMD_STOP_TETHERING_ERROR        = BASE_IFACE + 10;
74     // notification from the master SM that it had trouble setting the DNS forwarders
75     public static final int CMD_SET_DNS_FORWARDERS_ERROR    = BASE_IFACE + 11;
76     // the upstream connection has changed
77     public static final int CMD_TETHER_CONNECTION_CHANGED   = BASE_IFACE + 12;
78     // new IPv6 tethering parameters need to be processed
79     public static final int CMD_IPV6_TETHER_UPDATE          = BASE_IFACE + 13;
80 
81     private final State mInitialState;
82     private final State mLocalHotspotState;
83     private final State mTetheredState;
84     private final State mUnavailableState;
85 
86     private final SharedLog mLog;
87     private final INetworkManagementService mNMService;
88     private final INetworkStatsService mStatsService;
89     private final IControlsTethering mTetherController;
90 
91     private final String mIfaceName;
92     private final int mInterfaceType;
93     private final IPv6TetheringInterfaceServices mIPv6TetherSvc;
94 
95     private int mLastError;
96     private String mMyUpstreamIfaceName;  // may change over time
97 
TetherInterfaceStateMachine( String ifaceName, Looper looper, int interfaceType, SharedLog log, INetworkManagementService nMService, INetworkStatsService statsService, IControlsTethering tetherController, IPv6TetheringInterfaceServices ipv6Svc)98     public TetherInterfaceStateMachine(
99             String ifaceName, Looper looper, int interfaceType, SharedLog log,
100             INetworkManagementService nMService, INetworkStatsService statsService,
101             IControlsTethering tetherController, IPv6TetheringInterfaceServices ipv6Svc) {
102         super(ifaceName, looper);
103         mLog = log.forSubComponent(ifaceName);
104         mNMService = nMService;
105         mStatsService = statsService;
106         mTetherController = tetherController;
107         mIfaceName = ifaceName;
108         mInterfaceType = interfaceType;
109         mIPv6TetherSvc = ipv6Svc;
110         mLastError = ConnectivityManager.TETHER_ERROR_NO_ERROR;
111 
112         mInitialState = new InitialState();
113         mLocalHotspotState = new LocalHotspotState();
114         mTetheredState = new TetheredState();
115         mUnavailableState = new UnavailableState();
116         addState(mInitialState);
117         addState(mLocalHotspotState);
118         addState(mTetheredState);
119         addState(mUnavailableState);
120 
121         setInitialState(mInitialState);
122     }
123 
interfaceName()124     public String interfaceName() { return mIfaceName; }
125 
interfaceType()126     public int interfaceType() { return mInterfaceType; }
127 
lastError()128     public int lastError() { return mLastError; }
129 
stop()130     public void stop() { sendMessage(CMD_INTERFACE_DOWN); }
131 
unwanted()132     public void unwanted() { sendMessage(CMD_TETHER_UNREQUESTED); }
133 
134     // configured when we start tethering and unconfig'd on error or conclusion
configureIfaceIp(boolean enabled)135     private boolean configureIfaceIp(boolean enabled) {
136         if (VDBG) Log.d(TAG, "configureIfaceIp(" + enabled + ")");
137 
138         String ipAsString = null;
139         int prefixLen = 0;
140         if (mInterfaceType == ConnectivityManager.TETHERING_USB) {
141             ipAsString = USB_NEAR_IFACE_ADDR;
142             prefixLen = USB_PREFIX_LENGTH;
143         } else if (mInterfaceType == ConnectivityManager.TETHERING_WIFI) {
144             ipAsString = WIFI_HOST_IFACE_ADDR;
145             prefixLen = WIFI_HOST_IFACE_PREFIX_LENGTH;
146         } else {
147             // Nothing to do, BT does this elsewhere.
148             return true;
149         }
150 
151         InterfaceConfiguration ifcg = null;
152         try {
153             ifcg = mNMService.getInterfaceConfig(mIfaceName);
154             if (ifcg != null) {
155                 InetAddress addr = NetworkUtils.numericToInetAddress(ipAsString);
156                 ifcg.setLinkAddress(new LinkAddress(addr, prefixLen));
157                 if (mInterfaceType == ConnectivityManager.TETHERING_WIFI) {
158                     // The WiFi stack has ownership of the interface up/down state.
159                     // It is unclear whether the bluetooth or USB stacks will manage their own
160                     // state.
161                     ifcg.ignoreInterfaceUpDownStatus();
162                 } else {
163                     if (enabled) {
164                         ifcg.setInterfaceUp();
165                     } else {
166                         ifcg.setInterfaceDown();
167                     }
168                 }
169                 ifcg.clearFlag("running");
170                 mNMService.setInterfaceConfig(mIfaceName, ifcg);
171             }
172         } catch (Exception e) {
173             mLog.e("Error configuring interface " + e);
174             return false;
175         }
176 
177         return true;
178     }
179 
maybeLogMessage(State state, int what)180     private void maybeLogMessage(State state, int what) {
181         if (DBG) {
182             Log.d(TAG, state.getName() + " got " +
183                     sMagicDecoderRing.get(what, Integer.toString(what)) + ", Iface = " +
184                     mIfaceName);
185         }
186     }
187 
sendInterfaceState(int newInterfaceState)188     private void sendInterfaceState(int newInterfaceState) {
189         mTetherController.notifyInterfaceStateChange(
190                 mIfaceName, TetherInterfaceStateMachine.this, newInterfaceState, mLastError);
191     }
192 
193     class InitialState extends State {
194         @Override
enter()195         public void enter() {
196             sendInterfaceState(IControlsTethering.STATE_AVAILABLE);
197         }
198 
199         @Override
processMessage(Message message)200         public boolean processMessage(Message message) {
201             maybeLogMessage(this, message.what);
202             boolean retValue = true;
203             switch (message.what) {
204                 case CMD_TETHER_REQUESTED:
205                     mLastError = ConnectivityManager.TETHER_ERROR_NO_ERROR;
206                     switch (message.arg1) {
207                         case IControlsTethering.STATE_LOCAL_ONLY:
208                             transitionTo(mLocalHotspotState);
209                             break;
210                         case IControlsTethering.STATE_TETHERED:
211                             transitionTo(mTetheredState);
212                             break;
213                         default:
214                             mLog.e("Invalid tethering interface serving state specified.");
215                     }
216                     break;
217                 case CMD_INTERFACE_DOWN:
218                     transitionTo(mUnavailableState);
219                     break;
220                 case CMD_IPV6_TETHER_UPDATE:
221                     mIPv6TetherSvc.updateUpstreamIPv6LinkProperties(
222                             (LinkProperties) message.obj);
223                     break;
224                 default:
225                     retValue = false;
226                     break;
227             }
228             return retValue;
229         }
230     }
231 
232     class BaseServingState extends State {
233         @Override
enter()234         public void enter() {
235             if (!configureIfaceIp(true)) {
236                 mLastError = ConnectivityManager.TETHER_ERROR_IFACE_CFG_ERROR;
237                 return;
238             }
239 
240             try {
241                 mNMService.tetherInterface(mIfaceName);
242             } catch (Exception e) {
243                 mLog.e("Error Tethering: " + e);
244                 mLastError = ConnectivityManager.TETHER_ERROR_TETHER_IFACE_ERROR;
245                 return;
246             }
247 
248             if (!mIPv6TetherSvc.start()) {
249                 mLog.e("Failed to start IPv6TetheringInterfaceServices");
250                 // TODO: Make this a fatal error once Bluetooth IPv6 is sorted.
251                 return;
252             }
253         }
254 
255         @Override
exit()256         public void exit() {
257             // Note that at this point, we're leaving the tethered state.  We can fail any
258             // of these operations, but it doesn't really change that we have to try them
259             // all in sequence.
260             mIPv6TetherSvc.stop();
261 
262             try {
263                 mNMService.untetherInterface(mIfaceName);
264             } catch (Exception e) {
265                 mLastError = ConnectivityManager.TETHER_ERROR_UNTETHER_IFACE_ERROR;
266                 mLog.e("Failed to untether interface: " + e);
267             }
268 
269             configureIfaceIp(false);
270         }
271 
272         @Override
processMessage(Message message)273         public boolean processMessage(Message message) {
274             maybeLogMessage(this, message.what);
275             switch (message.what) {
276                 case CMD_TETHER_UNREQUESTED:
277                     transitionTo(mInitialState);
278                     if (DBG) Log.d(TAG, "Untethered (unrequested)" + mIfaceName);
279                     break;
280                 case CMD_INTERFACE_DOWN:
281                     transitionTo(mUnavailableState);
282                     if (DBG) Log.d(TAG, "Untethered (ifdown)" + mIfaceName);
283                     break;
284                 case CMD_IPV6_TETHER_UPDATE:
285                     mIPv6TetherSvc.updateUpstreamIPv6LinkProperties(
286                             (LinkProperties) message.obj);
287                     break;
288                 case CMD_IP_FORWARDING_ENABLE_ERROR:
289                 case CMD_IP_FORWARDING_DISABLE_ERROR:
290                 case CMD_START_TETHERING_ERROR:
291                 case CMD_STOP_TETHERING_ERROR:
292                 case CMD_SET_DNS_FORWARDERS_ERROR:
293                     mLastError = ConnectivityManager.TETHER_ERROR_MASTER_ERROR;
294                     transitionTo(mInitialState);
295                     break;
296                 default:
297                     return false;
298             }
299             return true;
300         }
301     }
302 
303     // Handling errors in BaseServingState.enter() by transitioning is
304     // problematic because transitioning during a multi-state jump yields
305     // a Log.wtf(). Ultimately, there should be only one ServingState,
306     // and forwarding and NAT rules should be handled by a coordinating
307     // functional element outside of TetherInterfaceStateMachine.
308     class LocalHotspotState extends BaseServingState {
309         @Override
enter()310         public void enter() {
311             super.enter();
312             if (mLastError != ConnectivityManager.TETHER_ERROR_NO_ERROR) {
313                 transitionTo(mInitialState);
314             }
315 
316             if (DBG) Log.d(TAG, "Local hotspot " + mIfaceName);
317             sendInterfaceState(IControlsTethering.STATE_LOCAL_ONLY);
318         }
319 
320         @Override
processMessage(Message message)321         public boolean processMessage(Message message) {
322             if (super.processMessage(message)) return true;
323 
324             maybeLogMessage(this, message.what);
325             switch (message.what) {
326                 case CMD_TETHER_REQUESTED:
327                     mLog.e("CMD_TETHER_REQUESTED while in local-only hotspot mode.");
328                     break;
329                 case CMD_TETHER_CONNECTION_CHANGED:
330                     // Ignored in local hotspot state.
331                     break;
332                 default:
333                     return false;
334             }
335             return true;
336         }
337     }
338 
339     // Handling errors in BaseServingState.enter() by transitioning is
340     // problematic because transitioning during a multi-state jump yields
341     // a Log.wtf(). Ultimately, there should be only one ServingState,
342     // and forwarding and NAT rules should be handled by a coordinating
343     // functional element outside of TetherInterfaceStateMachine.
344     class TetheredState extends BaseServingState {
345         @Override
enter()346         public void enter() {
347             super.enter();
348             if (mLastError != ConnectivityManager.TETHER_ERROR_NO_ERROR) {
349                 transitionTo(mInitialState);
350             }
351 
352             if (DBG) Log.d(TAG, "Tethered " + mIfaceName);
353             sendInterfaceState(IControlsTethering.STATE_TETHERED);
354         }
355 
356         @Override
exit()357         public void exit() {
358             cleanupUpstream();
359             super.exit();
360         }
361 
cleanupUpstream()362         private void cleanupUpstream() {
363             if (mMyUpstreamIfaceName == null) return;
364 
365             cleanupUpstreamInterface(mMyUpstreamIfaceName);
366             mMyUpstreamIfaceName = null;
367         }
368 
cleanupUpstreamInterface(String upstreamIface)369         private void cleanupUpstreamInterface(String upstreamIface) {
370             // Note that we don't care about errors here.
371             // Sometimes interfaces are gone before we get
372             // to remove their rules, which generates errors.
373             // Just do the best we can.
374             try {
375                 // About to tear down NAT; gather remaining statistics.
376                 mStatsService.forceUpdate();
377             } catch (Exception e) {
378                 if (VDBG) Log.e(TAG, "Exception in forceUpdate: " + e.toString());
379             }
380             try {
381                 mNMService.stopInterfaceForwarding(mIfaceName, upstreamIface);
382             } catch (Exception e) {
383                 if (VDBG) Log.e(TAG, "Exception in removeInterfaceForward: " + e.toString());
384             }
385             try {
386                 mNMService.disableNat(mIfaceName, upstreamIface);
387             } catch (Exception e) {
388                 if (VDBG) Log.e(TAG, "Exception in disableNat: " + e.toString());
389             }
390         }
391 
392         @Override
processMessage(Message message)393         public boolean processMessage(Message message) {
394             if (super.processMessage(message)) return true;
395 
396             maybeLogMessage(this, message.what);
397             boolean retValue = true;
398             switch (message.what) {
399                 case CMD_TETHER_REQUESTED:
400                     mLog.e("CMD_TETHER_REQUESTED while already tethering.");
401                     break;
402                 case CMD_TETHER_CONNECTION_CHANGED:
403                     String newUpstreamIfaceName = (String)(message.obj);
404                     if ((mMyUpstreamIfaceName == null && newUpstreamIfaceName == null) ||
405                             (mMyUpstreamIfaceName != null &&
406                             mMyUpstreamIfaceName.equals(newUpstreamIfaceName))) {
407                         if (VDBG) Log.d(TAG, "Connection changed noop - dropping");
408                         break;
409                     }
410                     cleanupUpstream();
411                     if (newUpstreamIfaceName != null) {
412                         try {
413                             mNMService.enableNat(mIfaceName, newUpstreamIfaceName);
414                             mNMService.startInterfaceForwarding(mIfaceName,
415                                     newUpstreamIfaceName);
416                         } catch (Exception e) {
417                             mLog.e("Exception enabling NAT: " + e);
418                             cleanupUpstreamInterface(newUpstreamIfaceName);
419                             mLastError = ConnectivityManager.TETHER_ERROR_ENABLE_NAT_ERROR;
420                             transitionTo(mInitialState);
421                             return true;
422                         }
423                     }
424                     mMyUpstreamIfaceName = newUpstreamIfaceName;
425                     break;
426                 default:
427                     retValue = false;
428                     break;
429             }
430             return retValue;
431         }
432     }
433 
434     /**
435      * This state is terminal for the per interface state machine.  At this
436      * point, the master state machine should have removed this interface
437      * specific state machine from its list of possible recipients of
438      * tethering requests.  The state machine itself will hang around until
439      * the garbage collector finds it.
440      */
441     class UnavailableState extends State {
442         @Override
enter()443         public void enter() {
444             mLastError = ConnectivityManager.TETHER_ERROR_NO_ERROR;
445             sendInterfaceState(IControlsTethering.STATE_UNAVAILABLE);
446         }
447     }
448 }
449