Home | History | Annotate | Download | only in tethering
      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.os.INetworkManagementService;
     26 import android.os.Looper;
     27 import android.os.Message;
     28 import android.util.Log;
     29 import android.util.SparseArray;
     30 
     31 import com.android.internal.util.MessageUtils;
     32 import com.android.internal.util.Protocol;
     33 import com.android.internal.util.State;
     34 import com.android.internal.util.StateMachine;
     35 
     36 import java.net.InetAddress;
     37 
     38 /**
     39  * @hide
     40  *
     41  * Tracks the eligibility of a given network interface for tethering.
     42  */
     43 public class TetherInterfaceStateMachine extends StateMachine {
     44     private static final String USB_NEAR_IFACE_ADDR = "192.168.42.129";
     45     private static final int USB_PREFIX_LENGTH = 24;
     46     private static final String WIFI_HOST_IFACE_ADDR = "192.168.43.1";
     47     private static final int WIFI_HOST_IFACE_PREFIX_LENGTH = 24;
     48 
     49     private final static String TAG = "TetherInterfaceSM";
     50     private final static boolean DBG = false;
     51     private final static boolean VDBG = false;
     52     private static final Class[] messageClasses = {
     53             TetherInterfaceStateMachine.class
     54     };
     55     private static final SparseArray<String> sMagicDecoderRing =
     56             MessageUtils.findMessageNames(messageClasses);
     57 
     58     private static final int BASE_IFACE              = Protocol.BASE_TETHERING + 100;
     59     // request from the user that it wants to tether
     60     public static final int CMD_TETHER_REQUESTED            = BASE_IFACE + 2;
     61     // request from the user that it wants to untether
     62     public static final int CMD_TETHER_UNREQUESTED          = BASE_IFACE + 3;
     63     // notification that this interface is down
     64     public static final int CMD_INTERFACE_DOWN              = BASE_IFACE + 4;
     65     // notification from the master SM that it had trouble enabling IP Forwarding
     66     public static final int CMD_IP_FORWARDING_ENABLE_ERROR  = BASE_IFACE + 7;
     67     // notification from the master SM that it had trouble disabling IP Forwarding
     68     public static final int CMD_IP_FORWARDING_DISABLE_ERROR = BASE_IFACE + 8;
     69     // notification from the master SM that it had trouble starting tethering
     70     public static final int CMD_START_TETHERING_ERROR       = BASE_IFACE + 9;
     71     // notification from the master SM that it had trouble stopping tethering
     72     public static final int CMD_STOP_TETHERING_ERROR        = BASE_IFACE + 10;
     73     // notification from the master SM that it had trouble setting the DNS forwarders
     74     public static final int CMD_SET_DNS_FORWARDERS_ERROR    = BASE_IFACE + 11;
     75     // the upstream connection has changed
     76     public static final int CMD_TETHER_CONNECTION_CHANGED   = BASE_IFACE + 12;
     77     // new IPv6 tethering parameters need to be processed
     78     public static final int CMD_IPV6_TETHER_UPDATE          = BASE_IFACE + 13;
     79 
     80     private final State mInitialState;
     81     private final State mTetheredState;
     82     private final State mUnavailableState;
     83 
     84     private final INetworkManagementService mNMService;
     85     private final INetworkStatsService mStatsService;
     86     private final IControlsTethering mTetherController;
     87 
     88     private final String mIfaceName;
     89     private final int mInterfaceType;
     90     private final IPv6TetheringInterfaceServices mIPv6TetherSvc;
     91 
     92     private int mLastError;
     93     private String mMyUpstreamIfaceName;  // may change over time
     94 
     95     public TetherInterfaceStateMachine(String ifaceName, Looper looper, int interfaceType,
     96                     INetworkManagementService nMService, INetworkStatsService statsService,
     97                     IControlsTethering tetherController) {
     98         super(ifaceName, looper);
     99         mNMService = nMService;
    100         mStatsService = statsService;
    101         mTetherController = tetherController;
    102         mIfaceName = ifaceName;
    103         mInterfaceType = interfaceType;
    104         mIPv6TetherSvc = new IPv6TetheringInterfaceServices(mIfaceName, mNMService);
    105         mLastError = ConnectivityManager.TETHER_ERROR_NO_ERROR;
    106 
    107         mInitialState = new InitialState();
    108         addState(mInitialState);
    109         mTetheredState = new TetheredState();
    110         addState(mTetheredState);
    111         mUnavailableState = new UnavailableState();
    112         addState(mUnavailableState);
    113 
    114         setInitialState(mInitialState);
    115     }
    116 
    117     public int interfaceType() {
    118         return mInterfaceType;
    119     }
    120 
    121     // configured when we start tethering and unconfig'd on error or conclusion
    122     private boolean configureIfaceIp(boolean enabled) {
    123         if (VDBG) Log.d(TAG, "configureIfaceIp(" + enabled + ")");
    124 
    125         String ipAsString = null;
    126         int prefixLen = 0;
    127         if (mInterfaceType == ConnectivityManager.TETHERING_USB) {
    128             ipAsString = USB_NEAR_IFACE_ADDR;
    129             prefixLen = USB_PREFIX_LENGTH;
    130         } else if (mInterfaceType == ConnectivityManager.TETHERING_WIFI) {
    131             ipAsString = WIFI_HOST_IFACE_ADDR;
    132             prefixLen = WIFI_HOST_IFACE_PREFIX_LENGTH;
    133         } else {
    134             // Nothing to do, BT does this elsewhere.
    135             return true;
    136         }
    137 
    138         InterfaceConfiguration ifcg = null;
    139         try {
    140             ifcg = mNMService.getInterfaceConfig(mIfaceName);
    141             if (ifcg != null) {
    142                 InetAddress addr = NetworkUtils.numericToInetAddress(ipAsString);
    143                 ifcg.setLinkAddress(new LinkAddress(addr, prefixLen));
    144                 if (enabled) {
    145                     ifcg.setInterfaceUp();
    146                 } else {
    147                     ifcg.setInterfaceDown();
    148                 }
    149                 ifcg.clearFlag("running");
    150                 mNMService.setInterfaceConfig(mIfaceName, ifcg);
    151             }
    152         } catch (Exception e) {
    153             Log.e(TAG, "Error configuring interface " + mIfaceName, e);
    154             return false;
    155         }
    156 
    157         return true;
    158     }
    159 
    160     private void maybeLogMessage(State state, int what) {
    161         if (DBG) {
    162             Log.d(TAG, state.getName() + " got " +
    163                     sMagicDecoderRing.get(what, Integer.toString(what)));
    164         }
    165     }
    166 
    167     class InitialState extends State {
    168         @Override
    169         public void enter() {
    170             mTetherController.notifyInterfaceStateChange(
    171                     mIfaceName, TetherInterfaceStateMachine.this,
    172                     IControlsTethering.STATE_AVAILABLE, mLastError);
    173         }
    174 
    175         @Override
    176         public boolean processMessage(Message message) {
    177             maybeLogMessage(this, message.what);
    178             boolean retValue = true;
    179             switch (message.what) {
    180                 case CMD_TETHER_REQUESTED:
    181                     mLastError = ConnectivityManager.TETHER_ERROR_NO_ERROR;
    182                     transitionTo(mTetheredState);
    183                     break;
    184                 case CMD_INTERFACE_DOWN:
    185                     transitionTo(mUnavailableState);
    186                     break;
    187                 case CMD_IPV6_TETHER_UPDATE:
    188                     mIPv6TetherSvc.updateUpstreamIPv6LinkProperties(
    189                             (LinkProperties) message.obj);
    190                     break;
    191                 default:
    192                     retValue = false;
    193                     break;
    194             }
    195             return retValue;
    196         }
    197     }
    198 
    199     class TetheredState extends State {
    200         @Override
    201         public void enter() {
    202             if (!configureIfaceIp(true)) {
    203                 mLastError = ConnectivityManager.TETHER_ERROR_IFACE_CFG_ERROR;
    204                 transitionTo(mInitialState);
    205                 return;
    206             }
    207 
    208             try {
    209                 mNMService.tetherInterface(mIfaceName);
    210             } catch (Exception e) {
    211                 Log.e(TAG, "Error Tethering: " + e.toString());
    212                 mLastError = ConnectivityManager.TETHER_ERROR_TETHER_IFACE_ERROR;
    213                 transitionTo(mInitialState);
    214                 return;
    215             }
    216 
    217             if (!mIPv6TetherSvc.start()) {
    218                 Log.e(TAG, "Failed to start IPv6TetheringInterfaceServices");
    219             }
    220 
    221             if (DBG) Log.d(TAG, "Tethered " + mIfaceName);
    222             mTetherController.notifyInterfaceStateChange(
    223                     mIfaceName, TetherInterfaceStateMachine.this,
    224                     IControlsTethering.STATE_TETHERED, mLastError);
    225         }
    226 
    227         @Override
    228         public void exit() {
    229             // Note that at this point, we're leaving the tethered state.  We can fail any
    230             // of these operations, but it doesn't really change that we have to try them
    231             // all in sequence.
    232             mIPv6TetherSvc.stop();
    233             cleanupUpstream();
    234 
    235             try {
    236                 mNMService.untetherInterface(mIfaceName);
    237             } catch (Exception ee) {
    238                 mLastError = ConnectivityManager.TETHER_ERROR_UNTETHER_IFACE_ERROR;
    239                 Log.e(TAG, "Failed to untether interface: " + ee.toString());
    240             }
    241 
    242             configureIfaceIp(false);
    243         }
    244 
    245         private void cleanupUpstream() {
    246             if (mMyUpstreamIfaceName != null) {
    247                 // note that we don't care about errors here.
    248                 // sometimes interfaces are gone before we get
    249                 // to remove their rules, which generates errors.
    250                 // just do the best we can.
    251                 try {
    252                     // about to tear down NAT; gather remaining statistics
    253                     mStatsService.forceUpdate();
    254                 } catch (Exception e) {
    255                     if (VDBG) Log.e(TAG, "Exception in forceUpdate: " + e.toString());
    256                 }
    257                 try {
    258                     mNMService.stopInterfaceForwarding(mIfaceName, mMyUpstreamIfaceName);
    259                 } catch (Exception e) {
    260                     if (VDBG) Log.e(
    261                             TAG, "Exception in removeInterfaceForward: " + e.toString());
    262                 }
    263                 try {
    264                     mNMService.disableNat(mIfaceName, mMyUpstreamIfaceName);
    265                 } catch (Exception e) {
    266                     if (VDBG) Log.e(TAG, "Exception in disableNat: " + e.toString());
    267                 }
    268                 mMyUpstreamIfaceName = null;
    269             }
    270             return;
    271         }
    272 
    273         @Override
    274         public boolean processMessage(Message message) {
    275             maybeLogMessage(this, message.what);
    276             boolean retValue = true;
    277             switch (message.what) {
    278                 case CMD_TETHER_UNREQUESTED:
    279                     transitionTo(mInitialState);
    280                     if (DBG) Log.d(TAG, "Untethered (unrequested)" + mIfaceName);
    281                     break;
    282                 case CMD_INTERFACE_DOWN:
    283                     transitionTo(mUnavailableState);
    284                     if (DBG) Log.d(TAG, "Untethered (ifdown)" + mIfaceName);
    285                     break;
    286                 case CMD_TETHER_CONNECTION_CHANGED:
    287                     String newUpstreamIfaceName = (String)(message.obj);
    288                     if ((mMyUpstreamIfaceName == null && newUpstreamIfaceName == null) ||
    289                             (mMyUpstreamIfaceName != null &&
    290                             mMyUpstreamIfaceName.equals(newUpstreamIfaceName))) {
    291                         if (VDBG) Log.d(TAG, "Connection changed noop - dropping");
    292                         break;
    293                     }
    294                     cleanupUpstream();
    295                     if (newUpstreamIfaceName != null) {
    296                         try {
    297                             mNMService.enableNat(mIfaceName, newUpstreamIfaceName);
    298                             mNMService.startInterfaceForwarding(mIfaceName,
    299                                     newUpstreamIfaceName);
    300                         } catch (Exception e) {
    301                             Log.e(TAG, "Exception enabling Nat: " + e.toString());
    302                             mLastError = ConnectivityManager.TETHER_ERROR_ENABLE_NAT_ERROR;
    303                             transitionTo(mInitialState);
    304                             return true;
    305                         }
    306                     }
    307                     mMyUpstreamIfaceName = newUpstreamIfaceName;
    308                     break;
    309                 case CMD_IPV6_TETHER_UPDATE:
    310                     mIPv6TetherSvc.updateUpstreamIPv6LinkProperties(
    311                             (LinkProperties) message.obj);
    312                     break;
    313                 case CMD_IP_FORWARDING_ENABLE_ERROR:
    314                 case CMD_IP_FORWARDING_DISABLE_ERROR:
    315                 case CMD_START_TETHERING_ERROR:
    316                 case CMD_STOP_TETHERING_ERROR:
    317                 case CMD_SET_DNS_FORWARDERS_ERROR:
    318                     mLastError = ConnectivityManager.TETHER_ERROR_MASTER_ERROR;
    319                     transitionTo(mInitialState);
    320                     break;
    321                 default:
    322                     retValue = false;
    323                     break;
    324             }
    325             return retValue;
    326         }
    327     }
    328 
    329     /**
    330      * This state is terminal for the per interface state machine.  At this
    331      * point, the master state machine should have removed this interface
    332      * specific state machine from its list of possible recipients of
    333      * tethering requests.  The state machine itself will hang around until
    334      * the garbage collector finds it.
    335      */
    336     class UnavailableState extends State {
    337         @Override
    338         public void enter() {
    339             mLastError = ConnectivityManager.TETHER_ERROR_NO_ERROR;
    340             mTetherController.notifyInterfaceStateChange(
    341                     mIfaceName, TetherInterfaceStateMachine.this,
    342                     IControlsTethering.STATE_UNAVAILABLE, mLastError);
    343         }
    344     }
    345 }
    346