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