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