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