• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 org.junit.Assert.assertEquals;
20 import static org.junit.Assert.assertFalse;
21 import static org.junit.Assert.assertTrue;
22 import static org.mockito.Mockito.eq;
23 import static org.mockito.Mockito.inOrder;
24 import static org.mockito.Mockito.times;
25 import static org.mockito.Mockito.verify;
26 import static org.mockito.Mockito.verifyNoMoreInteractions;
27 import static org.mockito.Mockito.when;
28 
29 import android.net.ConnectivityManager;
30 import android.net.IDnsResolver;
31 import android.net.INetd;
32 import android.net.InterfaceConfiguration;
33 import android.net.IpPrefix;
34 import android.net.LinkAddress;
35 import android.net.LinkProperties;
36 import android.net.NetworkAgentConfig;
37 import android.net.NetworkInfo;
38 import android.os.Handler;
39 import android.os.INetworkManagementService;
40 import android.os.test.TestLooper;
41 
42 import androidx.test.filters.SmallTest;
43 import androidx.test.runner.AndroidJUnit4;
44 
45 import com.android.server.ConnectivityService;
46 
47 import org.junit.Before;
48 import org.junit.Test;
49 import org.junit.runner.RunWith;
50 import org.mockito.ArgumentCaptor;
51 import org.mockito.InOrder;
52 import org.mockito.Mock;
53 import org.mockito.MockitoAnnotations;
54 
55 @RunWith(AndroidJUnit4.class)
56 @SmallTest
57 public class Nat464XlatTest {
58 
59     static final String BASE_IFACE = "test0";
60     static final String STACKED_IFACE = "v4-test0";
61     static final LinkAddress V6ADDR = new LinkAddress("2001:db8:1::f00/64");
62     static final LinkAddress ADDR = new LinkAddress("192.0.2.5/29");
63     static final String NAT64_PREFIX = "64:ff9b::/96";
64     static final String OTHER_NAT64_PREFIX = "2001:db8:0:64::/96";
65     static final int NETID = 42;
66 
67     @Mock ConnectivityService mConnectivity;
68     @Mock IDnsResolver mDnsResolver;
69     @Mock INetd mNetd;
70     @Mock INetworkManagementService mNms;
71     @Mock InterfaceConfiguration mConfig;
72     @Mock NetworkAgentInfo mNai;
73 
74     TestLooper mLooper;
75     Handler mHandler;
76     NetworkAgentConfig mAgentConfig = new NetworkAgentConfig();
77 
makeNat464Xlat()78     Nat464Xlat makeNat464Xlat() {
79         return new Nat464Xlat(mNai, mNetd, mDnsResolver, mNms) {
80             @Override protected int getNetId() {
81                 return NETID;
82             }
83         };
84     }
85 
86     private void markNetworkConnected() {
87         mNai.networkInfo.setDetailedState(NetworkInfo.DetailedState.CONNECTED, "", "");
88     }
89 
90     private void markNetworkDisconnected() {
91         mNai.networkInfo.setDetailedState(NetworkInfo.DetailedState.DISCONNECTED, "", "");
92     }
93 
94     @Before
95     public void setUp() throws Exception {
96         mLooper = new TestLooper();
97         mHandler = new Handler(mLooper.getLooper());
98 
99         MockitoAnnotations.initMocks(this);
100 
101         mNai.linkProperties = new LinkProperties();
102         mNai.linkProperties.setInterfaceName(BASE_IFACE);
103         mNai.networkInfo = new NetworkInfo(null);
104         mNai.networkInfo.setType(ConnectivityManager.TYPE_WIFI);
105         markNetworkConnected();
106         when(mNai.connService()).thenReturn(mConnectivity);
107         when(mNai.netAgentConfig()).thenReturn(mAgentConfig);
108         when(mNai.handler()).thenReturn(mHandler);
109 
110         when(mNms.getInterfaceConfig(eq(STACKED_IFACE))).thenReturn(mConfig);
111         when(mConfig.getLinkAddress()).thenReturn(ADDR);
112     }
113 
114     private void assertRequiresClat(boolean expected, NetworkAgentInfo nai) {
115         String msg = String.format("requiresClat expected %b for type=%d state=%s skip=%b "
116                 + "nat64Prefix=%s addresses=%s", expected, nai.networkInfo.getType(),
117                 nai.networkInfo.getDetailedState(),
118                 mAgentConfig.skip464xlat, nai.linkProperties.getNat64Prefix(),
119                 nai.linkProperties.getLinkAddresses());
120         assertEquals(msg, expected, Nat464Xlat.requiresClat(nai));
121     }
122 
123     private void assertShouldStartClat(boolean expected, NetworkAgentInfo nai) {
124         String msg = String.format("shouldStartClat expected %b for type=%d state=%s skip=%b "
125                 + "nat64Prefix=%s addresses=%s", expected, nai.networkInfo.getType(),
126                 nai.networkInfo.getDetailedState(),
127                 mAgentConfig.skip464xlat, nai.linkProperties.getNat64Prefix(),
128                 nai.linkProperties.getLinkAddresses());
129         assertEquals(msg, expected, Nat464Xlat.shouldStartClat(nai));
130     }
131 
132     @Test
133     public void testRequiresClat() throws Exception {
134         final int[] supportedTypes = {
135             ConnectivityManager.TYPE_MOBILE,
136             ConnectivityManager.TYPE_WIFI,
137             ConnectivityManager.TYPE_ETHERNET,
138         };
139 
140         // NetworkInfo doesn't allow setting the State directly, but rather
141         // requires setting DetailedState in order set State as a side-effect.
142         final NetworkInfo.DetailedState[] supportedDetailedStates = {
143             NetworkInfo.DetailedState.CONNECTED,
144             NetworkInfo.DetailedState.SUSPENDED,
145         };
146 
147         LinkProperties oldLp = new LinkProperties(mNai.linkProperties);
148         for (int type : supportedTypes) {
149             mNai.networkInfo.setType(type);
150             for (NetworkInfo.DetailedState state : supportedDetailedStates) {
151                 mNai.networkInfo.setDetailedState(state, "reason", "extraInfo");
152 
153                 mNai.linkProperties.setNat64Prefix(new IpPrefix(OTHER_NAT64_PREFIX));
154                 assertRequiresClat(false, mNai);
155                 assertShouldStartClat(false, mNai);
156 
157                 mNai.linkProperties.addLinkAddress(new LinkAddress("fc00::1/64"));
158                 assertRequiresClat(false, mNai);
159                 assertShouldStartClat(false, mNai);
160 
161                 mNai.linkProperties.addLinkAddress(new LinkAddress("2001:db8::1/64"));
162                 assertRequiresClat(true, mNai);
163                 assertShouldStartClat(true, mNai);
164 
165                 mAgentConfig.skip464xlat = true;
166                 assertRequiresClat(false, mNai);
167                 assertShouldStartClat(false, mNai);
168 
169                 mAgentConfig.skip464xlat = false;
170                 assertRequiresClat(true, mNai);
171                 assertShouldStartClat(true, mNai);
172 
173                 mNai.linkProperties.addLinkAddress(new LinkAddress("192.0.2.2/24"));
174                 assertRequiresClat(false, mNai);
175                 assertShouldStartClat(false, mNai);
176 
177                 mNai.linkProperties.removeLinkAddress(new LinkAddress("192.0.2.2/24"));
178                 assertRequiresClat(true, mNai);
179                 assertShouldStartClat(true, mNai);
180 
181                 mNai.linkProperties.setNat64Prefix(null);
182                 assertRequiresClat(true, mNai);
183                 assertShouldStartClat(false, mNai);
184 
185                 mNai.linkProperties = new LinkProperties(oldLp);
186             }
187         }
188     }
189 
190     private void makeClatUnnecessary(boolean dueToDisconnect) {
191         if (dueToDisconnect) {
192             markNetworkDisconnected();
193         } else {
194             mNai.linkProperties.addLinkAddress(ADDR);
195         }
196     }
197 
198     private void checkNormalStartAndStop(boolean dueToDisconnect) throws Exception {
199         Nat464Xlat nat = makeNat464Xlat();
200         ArgumentCaptor<LinkProperties> c = ArgumentCaptor.forClass(LinkProperties.class);
201 
202         mNai.linkProperties.addLinkAddress(V6ADDR);
203 
204         nat.setNat64PrefixFromDns(new IpPrefix(NAT64_PREFIX));
205 
206         // Start clat.
207         nat.start();
208 
209         verify(mNms).registerObserver(eq(nat));
210         verify(mNetd).clatdStart(eq(BASE_IFACE), eq(NAT64_PREFIX));
211 
212         // Stacked interface up notification arrives.
213         nat.interfaceLinkStateChanged(STACKED_IFACE, true);
214         mLooper.dispatchNext();
215 
216         verify(mNms).getInterfaceConfig(eq(STACKED_IFACE));
217         verify(mConnectivity).handleUpdateLinkProperties(eq(mNai), c.capture());
218         assertFalse(c.getValue().getStackedLinks().isEmpty());
219         assertTrue(c.getValue().getAllInterfaceNames().contains(STACKED_IFACE));
220         assertRunning(nat);
221 
222         // Stop clat (Network disconnects, IPv4 addr appears, ...).
223         makeClatUnnecessary(dueToDisconnect);
224         nat.stop();
225 
226         verify(mNetd).clatdStop(eq(BASE_IFACE));
227         verify(mConnectivity, times(2)).handleUpdateLinkProperties(eq(mNai), c.capture());
228         verify(mNms).unregisterObserver(eq(nat));
229         assertTrue(c.getValue().getStackedLinks().isEmpty());
230         assertFalse(c.getValue().getAllInterfaceNames().contains(STACKED_IFACE));
231         verify(mDnsResolver).stopPrefix64Discovery(eq(NETID));
232         assertIdle(nat);
233 
234         // Stacked interface removed notification arrives and is ignored.
235         nat.interfaceRemoved(STACKED_IFACE);
236         mLooper.dispatchNext();
237 
238         verifyNoMoreInteractions(mNetd, mNms, mConnectivity);
239     }
240 
241     @Test
242     public void testNormalStartAndStopDueToDisconnect() throws Exception {
243         checkNormalStartAndStop(true);
244     }
245 
246     @Test
247     public void testNormalStartAndStopDueToIpv4Addr() throws Exception {
248         checkNormalStartAndStop(false);
249     }
250 
251     private void checkStartStopStart(boolean interfaceRemovedFirst) throws Exception {
252         Nat464Xlat nat = makeNat464Xlat();
253         ArgumentCaptor<LinkProperties> c = ArgumentCaptor.forClass(LinkProperties.class);
254         InOrder inOrder = inOrder(mNetd, mConnectivity);
255 
256         mNai.linkProperties.addLinkAddress(V6ADDR);
257 
258         nat.setNat64PrefixFromDns(new IpPrefix(NAT64_PREFIX));
259 
260         nat.start();
261 
262         inOrder.verify(mNetd).clatdStart(eq(BASE_IFACE), eq(NAT64_PREFIX));
263 
264         // Stacked interface up notification arrives.
265         nat.interfaceLinkStateChanged(STACKED_IFACE, true);
266         mLooper.dispatchNext();
267 
268         inOrder.verify(mConnectivity).handleUpdateLinkProperties(eq(mNai), c.capture());
269         assertFalse(c.getValue().getStackedLinks().isEmpty());
270         assertTrue(c.getValue().getAllInterfaceNames().contains(STACKED_IFACE));
271         assertRunning(nat);
272 
273         // ConnectivityService stops clat (Network disconnects, IPv4 addr appears, ...).
274         nat.stop();
275 
276         inOrder.verify(mNetd).clatdStop(eq(BASE_IFACE));
277 
278         inOrder.verify(mConnectivity, times(1)).handleUpdateLinkProperties(eq(mNai), c.capture());
279         assertTrue(c.getValue().getStackedLinks().isEmpty());
280         assertFalse(c.getValue().getAllInterfaceNames().contains(STACKED_IFACE));
281         assertIdle(nat);
282 
283         if (interfaceRemovedFirst) {
284             // Stacked interface removed notification arrives and is ignored.
285             nat.interfaceRemoved(STACKED_IFACE);
286             mLooper.dispatchNext();
287             nat.interfaceLinkStateChanged(STACKED_IFACE, false);
288             mLooper.dispatchNext();
289         }
290 
291         assertTrue(c.getValue().getStackedLinks().isEmpty());
292         assertFalse(c.getValue().getAllInterfaceNames().contains(STACKED_IFACE));
293         assertIdle(nat);
294         inOrder.verifyNoMoreInteractions();
295 
296         nat.start();
297 
298         inOrder.verify(mNetd).clatdStart(eq(BASE_IFACE), eq(NAT64_PREFIX));
299 
300         if (!interfaceRemovedFirst) {
301             // Stacked interface removed notification arrives and is ignored.
302             nat.interfaceRemoved(STACKED_IFACE);
303             mLooper.dispatchNext();
304             nat.interfaceLinkStateChanged(STACKED_IFACE, false);
305             mLooper.dispatchNext();
306         }
307 
308         // Stacked interface up notification arrives.
309         nat.interfaceLinkStateChanged(STACKED_IFACE, true);
310         mLooper.dispatchNext();
311 
312         inOrder.verify(mConnectivity).handleUpdateLinkProperties(eq(mNai), c.capture());
313         assertFalse(c.getValue().getStackedLinks().isEmpty());
314         assertTrue(c.getValue().getAllInterfaceNames().contains(STACKED_IFACE));
315         assertRunning(nat);
316 
317         // ConnectivityService stops clat again.
318         nat.stop();
319 
320         inOrder.verify(mNetd).clatdStop(eq(BASE_IFACE));
321 
322         inOrder.verify(mConnectivity, times(1)).handleUpdateLinkProperties(eq(mNai), c.capture());
323         assertTrue(c.getValue().getStackedLinks().isEmpty());
324         assertFalse(c.getValue().getAllInterfaceNames().contains(STACKED_IFACE));
325         assertIdle(nat);
326 
327         inOrder.verifyNoMoreInteractions();
328     }
329 
330     @Test
331     public void testStartStopStart() throws Exception {
332         checkStartStopStart(true);
333     }
334 
335     @Test
336     public void testStartStopStartBeforeInterfaceRemoved() throws Exception {
337         checkStartStopStart(false);
338     }
339 
340     @Test
341     public void testClatdCrashWhileRunning() throws Exception {
342         Nat464Xlat nat = makeNat464Xlat();
343         ArgumentCaptor<LinkProperties> c = ArgumentCaptor.forClass(LinkProperties.class);
344 
345         nat.setNat64PrefixFromDns(new IpPrefix(NAT64_PREFIX));
346 
347         nat.start();
348 
349         verify(mNms).registerObserver(eq(nat));
350         verify(mNetd).clatdStart(eq(BASE_IFACE), eq(NAT64_PREFIX));
351 
352         // Stacked interface up notification arrives.
353         nat.interfaceLinkStateChanged(STACKED_IFACE, true);
354         mLooper.dispatchNext();
355 
356         verify(mNms).getInterfaceConfig(eq(STACKED_IFACE));
357         verify(mConnectivity, times(1)).handleUpdateLinkProperties(eq(mNai), c.capture());
358         assertFalse(c.getValue().getStackedLinks().isEmpty());
359         assertTrue(c.getValue().getAllInterfaceNames().contains(STACKED_IFACE));
360         assertRunning(nat);
361 
362         // Stacked interface removed notification arrives (clatd crashed, ...).
363         nat.interfaceRemoved(STACKED_IFACE);
364         mLooper.dispatchNext();
365 
366         verify(mNetd).clatdStop(eq(BASE_IFACE));
367         verify(mConnectivity, times(2)).handleUpdateLinkProperties(eq(mNai), c.capture());
368         verify(mNms).unregisterObserver(eq(nat));
369         verify(mDnsResolver).stopPrefix64Discovery(eq(NETID));
370         assertTrue(c.getValue().getStackedLinks().isEmpty());
371         assertFalse(c.getValue().getAllInterfaceNames().contains(STACKED_IFACE));
372         assertIdle(nat);
373 
374         // ConnectivityService stops clat: no-op.
375         nat.stop();
376 
377         verifyNoMoreInteractions(mNetd, mNms, mConnectivity);
378     }
379 
380     private void checkStopBeforeClatdStarts(boolean dueToDisconnect) throws Exception {
381         Nat464Xlat nat = makeNat464Xlat();
382 
383         mNai.linkProperties.addLinkAddress(new LinkAddress("2001:db8::1/64"));
384 
385         nat.setNat64PrefixFromDns(new IpPrefix(NAT64_PREFIX));
386 
387         nat.start();
388 
389         verify(mNms).registerObserver(eq(nat));
390         verify(mNetd).clatdStart(eq(BASE_IFACE), eq(NAT64_PREFIX));
391 
392         // ConnectivityService immediately stops clat (Network disconnects, IPv4 addr appears, ...)
393         makeClatUnnecessary(dueToDisconnect);
394         nat.stop();
395 
396         verify(mNetd).clatdStop(eq(BASE_IFACE));
397         verify(mNms).unregisterObserver(eq(nat));
398         verify(mDnsResolver).stopPrefix64Discovery(eq(NETID));
399         assertIdle(nat);
400 
401         // In-flight interface up notification arrives: no-op
402         nat.interfaceLinkStateChanged(STACKED_IFACE, true);
403         mLooper.dispatchNext();
404 
405         // Interface removed notification arrives after stopClatd() takes effect: no-op.
406         nat.interfaceRemoved(STACKED_IFACE);
407         mLooper.dispatchNext();
408 
409         assertIdle(nat);
410 
411         verifyNoMoreInteractions(mNetd, mNms, mConnectivity);
412     }
413 
414     @Test
415     public void testStopDueToDisconnectBeforeClatdStarts() throws Exception {
416         checkStopBeforeClatdStarts(true);
417     }
418 
419     @Test
420     public void testStopDueToIpv4AddrBeforeClatdStarts() throws Exception {
421         checkStopBeforeClatdStarts(false);
422     }
423 
424     private void checkStopAndClatdNeverStarts(boolean dueToDisconnect) throws Exception {
425         Nat464Xlat nat = makeNat464Xlat();
426 
427         mNai.linkProperties.addLinkAddress(new LinkAddress("2001:db8::1/64"));
428 
429         nat.setNat64PrefixFromDns(new IpPrefix(NAT64_PREFIX));
430 
431         nat.start();
432 
433         verify(mNms).registerObserver(eq(nat));
434         verify(mNetd).clatdStart(eq(BASE_IFACE), eq(NAT64_PREFIX));
435 
436         // ConnectivityService immediately stops clat (Network disconnects, IPv4 addr appears, ...)
437         makeClatUnnecessary(dueToDisconnect);
438         nat.stop();
439 
440         verify(mNetd).clatdStop(eq(BASE_IFACE));
441         verify(mNms).unregisterObserver(eq(nat));
442         verify(mDnsResolver).stopPrefix64Discovery(eq(NETID));
443         assertIdle(nat);
444 
445         verifyNoMoreInteractions(mNetd, mNms, mConnectivity);
446     }
447 
448     @Test
449     public void testStopDueToDisconnectAndClatdNeverStarts() throws Exception {
450         checkStopAndClatdNeverStarts(true);
451     }
452 
453     @Test
454     public void testStopDueToIpv4AddressAndClatdNeverStarts() throws Exception {
455         checkStopAndClatdNeverStarts(false);
456     }
457 
458     @Test
459     public void testNat64PrefixPreference() throws Exception {
460         final IpPrefix prefixFromDns = new IpPrefix(NAT64_PREFIX);
461         final IpPrefix prefixFromRa = new IpPrefix(OTHER_NAT64_PREFIX);
462 
463         Nat464Xlat nat = makeNat464Xlat();
464 
465         final LinkProperties emptyLp = new LinkProperties();
466         LinkProperties fixedupLp;
467 
468         fixedupLp = new LinkProperties();
469         nat.setNat64PrefixFromDns(prefixFromDns);
470         nat.fixupLinkProperties(emptyLp, fixedupLp);
471         assertEquals(prefixFromDns, fixedupLp.getNat64Prefix());
472 
473         fixedupLp = new LinkProperties();
474         nat.setNat64PrefixFromRa(prefixFromRa);
475         nat.fixupLinkProperties(emptyLp, fixedupLp);
476         assertEquals(prefixFromRa, fixedupLp.getNat64Prefix());
477 
478         fixedupLp = new LinkProperties();
479         nat.setNat64PrefixFromRa(null);
480         nat.fixupLinkProperties(emptyLp, fixedupLp);
481         assertEquals(prefixFromDns, fixedupLp.getNat64Prefix());
482 
483         fixedupLp = new LinkProperties();
484         nat.setNat64PrefixFromRa(prefixFromRa);
485         nat.fixupLinkProperties(emptyLp, fixedupLp);
486         assertEquals(prefixFromRa, fixedupLp.getNat64Prefix());
487 
488         fixedupLp = new LinkProperties();
489         nat.setNat64PrefixFromDns(null);
490         nat.fixupLinkProperties(emptyLp, fixedupLp);
491         assertEquals(prefixFromRa, fixedupLp.getNat64Prefix());
492 
493         fixedupLp = new LinkProperties();
494         nat.setNat64PrefixFromRa(null);
495         nat.fixupLinkProperties(emptyLp, fixedupLp);
496         assertEquals(null, fixedupLp.getNat64Prefix());
497     }
498 
499     static void assertIdle(Nat464Xlat nat) {
500         assertTrue("Nat464Xlat was not IDLE", !nat.isStarted());
501     }
502 
503     static void assertRunning(Nat464Xlat nat) {
504         assertTrue("Nat464Xlat was not RUNNING", nat.isRunning());
505     }
506 }
507