1 /*
2  * Copyright (C) 2017 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 
21 import static org.junit.Assert.assertEquals;
22 import static org.junit.Assert.assertFalse;
23 import static org.junit.Assert.assertNull;
24 import static org.junit.Assert.assertTrue;
25 import static org.mockito.Mockito.any;
26 import static org.mockito.Mockito.anyInt;
27 import static org.mockito.Mockito.anyString;
28 import static org.mockito.Mockito.doReturn;
29 import static org.mockito.Mockito.eq;
30 import static org.mockito.Mockito.inOrder;
31 import static org.mockito.Mockito.never;
32 import static org.mockito.Mockito.times;
33 import static org.mockito.Mockito.verify;
34 import static org.mockito.Mockito.verifyNoMoreInteractions;
35 import static org.mockito.Mockito.when;
36 
37 import android.net.ConnectivityManager;
38 import android.net.IDnsResolver;
39 import android.net.INetd;
40 import android.net.InterfaceConfigurationParcel;
41 import android.net.IpPrefix;
42 import android.net.LinkAddress;
43 import android.net.LinkProperties;
44 import android.net.NetworkAgentConfig;
45 import android.net.NetworkCapabilities;
46 import android.net.NetworkInfo;
47 import android.os.Build;
48 import android.os.Handler;
49 import android.os.test.TestLooper;
50 
51 import androidx.annotation.NonNull;
52 import androidx.annotation.Nullable;
53 import androidx.test.filters.SmallTest;
54 
55 import com.android.modules.utils.build.SdkLevel;
56 import com.android.server.ConnectivityService;
57 import com.android.testutils.DevSdkIgnoreRule;
58 import com.android.testutils.DevSdkIgnoreRunner;
59 
60 import org.junit.Before;
61 import org.junit.Test;
62 import org.junit.runner.RunWith;
63 import org.mockito.ArgumentCaptor;
64 import org.mockito.InOrder;
65 import org.mockito.Mock;
66 import org.mockito.MockitoAnnotations;
67 
68 @RunWith(DevSdkIgnoreRunner.class)
69 @SmallTest
70 @DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.R)
71 public class Nat464XlatTest {
72 
73     static final String BASE_IFACE = "test0";
74     static final String STACKED_IFACE = "v4-test0";
75     static final LinkAddress V6ADDR = new LinkAddress("2001:db8:1::f00/64");
76     static final LinkAddress ADDR = new LinkAddress("192.0.2.5/29");
77     static final String CLAT_V6 = "64:ff9b::1";
78     static final String NAT64_PREFIX = "64:ff9b::/96";
79     static final String OTHER_NAT64_PREFIX = "2001:db8:0:64::/96";
80     static final int NETID = 42;
81 
82     @Mock ConnectivityService mConnectivity;
83     @Mock IDnsResolver mDnsResolver;
84     @Mock INetd mNetd;
85     @Mock NetworkAgentInfo mNai;
86     @Mock ClatCoordinator mClatCoordinator;
87 
88     TestLooper mLooper;
89     NetworkAgentConfig mAgentConfig = new NetworkAgentConfig();
90 
makeNat464Xlat(boolean isCellular464XlatEnabled)91     Nat464Xlat makeNat464Xlat(boolean isCellular464XlatEnabled) {
92         final ConnectivityService.Dependencies deps = new ConnectivityService.Dependencies() {
93             @Override public ClatCoordinator getClatCoordinator(INetd netd) {
94                 return mClatCoordinator;
95             }
96         };
97 
98         // The test looper needs to be created here on the test case thread and not in setUp,
99         // because setUp and test cases are run in different threads. Creating the test looper in
100         // setUp would make Looper.getThread() return the setUp thread, which does not match the
101         // test case thread that is actually used to process the messages.
102         mLooper = new TestLooper();
103         final Handler handler = new Handler(mLooper.getLooper());
104         doReturn(handler).when(mNai).handler();
105 
106         return new Nat464Xlat(mNai, mNetd, mDnsResolver, deps) {
107             @Override protected int getNetId() {
108                 return NETID;
109             }
110 
111             @Override protected boolean isCellular464XlatEnabled() {
112                 return isCellular464XlatEnabled;
113             }
114         };
115     }
116 
117     private void markNetworkConnected() {
118         mNai.networkInfo.setDetailedState(NetworkInfo.DetailedState.CONNECTED, "", "");
119     }
120 
121     private void markNetworkDisconnected() {
122         mNai.networkInfo.setDetailedState(NetworkInfo.DetailedState.DISCONNECTED, "", "");
123     }
124 
125     @Before
126     public void setUp() throws Exception {
127         MockitoAnnotations.initMocks(this);
128 
129         mNai.linkProperties = new LinkProperties();
130         mNai.linkProperties.setInterfaceName(BASE_IFACE);
131         mNai.networkInfo = new NetworkInfo(ConnectivityManager.TYPE_WIFI, 0 /* subtype */,
132                 null /* typeName */, null /* subtypeName */);
133         mNai.networkCapabilities = new NetworkCapabilities();
134         markNetworkConnected();
135         when(mNai.connService()).thenReturn(mConnectivity);
136         when(mNai.netAgentConfig()).thenReturn(mAgentConfig);
137         final InterfaceConfigurationParcel mConfig = new InterfaceConfigurationParcel();
138         when(mNetd.interfaceGetCfg(eq(STACKED_IFACE))).thenReturn(mConfig);
139         mConfig.ipv4Addr = ADDR.getAddress().getHostAddress();
140         mConfig.prefixLength =  ADDR.getPrefixLength();
141         doReturn(CLAT_V6).when(mClatCoordinator).clatStart(
142                 BASE_IFACE, NETID, new IpPrefix(NAT64_PREFIX));
143     }
144 
145     private void assertRequiresClat(boolean expected, NetworkAgentInfo nai) {
146         Nat464Xlat nat = makeNat464Xlat(true);
147         String msg = String.format("requiresClat expected %b for type=%d state=%s skip=%b "
148                 + "nat64Prefix=%s addresses=%s", expected, nai.networkInfo.getType(),
149                 nai.networkInfo.getDetailedState(),
150                 mAgentConfig.skip464xlat, nai.linkProperties.getNat64Prefix(),
151                 nai.linkProperties.getLinkAddresses());
152         assertEquals(msg, expected, nat.requiresClat(nai));
153     }
154 
155     private void assertShouldStartClat(boolean expected, NetworkAgentInfo nai) {
156         Nat464Xlat nat = makeNat464Xlat(true);
157         String msg = String.format("shouldStartClat expected %b for type=%d state=%s skip=%b "
158                 + "nat64Prefix=%s addresses=%s", expected, nai.networkInfo.getType(),
159                 nai.networkInfo.getDetailedState(),
160                 mAgentConfig.skip464xlat, nai.linkProperties.getNat64Prefix(),
161                 nai.linkProperties.getLinkAddresses());
162         assertEquals(msg, expected, nat.shouldStartClat(nai));
163     }
164 
165     @Test
166     public void testRequiresClat() throws Exception {
167         final int[] supportedTypes = {
168             ConnectivityManager.TYPE_MOBILE,
169             ConnectivityManager.TYPE_WIFI,
170             ConnectivityManager.TYPE_ETHERNET,
171         };
172 
173         // NetworkInfo doesn't allow setting the State directly, but rather
174         // requires setting DetailedState in order set State as a side-effect.
175         final NetworkInfo.DetailedState[] supportedDetailedStates = {
176             NetworkInfo.DetailedState.CONNECTED,
177             NetworkInfo.DetailedState.SUSPENDED,
178         };
179 
180         LinkProperties oldLp = new LinkProperties(mNai.linkProperties);
181         for (int type : supportedTypes) {
182             mNai.networkInfo.setType(type);
183             for (NetworkInfo.DetailedState state : supportedDetailedStates) {
184                 mNai.networkInfo.setDetailedState(state, "reason", "extraInfo");
185 
186                 mNai.linkProperties.setNat64Prefix(new IpPrefix(OTHER_NAT64_PREFIX));
187                 assertRequiresClat(false, mNai);
188                 assertShouldStartClat(false, mNai);
189 
190                 mNai.linkProperties.addLinkAddress(new LinkAddress("fc00::1/64"));
191                 assertRequiresClat(false, mNai);
192                 assertShouldStartClat(false, mNai);
193 
194                 mNai.linkProperties.addLinkAddress(new LinkAddress("2001:db8::1/64"));
195                 assertRequiresClat(true, mNai);
196                 assertShouldStartClat(true, mNai);
197 
198                 mAgentConfig.skip464xlat = true;
199                 assertRequiresClat(false, mNai);
200                 assertShouldStartClat(false, mNai);
201 
202                 mAgentConfig.skip464xlat = false;
203                 assertRequiresClat(true, mNai);
204                 assertShouldStartClat(true, mNai);
205 
206                 mNai.linkProperties.addLinkAddress(new LinkAddress("192.0.2.2/24"));
207                 assertRequiresClat(false, mNai);
208                 assertShouldStartClat(false, mNai);
209 
210                 mNai.linkProperties.removeLinkAddress(new LinkAddress("192.0.2.2/24"));
211                 assertRequiresClat(true, mNai);
212                 assertShouldStartClat(true, mNai);
213 
214                 mNai.linkProperties.setNat64Prefix(null);
215                 assertRequiresClat(true, mNai);
216                 assertShouldStartClat(false, mNai);
217 
218                 mNai.linkProperties = new LinkProperties(oldLp);
219             }
220         }
221     }
222 
223     private void makeClatUnnecessary(boolean dueToDisconnect) {
224         if (dueToDisconnect) {
225             markNetworkDisconnected();
226         } else {
227             mNai.linkProperties.addLinkAddress(ADDR);
228         }
229     }
230 
231     private <T> T verifyWithOrder(@Nullable InOrder inOrder, @NonNull T t) {
232         if (inOrder != null) {
233             return inOrder.verify(t);
234         } else {
235             return verify(t);
236         }
237     }
238 
239     private void verifyClatdStart(@Nullable InOrder inOrder) throws Exception {
240         if (SdkLevel.isAtLeastT()) {
241             verifyWithOrder(inOrder, mClatCoordinator)
242                 .clatStart(eq(BASE_IFACE), eq(NETID), eq(new IpPrefix(NAT64_PREFIX)));
243         } else {
244             verifyWithOrder(inOrder, mNetd).clatdStart(eq(BASE_IFACE), eq(NAT64_PREFIX));
245         }
246     }
247 
248     private void verifyNeverClatdStart() throws Exception {
249         if (SdkLevel.isAtLeastT()) {
250             verify(mClatCoordinator, never()).clatStart(anyString(), anyInt(), any());
251         } else {
252             verify(mNetd, never()).clatdStart(anyString(), anyString());
253         }
254     }
255 
256     private void verifyClatdStop(@Nullable InOrder inOrder) throws Exception {
257         if (SdkLevel.isAtLeastT()) {
258             verifyWithOrder(inOrder, mClatCoordinator).clatStop();
259         } else {
260             verifyWithOrder(inOrder, mNetd).clatdStop(eq(BASE_IFACE));
261         }
262     }
263 
264     private void checkNormalStartAndStop(boolean dueToDisconnect) throws Exception {
265         Nat464Xlat nat = makeNat464Xlat(true);
266         ArgumentCaptor<LinkProperties> c = ArgumentCaptor.forClass(LinkProperties.class);
267 
268         mNai.linkProperties.addLinkAddress(V6ADDR);
269 
270         nat.setNat64PrefixFromDns(new IpPrefix(NAT64_PREFIX));
271 
272         // Start clat.
273         nat.start();
274 
275         verifyClatdStart(null /* inOrder */);
276 
277         // Stacked interface up notification arrives.
278         nat.handleInterfaceLinkStateChanged(STACKED_IFACE, true);
279 
280         verify(mNetd).interfaceGetCfg(eq(STACKED_IFACE));
281         verify(mConnectivity).handleUpdateLinkProperties(eq(mNai), c.capture());
282         assertFalse(c.getValue().getStackedLinks().isEmpty());
283         assertTrue(c.getValue().getAllInterfaceNames().contains(STACKED_IFACE));
284         assertRunning(nat);
285 
286         // Stop clat (Network disconnects, IPv4 addr appears, ...).
287         makeClatUnnecessary(dueToDisconnect);
288         nat.stop();
289 
290         verifyClatdStop(null /* inOrder */);
291         verify(mConnectivity, times(2)).handleUpdateLinkProperties(eq(mNai), c.capture());
292         assertTrue(c.getValue().getStackedLinks().isEmpty());
293         assertFalse(c.getValue().getAllInterfaceNames().contains(STACKED_IFACE));
294         verify(mDnsResolver).stopPrefix64Discovery(eq(NETID));
295         assertIdle(nat);
296         // Verify the generated v6 is reset when clat is stopped.
297         assertNull(nat.mIPv6Address);
298         // Stacked interface removed notification arrives and is ignored.
299         nat.handleInterfaceRemoved(STACKED_IFACE);
300 
301         verifyNoMoreInteractions(mNetd, mConnectivity);
302     }
303 
304     @Test
305     public void testNormalStartAndStopDueToDisconnect() throws Exception {
306         checkNormalStartAndStop(true);
307     }
308 
309     @Test
310     public void testNormalStartAndStopDueToIpv4Addr() throws Exception {
311         checkNormalStartAndStop(false);
312     }
313 
314     private void checkStartStopStart(boolean interfaceRemovedFirst) throws Exception {
315         Nat464Xlat nat = makeNat464Xlat(true);
316         ArgumentCaptor<LinkProperties> c = ArgumentCaptor.forClass(LinkProperties.class);
317         InOrder inOrder = inOrder(mNetd, mConnectivity, mClatCoordinator);
318 
319         mNai.linkProperties.addLinkAddress(V6ADDR);
320 
321         nat.setNat64PrefixFromDns(new IpPrefix(NAT64_PREFIX));
322 
323         nat.start();
324 
325         verifyClatdStart(inOrder);
326 
327         // Stacked interface up notification arrives.
328         nat.handleInterfaceLinkStateChanged(STACKED_IFACE, true);
329 
330         inOrder.verify(mConnectivity).handleUpdateLinkProperties(eq(mNai), c.capture());
331         assertFalse(c.getValue().getStackedLinks().isEmpty());
332         assertTrue(c.getValue().getAllInterfaceNames().contains(STACKED_IFACE));
333         assertRunning(nat);
334 
335         // ConnectivityService stops clat (Network disconnects, IPv4 addr appears, ...).
336         nat.stop();
337 
338         verifyClatdStop(inOrder);
339 
340         inOrder.verify(mConnectivity, times(1)).handleUpdateLinkProperties(eq(mNai), c.capture());
341         assertTrue(c.getValue().getStackedLinks().isEmpty());
342         assertFalse(c.getValue().getAllInterfaceNames().contains(STACKED_IFACE));
343         assertIdle(nat);
344 
345         if (interfaceRemovedFirst) {
346             // Stacked interface removed notification arrives and is ignored.
347             nat.handleInterfaceRemoved(STACKED_IFACE);
348             nat.handleInterfaceLinkStateChanged(STACKED_IFACE, false);
349         }
350 
351         assertTrue(c.getValue().getStackedLinks().isEmpty());
352         assertFalse(c.getValue().getAllInterfaceNames().contains(STACKED_IFACE));
353         assertIdle(nat);
354         inOrder.verifyNoMoreInteractions();
355 
356         nat.start();
357 
358         verifyClatdStart(inOrder);
359 
360         if (!interfaceRemovedFirst) {
361             // Stacked interface removed notification arrives and is ignored.
362             nat.handleInterfaceRemoved(STACKED_IFACE);
363             nat.handleInterfaceLinkStateChanged(STACKED_IFACE, false);
364         }
365 
366         // Stacked interface up notification arrives.
367         nat.handleInterfaceLinkStateChanged(STACKED_IFACE, true);
368 
369         inOrder.verify(mConnectivity).handleUpdateLinkProperties(eq(mNai), c.capture());
370         assertFalse(c.getValue().getStackedLinks().isEmpty());
371         assertTrue(c.getValue().getAllInterfaceNames().contains(STACKED_IFACE));
372         assertRunning(nat);
373 
374         // ConnectivityService stops clat again.
375         nat.stop();
376 
377         verifyClatdStop(inOrder);
378 
379         inOrder.verify(mConnectivity, times(1)).handleUpdateLinkProperties(eq(mNai), c.capture());
380         assertTrue(c.getValue().getStackedLinks().isEmpty());
381         assertFalse(c.getValue().getAllInterfaceNames().contains(STACKED_IFACE));
382         assertIdle(nat);
383 
384         inOrder.verifyNoMoreInteractions();
385     }
386 
387     @Test
388     public void testStartStopStart() throws Exception {
389         checkStartStopStart(true);
390     }
391 
392     @Test
393     public void testStartStopStartBeforeInterfaceRemoved() throws Exception {
394         checkStartStopStart(false);
395     }
396 
397     @Test
398     public void testClatdCrashWhileRunning() throws Exception {
399         Nat464Xlat nat = makeNat464Xlat(true);
400         ArgumentCaptor<LinkProperties> c = ArgumentCaptor.forClass(LinkProperties.class);
401 
402         nat.setNat64PrefixFromDns(new IpPrefix(NAT64_PREFIX));
403 
404         nat.start();
405 
406         verifyClatdStart(null /* inOrder */);
407 
408         // Stacked interface up notification arrives.
409         nat.handleInterfaceLinkStateChanged(STACKED_IFACE, true);
410 
411         verify(mNetd).interfaceGetCfg(eq(STACKED_IFACE));
412         verify(mConnectivity, times(1)).handleUpdateLinkProperties(eq(mNai), c.capture());
413         assertFalse(c.getValue().getStackedLinks().isEmpty());
414         assertTrue(c.getValue().getAllInterfaceNames().contains(STACKED_IFACE));
415         assertRunning(nat);
416 
417         // Stacked interface removed notification arrives (clatd crashed, ...).
418         nat.handleInterfaceRemoved(STACKED_IFACE);
419 
420         verifyClatdStop(null /* inOrder */);
421         verify(mConnectivity, times(2)).handleUpdateLinkProperties(eq(mNai), c.capture());
422         verify(mDnsResolver).stopPrefix64Discovery(eq(NETID));
423         assertTrue(c.getValue().getStackedLinks().isEmpty());
424         assertFalse(c.getValue().getAllInterfaceNames().contains(STACKED_IFACE));
425         assertIdle(nat);
426 
427         // ConnectivityService stops clat: no-op.
428         nat.stop();
429 
430         verifyNoMoreInteractions(mNetd, mConnectivity);
431     }
432 
433     private void checkStopBeforeClatdStarts(boolean dueToDisconnect) throws Exception {
434         Nat464Xlat nat = makeNat464Xlat(true);
435 
436         mNai.linkProperties.addLinkAddress(new LinkAddress("2001:db8::1/64"));
437 
438         nat.setNat64PrefixFromDns(new IpPrefix(NAT64_PREFIX));
439 
440         nat.start();
441 
442         verifyClatdStart(null /* inOrder */);
443 
444         // ConnectivityService immediately stops clat (Network disconnects, IPv4 addr appears, ...)
445         makeClatUnnecessary(dueToDisconnect);
446         nat.stop();
447 
448         verifyClatdStop(null /* inOrder */);
449         verify(mDnsResolver).stopPrefix64Discovery(eq(NETID));
450         assertIdle(nat);
451 
452         // In-flight interface up notification arrives: no-op
453         nat.handleInterfaceLinkStateChanged(STACKED_IFACE, true);
454 
455         // Interface removed notification arrives after stopClatd() takes effect: no-op.
456         nat.handleInterfaceRemoved(STACKED_IFACE);
457 
458         assertIdle(nat);
459 
460         verifyNoMoreInteractions(mNetd, mConnectivity);
461     }
462 
463     @Test
464     public void testStopDueToDisconnectBeforeClatdStarts() throws Exception {
465         checkStopBeforeClatdStarts(true);
466     }
467 
468     @Test
469     public void testStopDueToIpv4AddrBeforeClatdStarts() throws Exception {
470         checkStopBeforeClatdStarts(false);
471     }
472 
473     private void checkStopAndClatdNeverStarts(boolean dueToDisconnect) throws Exception {
474         Nat464Xlat nat = makeNat464Xlat(true);
475 
476         mNai.linkProperties.addLinkAddress(new LinkAddress("2001:db8::1/64"));
477 
478         nat.setNat64PrefixFromDns(new IpPrefix(NAT64_PREFIX));
479 
480         nat.start();
481 
482         verifyClatdStart(null /* inOrder */);
483 
484         // ConnectivityService immediately stops clat (Network disconnects, IPv4 addr appears, ...)
485         makeClatUnnecessary(dueToDisconnect);
486         nat.stop();
487 
488         verifyClatdStop(null /* inOrder */);
489         verify(mDnsResolver).stopPrefix64Discovery(eq(NETID));
490         assertIdle(nat);
491 
492         verifyNoMoreInteractions(mNetd, mConnectivity);
493     }
494 
495     @Test
496     public void testStopDueToDisconnectAndClatdNeverStarts() throws Exception {
497         checkStopAndClatdNeverStarts(true);
498     }
499 
500     @Test
501     public void testStopDueToIpv4AddressAndClatdNeverStarts() throws Exception {
502         checkStopAndClatdNeverStarts(false);
503     }
504 
505     @Test
506     public void testNat64PrefixPreference() throws Exception {
507         final IpPrefix prefixFromDns = new IpPrefix(NAT64_PREFIX);
508         final IpPrefix prefixFromRa = new IpPrefix(OTHER_NAT64_PREFIX);
509 
510         Nat464Xlat nat = makeNat464Xlat(true);
511 
512         final LinkProperties emptyLp = new LinkProperties();
513         LinkProperties fixedupLp;
514 
515         fixedupLp = new LinkProperties();
516         nat.setNat64PrefixFromDns(prefixFromDns);
517         nat.fixupLinkProperties(emptyLp, fixedupLp);
518         assertEquals(prefixFromDns, fixedupLp.getNat64Prefix());
519 
520         fixedupLp = new LinkProperties();
521         nat.setNat64PrefixFromRa(prefixFromRa);
522         nat.fixupLinkProperties(emptyLp, fixedupLp);
523         assertEquals(prefixFromRa, fixedupLp.getNat64Prefix());
524 
525         fixedupLp = new LinkProperties();
526         nat.setNat64PrefixFromRa(null);
527         nat.fixupLinkProperties(emptyLp, fixedupLp);
528         assertEquals(prefixFromDns, fixedupLp.getNat64Prefix());
529 
530         fixedupLp = new LinkProperties();
531         nat.setNat64PrefixFromRa(prefixFromRa);
532         nat.fixupLinkProperties(emptyLp, fixedupLp);
533         assertEquals(prefixFromRa, fixedupLp.getNat64Prefix());
534 
535         fixedupLp = new LinkProperties();
536         nat.setNat64PrefixFromDns(null);
537         nat.fixupLinkProperties(emptyLp, fixedupLp);
538         assertEquals(prefixFromRa, fixedupLp.getNat64Prefix());
539 
540         fixedupLp = new LinkProperties();
541         nat.setNat64PrefixFromRa(null);
542         nat.fixupLinkProperties(emptyLp, fixedupLp);
543         assertEquals(null, fixedupLp.getNat64Prefix());
544     }
545 
546     private void checkClatDisabledOnCellular(boolean onCellular) throws Exception {
547         // Disable 464xlat on cellular networks.
548         Nat464Xlat nat = makeNat464Xlat(false);
549         mNai.linkProperties.addLinkAddress(V6ADDR);
550         mNai.networkCapabilities.setTransportType(TRANSPORT_CELLULAR, onCellular);
551         nat.update();
552 
553         final IpPrefix nat64Prefix = new IpPrefix(NAT64_PREFIX);
554         if (onCellular) {
555             // Prefix discovery is never started.
556             verify(mDnsResolver, never()).startPrefix64Discovery(eq(NETID));
557             assertIdle(nat);
558 
559             // If a NAT64 prefix comes in from an RA, clat is not started either.
560             mNai.linkProperties.setNat64Prefix(nat64Prefix);
561             nat.setNat64PrefixFromRa(nat64Prefix);
562             nat.update();
563             verifyNeverClatdStart();
564             assertIdle(nat);
565         } else {
566             // Prefix discovery is started.
567             verify(mDnsResolver).startPrefix64Discovery(eq(NETID));
568             assertIdle(nat);
569 
570             // If a NAT64 prefix comes in from an RA, clat is started.
571             mNai.linkProperties.setNat64Prefix(nat64Prefix);
572             nat.setNat64PrefixFromRa(nat64Prefix);
573             nat.update();
574             verifyClatdStart(null /* inOrder */);
575             assertStarting(nat);
576         }
577     }
578 
579     @Test
580     public void testClatDisabledOnCellular() throws Exception {
581         checkClatDisabledOnCellular(true);
582     }
583 
584     @Test
585     public void testClatDisabledOnNonCellular() throws Exception {
586         checkClatDisabledOnCellular(false);
587     }
588 
589     static void assertIdle(Nat464Xlat nat) {
590         assertTrue("Nat464Xlat was not IDLE", !nat.isStarted());
591     }
592 
593     static void assertStarting(Nat464Xlat nat) {
594         assertTrue("Nat464Xlat was not STARTING", nat.isStarting());
595     }
596 
597     static void assertRunning(Nat464Xlat nat) {
598         assertTrue("Nat464Xlat was not RUNNING", nat.isRunning());
599     }
600 }
601