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