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