1 /* 2 * Copyright (C) 2012 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; 18 19 import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR; 20 21 import static com.android.net.module.util.CollectionUtils.contains; 22 23 import android.annotation.NonNull; 24 import android.net.ConnectivityManager; 25 import android.net.IDnsResolver; 26 import android.net.INetd; 27 import android.net.InetAddresses; 28 import android.net.InterfaceConfigurationParcel; 29 import android.net.IpPrefix; 30 import android.net.LinkAddress; 31 import android.net.LinkProperties; 32 import android.net.NetworkInfo; 33 import android.net.RouteInfo; 34 import android.os.RemoteException; 35 import android.os.ServiceSpecificException; 36 import android.util.Log; 37 38 import com.android.internal.annotations.VisibleForTesting; 39 import com.android.net.module.util.NetworkStackConstants; 40 import com.android.server.ConnectivityService; 41 42 import java.net.Inet6Address; 43 import java.util.Objects; 44 45 /** 46 * Class to manage a 464xlat CLAT daemon. Nat464Xlat is not thread safe and should be manipulated 47 * from a consistent and unique thread context. It is the responsibility of ConnectivityService to 48 * call into this class from its own Handler thread. 49 * 50 * @hide 51 */ 52 public class Nat464Xlat { 53 private static final String TAG = Nat464Xlat.class.getSimpleName(); 54 55 // This must match the interface prefix in clatd.c. 56 private static final String CLAT_PREFIX = "v4-"; 57 58 // The network types on which we will start clatd, 59 // allowing clat only on networks for which we can support IPv6-only. 60 private static final int[] NETWORK_TYPES = { 61 ConnectivityManager.TYPE_MOBILE, 62 ConnectivityManager.TYPE_WIFI, 63 ConnectivityManager.TYPE_ETHERNET, 64 }; 65 66 // The network states in which running clatd is supported. 67 private static final NetworkInfo.State[] NETWORK_STATES = { 68 NetworkInfo.State.CONNECTED, 69 NetworkInfo.State.SUSPENDED, 70 }; 71 72 private final IDnsResolver mDnsResolver; 73 private final INetd mNetd; 74 75 // The network we're running on, and its type. 76 private final NetworkAgentInfo mNetwork; 77 78 private enum State { 79 IDLE, // start() not called. Base iface and stacked iface names are null. 80 DISCOVERING, // same as IDLE, except prefix discovery in progress. 81 STARTING, // start() called. Base iface and stacked iface names are known. 82 RUNNING, // start() called, and the stacked iface is known to be up. 83 } 84 85 /** 86 * NAT64 prefix currently in use. Only valid in STARTING or RUNNING states. 87 * Used, among other things, to avoid updates when switching from a prefix learned from one 88 * source (e.g., RA) to the same prefix learned from another source (e.g., RA). 89 */ 90 private IpPrefix mNat64PrefixInUse; 91 /** NAT64 prefix (if any) discovered from DNS via RFC 7050. */ 92 private IpPrefix mNat64PrefixFromDns; 93 /** NAT64 prefix (if any) learned from the network via RA. */ 94 private IpPrefix mNat64PrefixFromRa; 95 private String mBaseIface; 96 private String mIface; 97 private Inet6Address mIPv6Address; 98 private State mState = State.IDLE; 99 100 private boolean mEnableClatOnCellular; 101 private boolean mPrefixDiscoveryRunning; 102 Nat464Xlat(NetworkAgentInfo nai, INetd netd, IDnsResolver dnsResolver, ConnectivityService.Dependencies deps)103 public Nat464Xlat(NetworkAgentInfo nai, INetd netd, IDnsResolver dnsResolver, 104 ConnectivityService.Dependencies deps) { 105 mDnsResolver = dnsResolver; 106 mNetd = netd; 107 mNetwork = nai; 108 mEnableClatOnCellular = deps.getCellular464XlatEnabled(); 109 } 110 111 /** 112 * Whether to attempt 464xlat on this network. This is true for an IPv6-only network that is 113 * currently connected and where the NetworkAgent has not disabled 464xlat. It is the signal to 114 * enable NAT64 prefix discovery. 115 * 116 * @param nai the NetworkAgentInfo corresponding to the network. 117 * @return true if the network requires clat, false otherwise. 118 */ 119 @VisibleForTesting requiresClat(NetworkAgentInfo nai)120 protected boolean requiresClat(NetworkAgentInfo nai) { 121 // TODO: migrate to NetworkCapabilities.TRANSPORT_*. 122 final boolean supported = contains(NETWORK_TYPES, nai.networkInfo.getType()); 123 final boolean connected = contains(NETWORK_STATES, nai.networkInfo.getState()); 124 125 // Only run clat on networks that have a global IPv6 address and don't have a native IPv4 126 // address. 127 LinkProperties lp = nai.linkProperties; 128 final boolean isIpv6OnlyNetwork = (lp != null) && lp.hasGlobalIpv6Address() 129 && !lp.hasIpv4Address(); 130 131 // If the network tells us it doesn't use clat, respect that. 132 final boolean skip464xlat = (nai.netAgentConfig() != null) 133 && nai.netAgentConfig().skip464xlat; 134 135 return supported && connected && isIpv6OnlyNetwork && !skip464xlat 136 && (nai.networkCapabilities.hasTransport(TRANSPORT_CELLULAR) 137 ? isCellular464XlatEnabled() : true); 138 } 139 140 /** 141 * Whether the clat demon should be started on this network now. This is true if requiresClat is 142 * true and a NAT64 prefix has been discovered. 143 * 144 * @param nai the NetworkAgentInfo corresponding to the network. 145 * @return true if the network should start clat, false otherwise. 146 */ 147 @VisibleForTesting shouldStartClat(NetworkAgentInfo nai)148 protected boolean shouldStartClat(NetworkAgentInfo nai) { 149 LinkProperties lp = nai.linkProperties; 150 return requiresClat(nai) && lp != null && lp.getNat64Prefix() != null; 151 } 152 153 /** 154 * @return true if clatd has been started and has not yet stopped. 155 * A true result corresponds to internal states STARTING and RUNNING. 156 */ isStarted()157 public boolean isStarted() { 158 return (mState == State.STARTING || mState == State.RUNNING); 159 } 160 161 /** 162 * @return true if clatd has been started but the stacked interface is not yet up. 163 */ isStarting()164 public boolean isStarting() { 165 return mState == State.STARTING; 166 } 167 168 /** 169 * @return true if clatd has been started and the stacked interface is up. 170 */ isRunning()171 public boolean isRunning() { 172 return mState == State.RUNNING; 173 } 174 175 /** 176 * Start clatd, register this Nat464Xlat as a network observer for the stacked interface, 177 * and set internal state. 178 */ enterStartingState(String baseIface)179 private void enterStartingState(String baseIface) { 180 mNat64PrefixInUse = selectNat64Prefix(); 181 String addrStr = null; 182 try { 183 addrStr = mNetd.clatdStart(baseIface, mNat64PrefixInUse.toString()); 184 } catch (RemoteException | ServiceSpecificException e) { 185 Log.e(TAG, "Error starting clatd on " + baseIface + ": " + e); 186 } 187 mIface = CLAT_PREFIX + baseIface; 188 mBaseIface = baseIface; 189 mState = State.STARTING; 190 try { 191 mIPv6Address = (Inet6Address) InetAddresses.parseNumericAddress(addrStr); 192 } catch (ClassCastException | IllegalArgumentException | NullPointerException e) { 193 Log.e(TAG, "Invalid IPv6 address " + addrStr); 194 } 195 if (mPrefixDiscoveryRunning && !isPrefixDiscoveryNeeded()) { 196 stopPrefixDiscovery(); 197 } 198 if (!mPrefixDiscoveryRunning) { 199 setPrefix64(mNat64PrefixInUse); 200 } 201 } 202 203 /** 204 * Enter running state just after getting confirmation that the stacked interface is up, and 205 * turn ND offload off if on WiFi. 206 */ enterRunningState()207 private void enterRunningState() { 208 mState = State.RUNNING; 209 } 210 211 /** 212 * Unregister as a base observer for the stacked interface, and clear internal state. 213 */ leaveStartedState()214 private void leaveStartedState() { 215 mNat64PrefixInUse = null; 216 mIface = null; 217 mBaseIface = null; 218 219 if (!mPrefixDiscoveryRunning) { 220 setPrefix64(null); 221 } 222 223 if (isPrefixDiscoveryNeeded()) { 224 if (!mPrefixDiscoveryRunning) { 225 startPrefixDiscovery(); 226 } 227 mState = State.DISCOVERING; 228 } else { 229 stopPrefixDiscovery(); 230 mState = State.IDLE; 231 } 232 } 233 234 @VisibleForTesting start()235 protected void start() { 236 if (isStarted()) { 237 Log.e(TAG, "startClat: already started"); 238 return; 239 } 240 241 String baseIface = mNetwork.linkProperties.getInterfaceName(); 242 if (baseIface == null) { 243 Log.e(TAG, "startClat: Can't start clat on null interface"); 244 return; 245 } 246 // TODO: should we only do this if mNetd.clatdStart() succeeds? 247 Log.i(TAG, "Starting clatd on " + baseIface); 248 enterStartingState(baseIface); 249 } 250 251 @VisibleForTesting stop()252 protected void stop() { 253 if (!isStarted()) { 254 Log.e(TAG, "stopClat: already stopped"); 255 return; 256 } 257 258 Log.i(TAG, "Stopping clatd on " + mBaseIface); 259 try { 260 mNetd.clatdStop(mBaseIface); 261 } catch (RemoteException | ServiceSpecificException e) { 262 Log.e(TAG, "Error stopping clatd on " + mBaseIface + ": " + e); 263 } 264 265 String iface = mIface; 266 boolean wasRunning = isRunning(); 267 268 // Change state before updating LinkProperties. handleUpdateLinkProperties ends up calling 269 // fixupLinkProperties, and if at that time the state is still RUNNING, fixupLinkProperties 270 // would wrongly inform ConnectivityService that there is still a stacked interface. 271 leaveStartedState(); 272 273 if (wasRunning) { 274 LinkProperties lp = new LinkProperties(mNetwork.linkProperties); 275 lp.removeStackedLink(iface); 276 mNetwork.connService().handleUpdateLinkProperties(mNetwork, lp); 277 } 278 } 279 startPrefixDiscovery()280 private void startPrefixDiscovery() { 281 try { 282 mDnsResolver.startPrefix64Discovery(getNetId()); 283 } catch (RemoteException | ServiceSpecificException e) { 284 Log.e(TAG, "Error starting prefix discovery on netId " + getNetId() + ": " + e); 285 } 286 mPrefixDiscoveryRunning = true; 287 } 288 stopPrefixDiscovery()289 private void stopPrefixDiscovery() { 290 try { 291 mDnsResolver.stopPrefix64Discovery(getNetId()); 292 } catch (RemoteException | ServiceSpecificException e) { 293 Log.e(TAG, "Error stopping prefix discovery on netId " + getNetId() + ": " + e); 294 } 295 mPrefixDiscoveryRunning = false; 296 } 297 isPrefixDiscoveryNeeded()298 private boolean isPrefixDiscoveryNeeded() { 299 // If there is no NAT64 prefix in the RA, prefix discovery is always needed. It cannot be 300 // stopped after it succeeds, because stopping it will cause netd to report that the prefix 301 // has been removed, and that will cause us to stop clatd. 302 return requiresClat(mNetwork) && mNat64PrefixFromRa == null; 303 } 304 setPrefix64(IpPrefix prefix)305 private void setPrefix64(IpPrefix prefix) { 306 final String prefixString = (prefix != null) ? prefix.toString() : ""; 307 try { 308 mDnsResolver.setPrefix64(getNetId(), prefixString); 309 } catch (RemoteException | ServiceSpecificException e) { 310 Log.e(TAG, "Error setting NAT64 prefix on netId " + getNetId() + " to " 311 + prefix + ": " + e); 312 } 313 } 314 maybeHandleNat64PrefixChange()315 private void maybeHandleNat64PrefixChange() { 316 final IpPrefix newPrefix = selectNat64Prefix(); 317 if (!Objects.equals(mNat64PrefixInUse, newPrefix)) { 318 Log.d(TAG, "NAT64 prefix changed from " + mNat64PrefixInUse + " to " 319 + newPrefix); 320 stop(); 321 // It's safe to call update here, even though this method is called from update, because 322 // stop() is guaranteed to have moved out of STARTING and RUNNING, which are the only 323 // states in which this method can be called. 324 update(); 325 } 326 } 327 328 /** 329 * Starts/stops NAT64 prefix discovery and clatd as necessary. 330 */ update()331 public void update() { 332 // TODO: turn this class into a proper StateMachine. http://b/126113090 333 switch (mState) { 334 case IDLE: 335 if (isPrefixDiscoveryNeeded()) { 336 startPrefixDiscovery(); // Enters DISCOVERING state. 337 mState = State.DISCOVERING; 338 } else if (requiresClat(mNetwork)) { 339 start(); // Enters STARTING state. 340 } 341 break; 342 343 case DISCOVERING: 344 if (shouldStartClat(mNetwork)) { 345 // NAT64 prefix detected. Start clatd. 346 start(); // Enters STARTING state. 347 return; 348 } 349 if (!requiresClat(mNetwork)) { 350 // IPv4 address added. Go back to IDLE state. 351 stopPrefixDiscovery(); 352 mState = State.IDLE; 353 return; 354 } 355 break; 356 357 case STARTING: 358 case RUNNING: 359 // NAT64 prefix removed, or IPv4 address added. 360 // Stop clatd and go back into DISCOVERING or idle. 361 if (!shouldStartClat(mNetwork)) { 362 stop(); 363 break; 364 } 365 // Only necessary while clat is actually started. 366 maybeHandleNat64PrefixChange(); 367 break; 368 } 369 } 370 371 /** 372 * Picks a NAT64 prefix to use. Always prefers the prefix from the RA if one is received from 373 * both RA and DNS, because the prefix in the RA has better security and updatability, and will 374 * almost always be received first anyway. 375 * 376 * Any network that supports legacy hosts will support discovering the DNS64 prefix via DNS as 377 * well. If the prefix from the RA is withdrawn, fall back to that for reliability purposes. 378 */ selectNat64Prefix()379 private IpPrefix selectNat64Prefix() { 380 return mNat64PrefixFromRa != null ? mNat64PrefixFromRa : mNat64PrefixFromDns; 381 } 382 setNat64PrefixFromRa(IpPrefix prefix)383 public void setNat64PrefixFromRa(IpPrefix prefix) { 384 mNat64PrefixFromRa = prefix; 385 } 386 setNat64PrefixFromDns(IpPrefix prefix)387 public void setNat64PrefixFromDns(IpPrefix prefix) { 388 mNat64PrefixFromDns = prefix; 389 } 390 391 /** 392 * Copies the stacked clat link in oldLp, if any, to the passed LinkProperties. 393 * This is necessary because the LinkProperties in mNetwork come from the transport layer, which 394 * has no idea that 464xlat is running on top of it. 395 */ fixupLinkProperties(@onNull LinkProperties oldLp, @NonNull LinkProperties lp)396 public void fixupLinkProperties(@NonNull LinkProperties oldLp, @NonNull LinkProperties lp) { 397 // This must be done even if clatd is not running, because otherwise shouldStartClat would 398 // never return true. 399 lp.setNat64Prefix(selectNat64Prefix()); 400 401 if (!isRunning()) { 402 return; 403 } 404 if (lp.getAllInterfaceNames().contains(mIface)) { 405 return; 406 } 407 408 Log.d(TAG, "clatd running, updating NAI for " + mIface); 409 for (LinkProperties stacked: oldLp.getStackedLinks()) { 410 if (Objects.equals(mIface, stacked.getInterfaceName())) { 411 lp.addStackedLink(stacked); 412 return; 413 } 414 } 415 } 416 makeLinkProperties(LinkAddress clatAddress)417 private LinkProperties makeLinkProperties(LinkAddress clatAddress) { 418 LinkProperties stacked = new LinkProperties(); 419 stacked.setInterfaceName(mIface); 420 421 // Although the clat interface is a point-to-point tunnel, we don't 422 // point the route directly at the interface because some apps don't 423 // understand routes without gateways (see, e.g., http://b/9597256 424 // http://b/9597516). Instead, set the next hop of the route to the 425 // clat IPv4 address itself (for those apps, it doesn't matter what 426 // the IP of the gateway is, only that there is one). 427 RouteInfo ipv4Default = new RouteInfo( 428 new LinkAddress(NetworkStackConstants.IPV4_ADDR_ANY, 0), 429 clatAddress.getAddress(), mIface); 430 stacked.addRoute(ipv4Default); 431 stacked.addLinkAddress(clatAddress); 432 return stacked; 433 } 434 getLinkAddress(String iface)435 private LinkAddress getLinkAddress(String iface) { 436 try { 437 final InterfaceConfigurationParcel config = mNetd.interfaceGetCfg(iface); 438 return new LinkAddress( 439 InetAddresses.parseNumericAddress(config.ipv4Addr), config.prefixLength); 440 } catch (IllegalArgumentException | RemoteException | ServiceSpecificException e) { 441 Log.e(TAG, "Error getting link properties: " + e); 442 return null; 443 } 444 } 445 446 /** 447 * Adds stacked link on base link and transitions to RUNNING state. 448 */ handleInterfaceLinkStateChanged(String iface, boolean up)449 private void handleInterfaceLinkStateChanged(String iface, boolean up) { 450 // TODO: if we call start(), then stop(), then start() again, and the 451 // interfaceLinkStateChanged notification for the first start is delayed past the first 452 // stop, then the code becomes out of sync with system state and will behave incorrectly. 453 // 454 // This is not trivial to fix because: 455 // 1. It is not guaranteed that start() will eventually result in the interface coming up, 456 // because there could be an error starting clat (e.g., if the interface goes down before 457 // the packet socket can be bound). 458 // 2. If start is called multiple times, there is nothing in the interfaceLinkStateChanged 459 // notification that says which start() call the interface was created by. 460 // 461 // Once this code is converted to StateMachine, it will be possible to use deferMessage to 462 // ensure it stays in STARTING state until the interfaceLinkStateChanged notification fires, 463 // and possibly use a timeout (or provide some guarantees at the lower layer) to address #1. 464 if (!isStarting() || !up || !Objects.equals(mIface, iface)) { 465 return; 466 } 467 468 LinkAddress clatAddress = getLinkAddress(iface); 469 if (clatAddress == null) { 470 Log.e(TAG, "clatAddress was null for stacked iface " + iface); 471 return; 472 } 473 474 Log.i(TAG, String.format("interface %s is up, adding stacked link %s on top of %s", 475 mIface, mIface, mBaseIface)); 476 enterRunningState(); 477 LinkProperties lp = new LinkProperties(mNetwork.linkProperties); 478 lp.addStackedLink(makeLinkProperties(clatAddress)); 479 mNetwork.connService().handleUpdateLinkProperties(mNetwork, lp); 480 } 481 482 /** 483 * Removes stacked link on base link and transitions to IDLE state. 484 */ handleInterfaceRemoved(String iface)485 private void handleInterfaceRemoved(String iface) { 486 if (!Objects.equals(mIface, iface)) { 487 return; 488 } 489 if (!isRunning()) { 490 return; 491 } 492 493 Log.i(TAG, "interface " + iface + " removed"); 494 // If we're running, and the interface was removed, then we didn't call stop(), and it's 495 // likely that clatd crashed. Ensure we call stop() so we can start clatd again. Calling 496 // stop() will also update LinkProperties, and if clatd crashed, the LinkProperties update 497 // will cause ConnectivityService to call start() again. 498 stop(); 499 } 500 interfaceLinkStateChanged(String iface, boolean up)501 public void interfaceLinkStateChanged(String iface, boolean up) { 502 mNetwork.handler().post(() -> { handleInterfaceLinkStateChanged(iface, up); }); 503 } 504 interfaceRemoved(String iface)505 public void interfaceRemoved(String iface) { 506 mNetwork.handler().post(() -> handleInterfaceRemoved(iface)); 507 } 508 509 @Override toString()510 public String toString() { 511 return "mBaseIface: " + mBaseIface + ", mIface: " + mIface + ", mState: " + mState; 512 } 513 514 @VisibleForTesting getNetId()515 protected int getNetId() { 516 return mNetwork.network.getNetId(); 517 } 518 519 @VisibleForTesting isCellular464XlatEnabled()520 protected boolean isCellular464XlatEnabled() { 521 return mEnableClatOnCellular; 522 } 523 } 524