• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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