OSDN Git Service

Manage NAT64 prefix discovery lifecycle in the framework.
authorLorenzo Colitti <lorenzo@google.com>
Tue, 19 Feb 2019 04:21:56 +0000 (13:21 +0900)
committerLorenzo Colitti <lorenzo@google.com>
Tue, 26 Feb 2019 15:18:13 +0000 (00:18 +0900)
Currently NAT64 prefix discovery, which runs in netd, is started
by netd itself when a network is programmed with all-IPv6 DNS
servers. Unfortunately this is not correct because in many cases
we program DNS servers before the network is connected and it's
actually possible to send packets to them.

In general netd does not have enough visibility into network
lifecycle management to decide when to start and stop prefix
discovery. So move it into the framework with the rest of the
464xlat control plane.

Bug: 65674744
Test: atest FrameworksNetTests
Change-Id: I8fa051a9c216d9c05082bf7d0bbb0cbd56000162

services/core/java/com/android/server/ConnectivityService.java
services/core/java/com/android/server/connectivity/Nat464Xlat.java
services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
tests/net/java/com/android/server/ConnectivityServiceTest.java
tests/net/java/com/android/server/connectivity/Nat464XlatTest.java

index 8bd3450..6efa5c1 100644 (file)
@@ -2908,7 +2908,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
             e.rethrowFromSystemServer();
         }
         mNetworkAgentInfos.remove(nai.messenger);
