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