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