-        nai.updateClat();
+        nai.clatd.update();
         synchronized (mNetworkForNetId) {
             // Remove the NetworkAgent, but don't mark the netId as
             // available until we've told netd to delete it below.
@@ -5249,8 +5249,8 @@ public class ConnectivityService extends IConnectivityManager.Stub
             synchronized (networkAgent) {
                 networkAgent.linkProperties = newLp;
             }
-            // Start or stop clat accordingly to network state.
-            networkAgent.updateClat();
+            // Start or stop DNS64 detection and 464xlat according to network state.
+            networkAgent.clatd.update();
             notifyIfacesChangedForNetworkStats();
             if (networkAgent.everConnected) {
                 try {
index 7b7fc0d..2ae14a9 100644 (file)
@@ -27,8 +27,10 @@ import android.net.NetworkInfo;
 import android.net.RouteInfo;
 import android.os.INetworkManagementService;
 import android.os.RemoteException;
+import android.os.ServiceSpecificException;
 import android.util.Slog;
 
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.ArrayUtils;
 import com.android.server.net.BaseNetworkObserver;
 
@@ -70,9 +72,10 @@ public class Nat464Xlat extends BaseNetworkObserver {
     private final NetworkAgentInfo mNetwork;
 
     private enum State {
-        IDLE,       // start() not called. Base iface and stacked iface names are null.
-        STARTING,   // start() called. Base iface and stacked iface names are known.
-        RUNNING,    // start() called, and the stacked iface is known to be up.
+        IDLE,         // start() not called. Base iface and stacked iface names are null.
+        DISCOVERING,  // same as IDLE, except prefix discovery in progress.
+        STARTING,     // start() called. Base iface and stacked iface names are known.
+        RUNNING,      // start() called, and the stacked iface is known to be up.
     }
 
     private IpPrefix mNat64Prefix;
@@ -88,25 +91,51 @@ public class Nat464Xlat extends BaseNetworkObserver {
     }
 
     /**
-     * Determines whether a network requires clat.
+     * Whether to attempt 464xlat on this network. This is true for an IPv6-only network that is
+     * currently connected and where the NetworkAgent has not disabled 464xlat. It is the signal to
+     * enable NAT64 prefix discovery.
+     *
      * @param network the NetworkAgentInfo corresponding to the network.
      * @return true if the network requires clat, false otherwise.
      */
-    public static boolean requiresClat(NetworkAgentInfo nai) {
+    @VisibleForTesting
+    protected static boolean requiresClat(NetworkAgentInfo nai) {
         // TODO: migrate to NetworkCapabilities.TRANSPORT_*.
         final boolean supported = ArrayUtils.contains(NETWORK_TYPES, nai.networkInfo.getType());
         final boolean connected = ArrayUtils.contains(NETWORK_STATES, nai.networkInfo.getState());
 
-        // We only run clat on networks that have a global IPv6 address and a NAT64 prefix and don't
-        // have a native IPv4 address.
+        // Only run clat on networks that have a global IPv6 address and don't have a native IPv4
+        // address.
         LinkProperties lp = nai.linkProperties;
-        final boolean isNat64Network = (lp != null) && lp.hasGlobalIPv6Address()
-                && lp.getNat64Prefix() != null && !lp.hasIPv4Address();
+        final boolean isIpv6OnlyNetwork = (lp != null) && lp.hasGlobalIPv6Address()
+                && !lp.hasIPv4Address();
 
         // If the network tells us it doesn't use clat, respect that.
         final boolean skip464xlat = (nai.netMisc() != null) && nai.netMisc().skip464xlat;
 
-        return supported && connected && isNat64Network && !skip464xlat;
+        return supported && connected && isIpv6OnlyNetwork && !skip464xlat;
+    }
+
+    /**
+     * Whether the clat demon should be started on this network now. This is true if requiresClat is
+     * true and a NAT64 prefix has been discovered.
+     *
+     * @param nai the NetworkAgentInfo corresponding to the network.
+     * @return true if the network should start clat, false otherwise.
+     */
+    @VisibleForTesting
+    protected static boolean shouldStartClat(NetworkAgentInfo nai) {
+        LinkProperties lp = nai.linkProperties;
+        return requiresClat(nai) && lp != null && lp.getNat64Prefix() != null;
+    }
+
+    /**
+     * @return true if we have started prefix discovery and not yet stopped it (regardless of
+     * whether it is still running or has succeeded).
+     * A true result corresponds to internal states DISCOVERING, STARTING and RUNNING.
+     */
+    public boolean isPrefixDiscoveryStarted() {
+        return mState == State.DISCOVERING || isStarted();
     }
 
     /**
@@ -114,7 +143,7 @@ public class Nat464Xlat extends BaseNetworkObserver {
      * A true result corresponds to internal states STARTING and RUNNING.
      */
     public boolean isStarted() {
-        return mState != State.IDLE;
+        return (mState == State.STARTING || mState == State.RUNNING);
     }
 
     /**
@@ -170,7 +199,7 @@ public class Nat464Xlat extends BaseNetworkObserver {
     /**
      * Unregister as a base observer for the stacked interface, and clear internal state.
      */
-    private void enterIdleState() {
+    private void leaveStartedState() {
         try {
             mNMService.unregisterObserver(this);
         } catch (RemoteException | IllegalStateException e) {
@@ -179,12 +208,16 @@ public class Nat464Xlat extends BaseNetworkObserver {
         mIface = null;
         mBaseIface = null;
         mState = State.IDLE;
+        if (requiresClat(mNetwork)) {
+            mState = State.DISCOVERING;
+        } else {
+            stopPrefixDiscovery();
+            mState = State.IDLE;
+        }
     }
 
-    /**
-     * Starts the clat daemon.
-     */
-    public void start() {
+    @VisibleForTesting
+    protected void start() {
         if (isStarted()) {
             Slog.e(TAG, "startClat: already started");
             return;
@@ -205,10 +238,8 @@ public class Nat464Xlat extends BaseNetworkObserver {
         enterStartingState(baseIface);
     }
 
-    /**
-     * Stops the clat daemon.
-     */
-    public void stop() {
+    @VisibleForTesting
+    protected void stop() {
         if (!isStarted()) {
             Slog.e(TAG, "stopClat: already stopped");
             return;
@@ -224,11 +255,10 @@ public class Nat464Xlat extends BaseNetworkObserver {
         String iface = mIface;
         boolean wasRunning = isRunning();
 
-        // Enter IDLE state before updating LinkProperties. handleUpdateLinkProperties ends up
-        // calling fixupLinkProperties, and if at that time the state is still RUNNING,
-        // fixupLinkProperties would wrongly inform ConnectivityService that there is still a
-        // stacked interface.
-        enterIdleState();
+        // Change state before updating LinkProperties. handleUpdateLinkProperties ends up calling
+        // fixupLinkProperties, and if at that time the state is still RUNNING, fixupLinkProperties
+        // would wrongly inform ConnectivityService that there is still a stacked interface.
+        leaveStartedState();
 
         if (wasRunning) {
             LinkProperties lp = new LinkProperties(mNetwork.linkProperties);
@@ -237,6 +267,51 @@ public class Nat464Xlat extends BaseNetworkObserver {
         }
     }
 
+    private void startPrefixDiscovery() {
+        try {
+            mNetd.resolverStartPrefix64Discovery(getNetId());
+            mState = State.DISCOVERING;
+        } catch (RemoteException | ServiceSpecificException e) {
+            Slog.e(TAG, "Error starting prefix discovery on netId " + getNetId(), e);
+        }
+    }
+
+    private void stopPrefixDiscovery() {
+        try {
+            mNetd.resolverStopPrefix64Discovery(getNetId());
+        } catch (RemoteException | ServiceSpecificException e) {
+            Slog.e(TAG, "Error stopping prefix discovery on netId " + getNetId(), e);
+        }
+    }
+
+    /**
+     * Starts/stops NAT64 prefix discovery and clatd as necessary.
+     */
+    public void update() {
+        // TODO: turn this class into a proper StateMachine. // http://b/126113090
+        if (requiresClat(mNetwork)) {
+            if (!isPrefixDiscoveryStarted()) {
+                startPrefixDiscovery();
+            } else if (shouldStartClat(mNetwork)) {
+                // NAT64 prefix detected. Start clatd.
+                // TODO: support the NAT64 prefix changing after it's been discovered. There is no
+                // need to support this at the moment because it cannot happen without changes to
+                // the Dns64Configuration code in netd.
+                start();
+            } else {
+                // NAT64 prefix removed. Stop clatd and go back into DISCOVERING state.
+                stop();
+            }
+        } else {
+            // Network no longer requires clat. Stop clat and prefix discovery.
+            if (isStarted()) {
+                stop();
+            } else if (isPrefixDiscoveryStarted()) {
+                leaveStartedState();
+            }
+        }
+    }
+
     public void setNat64Prefix(IpPrefix nat64Prefix) {
         mNat64Prefix = nat64Prefix;
     }
@@ -297,6 +372,20 @@ public class Nat464Xlat extends BaseNetworkObserver {
      * Adds stacked link on base link and transitions to RUNNING state.
      */
     private void handleInterfaceLinkStateChanged(String iface, boolean up) {
+        // TODO: if we call start(), then stop(), then start() again, and the
+        // interfaceLinkStateChanged notification for the first start is delayed past the first
+        // stop, then the code becomes out of sync with system state and will behave incorrectly.
+        //
+        // This is not trivial to fix because:
+        // 1. It is not guaranteed that start() will eventually result in the interface coming up,
+        //    because there could be an error starting clat (e.g., if the interface goes down before
+        //    the packet socket can be bound).
+        // 2. If start is called multiple times, there is nothing in the interfaceLinkStateChanged
+        //    notification that says which start() call the interface was created by.
+        //
+        // Once this code is converted to StateMachine, it will be possible to use deferMessage to
+        // ensure it stays in STARTING state until the interfaceLinkStateChanged notification fires,
+        // and possibly use a timeout (or provide some guarantees at the lower layer) to address #1.
         if (!isStarting() || !up || !Objects.equals(mIface, iface)) {
             return;
         }
@@ -348,4 +437,9 @@ public class Nat464Xlat extends BaseNetworkObserver {
     public String toString() {
         return "mBaseIface: " + mBaseIface + ", mIface: " + mIface + ", mState: " + mState;
     }
+
+    @VisibleForTesting
+    protected int getNetId() {
+        return mNetwork.network.netId;
+    }
 }
index 96f7283..f11a0de 100644 (file)
@@ -592,15 +592,6 @@ public class NetworkAgentInfo implements Comparable<NetworkAgentInfo> {
         for (LingerTimer timer : mLingerTimers) { pw.println(timer); }
     }
 
-    /** Starts or stops clatd as required. */
-    public void updateClat() {
-        if (!clatd.isStarted() && Nat464Xlat.requiresClat(this)) {
-            clatd.start();
-        } else if (clatd.isStarted() && !Nat464Xlat.requiresClat(this)) {
-            clatd.stop();
-        }
-    }
-
     public String toString() {
         return "NetworkAgentInfo{ ni{" + networkInfo + "}  "
                 + "network{" + network + "}  nethandle{" + network.getNetworkHandle() + "}  "
index 18008b6..c126026 100644 (file)
@@ -5103,8 +5103,9 @@ public class ConnectivityServiceTest {
         final TestNetworkCallback networkCallback = new TestNetworkCallback();
         mCm.registerNetworkCallback(networkRequest, networkCallback);
 
-        // Prepare ipv6 only link properties and connect.
+        // Prepare ipv6 only link properties.
         mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
+        final int cellNetId = mCellNetworkAgent.getNetwork().netId;
         final LinkProperties cellLp = new LinkProperties();
         cellLp.setInterfaceName(MOBILE_IFNAME);
         cellLp.addLinkAddress(myIpv6);
@@ -5114,17 +5115,37 @@ public class ConnectivityServiceTest {
         when(mNetworkManagementService.getInterfaceConfig(CLAT_PREFIX + MOBILE_IFNAME))
                 .thenReturn(getClatInterfaceConfig(myIpv4));
 
-        // Connect with ipv6 link properties, then expect clat setup ipv4 and update link
-        // properties properly.
+        // Connect with ipv6 link properties. Expect prefix discovery to be started.
         mCellNetworkAgent.sendLinkProperties(cellLp);
         mCellNetworkAgent.connect(true);
         networkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
+        verify(mMockNetd, times(1)).resolverStartPrefix64Discovery(cellNetId);
 
-        // When NAT64 prefix detection succeeds, LinkProperties are updated and clatd is started.
+        // Switching default network updates TCP buffer sizes.
+        verifyTcpBufferSizeChange(ConnectivityService.DEFAULT_TCP_BUFFER_SIZES);
+
+        // Add an IPv4 address. Expect prefix discovery to be stopped. Netd doesn't tell us that
+        // the NAT64 prefix was removed because one was never discovered.
+        cellLp.addLinkAddress(myIpv4);
+        mCellNetworkAgent.sendLinkProperties(cellLp);
+        networkCallback.expectCallback(CallbackState.LINK_PROPERTIES, mCellNetworkAgent);
+        verify(mMockNetd, times(1)).resolverStopPrefix64Discovery(cellNetId);
+
+        verifyNoMoreInteractions(mMockNetd);
+        reset(mMockNetd);
+
+        // Remove IPv4 address. Expect prefix discovery to be started again.
+        cellLp.removeLinkAddress(myIpv4);
+        cellLp.removeRoute(new RouteInfo(myIpv4, null, MOBILE_IFNAME));
+        mCellNetworkAgent.sendLinkProperties(cellLp);
+        networkCallback.expectCallback(CallbackState.LINK_PROPERTIES, mCellNetworkAgent);
+        verify(mMockNetd, times(1)).resolverStartPrefix64Discovery(cellNetId);
+
+        // When NAT64 prefix discovery succeeds, LinkProperties are updated and clatd is started.
         Nat464Xlat clat = mService.getNat464Xlat(mCellNetworkAgent);
         assertNull(mCm.getLinkProperties(mCellNetworkAgent.getNetwork()).getNat64Prefix());
-        mService.mNetdEventCallback.onNat64PrefixEvent(mCellNetworkAgent.getNetwork().netId,
-                true /* added */, kNat64PrefixString, 96);
+        mService.mNetdEventCallback.onNat64PrefixEvent(cellNetId, true /* added */,
+                kNat64PrefixString, 96);
         LinkProperties lpBeforeClat = (LinkProperties) networkCallback.expectCallback(
                 CallbackState.LINK_PROPERTIES, mCellNetworkAgent).arg;
         assertEquals(0, lpBeforeClat.getStackedLinks().size());
@@ -5148,15 +5169,17 @@ public class ConnectivityServiceTest {
         assertNotEquals(stackedLpsAfterChange, Collections.EMPTY_LIST);
         assertEquals(makeClatLinkProperties(myIpv4), stackedLpsAfterChange.get(0));
 
-        // Add ipv4 address, expect that clatd is stopped and stacked linkproperties are cleaned up.
+        // Add ipv4 address, expect that clatd and prefix discovery are stopped and stacked
+        // linkproperties are cleaned up.
         cellLp.addLinkAddress(myIpv4);
         cellLp.addRoute(new RouteInfo(myIpv4, null, MOBILE_IFNAME));
         mCellNetworkAgent.sendLinkProperties(cellLp);
         networkCallback.expectCallback(CallbackState.LINK_PROPERTIES, mCellNetworkAgent);
         verify(mMockNetd, times(1)).clatdStop(MOBILE_IFNAME);
-        networkCallback.expectCallback(CallbackState.LINK_PROPERTIES, mCellNetworkAgent);
+        verify(mMockNetd, times(1)).resolverStopPrefix64Discovery(cellNetId);
 
         // As soon as stop is called, the linkproperties lose the stacked interface.
+        networkCallback.expectCallback(CallbackState.LINK_PROPERTIES, mCellNetworkAgent);
         LinkProperties actualLpAfterIpv4 = mCm.getLinkProperties(mCellNetworkAgent.getNetwork());
         LinkProperties expected = new LinkProperties(cellLp);
         expected.setNat64Prefix(kNat64Prefix);
@@ -5167,25 +5190,39 @@ public class ConnectivityServiceTest {
         clat.interfaceRemoved(CLAT_PREFIX + MOBILE_IFNAME);
         networkCallback.assertNoCallback();
 
+        verifyNoMoreInteractions(mMockNetd);
         reset(mMockNetd);
 
-        // Remove IPv4 address and expect clatd to be started again.
+        // Stopping prefix discovery causes netd to tell us that the NAT64 prefix is gone.
+        mService.mNetdEventCallback.onNat64PrefixEvent(cellNetId, false /* added */,
+                kNat64PrefixString, 96);
+        networkCallback.expectLinkPropertiesLike((lp) -> lp.getNat64Prefix() == null,
+                mCellNetworkAgent);
+
+        // Remove IPv4 address and expect prefix discovery and clatd to be started again.
         cellLp.removeLinkAddress(myIpv4);
         cellLp.removeRoute(new RouteInfo(myIpv4, null, MOBILE_IFNAME));
         cellLp.removeDnsServer(InetAddress.getByName("8.8.8.8"));
         mCellNetworkAgent.sendLinkProperties(cellLp);
         networkCallback.expectCallback(CallbackState.LINK_PROPERTIES, mCellNetworkAgent);
+        verify(mMockNetd, times(1)).resolverStartPrefix64Discovery(cellNetId);
+        mService.mNetdEventCallback.onNat64PrefixEvent(cellNetId, true /* added */,
+                kNat64PrefixString, 96);
+        networkCallback.expectCallback(CallbackState.LINK_PROPERTIES, mCellNetworkAgent);
         verify(mMockNetd, times(1)).clatdStart(MOBILE_IFNAME, kNat64Prefix.toString());
 
+
         // Clat iface comes up. Expect stacked link to be added.
         clat.interfaceLinkStateChanged(CLAT_PREFIX + MOBILE_IFNAME, true);
-        networkCallback.expectLinkPropertiesLike((lp) -> lp.getStackedLinks().size() == 1,
+        networkCallback.expectLinkPropertiesLike(
+                (lp) -> lp.getStackedLinks().size() == 1 && lp.getNat64Prefix() != null,
                 mCellNetworkAgent);
 
         // NAT64 prefix is removed. Expect that clat is stopped.
-        mService.mNetdEventCallback.onNat64PrefixEvent(mCellNetworkAgent.getNetwork().netId,
-                false /* added */, kNat64PrefixString, 96);
-        networkCallback.expectLinkPropertiesLike((lp) -> lp.getNat64Prefix() == null,
+        mService.mNetdEventCallback.onNat64PrefixEvent(cellNetId, false /* added */,
+                kNat64PrefixString, 96);
+        networkCallback.expectLinkPropertiesLike(
+                (lp) -> lp.getStackedLinks().size() == 0 && lp.getNat64Prefix() == null,
                 mCellNetworkAgent);
         verify(mMockNetd, times(1)).clatdStop(MOBILE_IFNAME);
         networkCallback.expectLinkPropertiesLike((lp) -> lp.getStackedLinks().size() == 0,
index 9b7ff31..37c0df8 100644 (file)
@@ -58,6 +58,7 @@ public class Nat464XlatTest {
     static final String STACKED_IFACE = "v4-test0";
     static final LinkAddress ADDR = new LinkAddress("192.0.2.5/29");
     static final String NAT64_PREFIX = "64:ff9b::/96";
+    static final int NETID = 42;
 
     @Mock ConnectivityService mConnectivity;
     @Mock NetworkMisc mMisc;
@@ -70,7 +71,11 @@ public class Nat464XlatTest {
     Handler mHandler;
 
     Nat464Xlat makeNat464Xlat() {
-        return new Nat464Xlat(mNai, mNetd, mNms);
+        return new Nat464Xlat(mNai, mNetd, mNms) {
+            @Override protected int getNetId() {
+                return NETID;
+            }
+        };
     }
 
     @Before
@@ -93,7 +98,7 @@ public class Nat464XlatTest {
     }
 
     private void assertRequiresClat(boolean expected, NetworkAgentInfo nai) {
-        String msg = String.format("requiresClat expected %b for type=%d state=%s skip464xlat=%b "
+        String msg = String.format("requiresClat expected %b for type=%d state=%s skip=%b "
                 + "nat64Prefix=%s addresses=%s", expected, nai.networkInfo.getType(),
                 nai.networkInfo.getDetailedState(),
                 mMisc.skip464xlat, nai.linkProperties.getNat64Prefix(),
@@ -101,6 +106,15 @@ public class Nat464XlatTest {
         assertEquals(msg, expected, Nat464Xlat.requiresClat(nai));
     }
 
+    private void assertShouldStartClat(boolean expected, NetworkAgentInfo nai) {
+        String msg = String.format("shouldStartClat expected %b for type=%d state=%s skip=%b "
+                + "nat64Prefix=%s addresses=%s", expected, nai.networkInfo.getType(),
+                nai.networkInfo.getDetailedState(),
+                mMisc.skip464xlat, nai.linkProperties.getNat64Prefix(),
+                nai.linkProperties.getLinkAddresses());
+        assertEquals(msg, expected, Nat464Xlat.shouldStartClat(nai));
+    }
+
     @Test
     public void testRequiresClat() throws Exception {
         final int[] supportedTypes = {
@@ -124,21 +138,35 @@ public class Nat464XlatTest {
 
                 mNai.linkProperties.setNat64Prefix(new IpPrefix("2001:db8:0:64::/96"));
                 assertRequiresClat(false, mNai);
+                assertShouldStartClat(false, mNai);
 
                 mNai.linkProperties.addLinkAddress(new LinkAddress("fc00::1/64"));
                 assertRequiresClat(false, mNai);
+                assertShouldStartClat(false, mNai);
 
                 mNai.linkProperties.addLinkAddress(new LinkAddress("2001:db8::1/64"));
                 assertRequiresClat(true, mNai);
+                assertShouldStartClat(true, mNai);
 
                 mMisc.skip464xlat = true;
                 assertRequiresClat(false, mNai);
+                assertShouldStartClat(false, mNai);
 
                 mMisc.skip464xlat = false;
                 assertRequiresClat(true, mNai);
+                assertShouldStartClat(true, mNai);
 
                 mNai.linkProperties.addLinkAddress(new LinkAddress("192.0.2.2/24"));
                 assertRequiresClat(false, mNai);
+                assertShouldStartClat(false, mNai);
+
+                mNai.linkProperties.removeLinkAddress(new LinkAddress("192.0.2.2/24"));
+                assertRequiresClat(true, mNai);
+                assertShouldStartClat(true, mNai);
+
+                mNai.linkProperties.setNat64Prefix(null);
+                assertRequiresClat(true, mNai);
+                assertShouldStartClat(false, mNai);
 
                 mNai.linkProperties = new LinkProperties(oldLp);
             }
@@ -152,7 +180,7 @@ public class Nat464XlatTest {
 
         nat.setNat64Prefix(new IpPrefix(NAT64_PREFIX));
 
-        // ConnectivityService starts clat.
+        // Start clat.
         nat.start();
 
         verify(mNms).registerObserver(eq(nat));
@@ -168,7 +196,7 @@ public class Nat464XlatTest {
         assertTrue(c.getValue().getAllInterfaceNames().contains(STACKED_IFACE));
         assertRunning(nat);
 
-        // ConnectivityService stops clat (Network disconnects, IPv4 addr appears, ...).
+        // Stop clat (Network disconnects, IPv4 addr appears, ...).
         nat.stop();
 
         verify(mNetd).clatdStop(eq(BASE_IFACE));
@@ -176,6 +204,7 @@ public class Nat464XlatTest {
         verify(mNms).unregisterObserver(eq(nat));
         assertTrue(c.getValue().getStackedLinks().isEmpty());
         assertFalse(c.getValue().getAllInterfaceNames().contains(STACKED_IFACE));
+        verify(mNetd).resolverStopPrefix64Discovery(eq(NETID));
         assertIdle(nat);
 
         // Stacked interface removed notification arrives and is ignored.
@@ -192,7 +221,6 @@ public class Nat464XlatTest {
 
         nat.setNat64Prefix(new IpPrefix(NAT64_PREFIX));
 
-        // ConnectivityService starts clat.
         nat.start();
 
         inOrder.verify(mNetd).clatdStart(eq(BASE_IFACE), eq(NAT64_PREFIX));
@@ -229,7 +257,6 @@ public class Nat464XlatTest {
         assertIdle(nat);
         inOrder.verifyNoMoreInteractions();
 
-        // ConnectivityService starts clatd again.
         nat.start();
 
         inOrder.verify(mNetd).clatdStart(eq(BASE_IFACE), eq(NAT64_PREFIX));
@@ -281,7 +308,6 @@ public class Nat464XlatTest {
 
         nat.setNat64Prefix(new IpPrefix(NAT64_PREFIX));
 
-        // ConnectivityService starts clat.
         nat.start();
 
         verify(mNms).registerObserver(eq(nat));
@@ -304,6 +330,7 @@ public class Nat464XlatTest {
         verify(mNetd).clatdStop(eq(BASE_IFACE));
         verify(mConnectivity, times(2)).handleUpdateLinkProperties(eq(mNai), c.capture());
         verify(mNms).unregisterObserver(eq(nat));
+        verify(mNetd).resolverStopPrefix64Discovery(eq(NETID));
         assertTrue(c.getValue().getStackedLinks().isEmpty());
         assertFalse(c.getValue().getAllInterfaceNames().contains(STACKED_IFACE));
         assertIdle(nat);
@@ -320,7 +347,6 @@ public class Nat464XlatTest {
 
         nat.setNat64Prefix(new IpPrefix(NAT64_PREFIX));
 
-        // ConnectivityService starts clat.
         nat.start();
 
         verify(mNms).registerObserver(eq(nat));
@@ -331,13 +357,13 @@ public class Nat464XlatTest {
 
         verify(mNetd).clatdStop(eq(BASE_IFACE));
         verify(mNms).unregisterObserver(eq(nat));
+        verify(mNetd).resolverStopPrefix64Discovery(eq(NETID));
         assertIdle(nat);
 
         // In-flight interface up notification arrives: no-op
         nat.interfaceLinkStateChanged(STACKED_IFACE, true);
         mLooper.dispatchNext();
 
-
         // Interface removed notification arrives after stopClatd() takes effect: no-op.
         nat.interfaceRemoved(STACKED_IFACE);
         mLooper.dispatchNext();
@@ -353,7 +379,6 @@ public class Nat464XlatTest {
 
         nat.setNat64Prefix(new IpPrefix(NAT64_PREFIX));
 
-        // ConnectivityService starts clat.
         nat.start();
 
         verify(mNms).registerObserver(eq(nat));
@@ -364,6 +389,7 @@ public class Nat464XlatTest {
 
         verify(mNetd).clatdStop(eq(BASE_IFACE));
         verify(mNms).unregisterObserver(eq(nat));
+        verify(mNetd).resolverStopPrefix64Discovery(eq(NETID));
         assertIdle(nat);
 
         verifyNoMoreInteractions(mNetd, mNms, mConnectivity);