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