Home | History | Annotate | Download | only in connectivity
      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.NetworkInfo;
     37 import android.net.NetworkMisc;
     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 ADDR = new LinkAddress("192.0.2.5/29");
     62     static final String NAT64_PREFIX = "64:ff9b::/96";
     63     static final int NETID = 42;
     64 
     65     @Mock ConnectivityService mConnectivity;
     66     @Mock NetworkMisc mMisc;
     67     @Mock IDnsResolver mDnsResolver;
     68     @Mock INetd mNetd;
     69     @Mock INetworkManagementService mNms;
     70     @Mock InterfaceConfiguration mConfig;
     71     @Mock NetworkAgentInfo mNai;
     72 
     73     TestLooper mLooper;
     74     Handler mHandler;
     75 
     76     Nat464Xlat makeNat464Xlat() {
     77         return new Nat464Xlat(mNai, mNetd, mDnsResolver, mNms) {
     78             @Override protected int getNetId() {
     79                 return NETID;
     80             }
     81         };
     82     }
     83 
     84     @Before
     85     public void setUp() throws Exception {
     86         mLooper = new TestLooper();
     87         mHandler = new Handler(mLooper.getLooper());
     88 
     89         MockitoAnnotations.initMocks(this);
     90 
     91         mNai.linkProperties = new LinkProperties();
     92         mNai.linkProperties.setInterfaceName(BASE_IFACE);
     93         mNai.networkInfo = new NetworkInfo(null);
     94         mNai.networkInfo.setType(ConnectivityManager.TYPE_WIFI);
     95         when(mNai.connService()).thenReturn(mConnectivity);
     96         when(mNai.netMisc()).thenReturn(mMisc);
     97         when(mNai.handler()).thenReturn(mHandler);
     98 
     99         when(mNms.getInterfaceConfig(eq(STACKED_IFACE))).thenReturn(mConfig);
    100         when(mConfig.getLinkAddress()).thenReturn(ADDR);
    101     }
    102 
    103     private void assertRequiresClat(boolean expected, NetworkAgentInfo nai) {
    104         String msg = String.format("requiresClat expected %b for type=%d state=%s skip=%b "
    105                 + "nat64Prefix=%s addresses=%s", expected, nai.networkInfo.getType(),
    106                 nai.networkInfo.getDetailedState(),
    107                 mMisc.skip464xlat, nai.linkProperties.getNat64Prefix(),
    108                 nai.linkProperties.getLinkAddresses());
    109         assertEquals(msg, expected, Nat464Xlat.requiresClat(nai));
    110     }
    111 
    112     private void assertShouldStartClat(boolean expected, NetworkAgentInfo nai) {
    113         String msg = String.format("shouldStartClat expected %b for type=%d state=%s skip=%b "
    114                 + "nat64Prefix=%s addresses=%s", expected, nai.networkInfo.getType(),
    115                 nai.networkInfo.getDetailedState(),
    116                 mMisc.skip464xlat, nai.linkProperties.getNat64Prefix(),
    117                 nai.linkProperties.getLinkAddresses());
    118         assertEquals(msg, expected, Nat464Xlat.shouldStartClat(nai));
    119     }
    120 
    121     @Test
    122     public void testRequiresClat() throws Exception {
    123         final int[] supportedTypes = {
    124             ConnectivityManager.TYPE_MOBILE,
    125             ConnectivityManager.TYPE_WIFI,
    126             ConnectivityManager.TYPE_ETHERNET,
    127         };
    128 
    129         // NetworkInfo doesn't allow setting the State directly, but rather
    130         // requires setting DetailedState in order set State as a side-effect.
    131         final NetworkInfo.DetailedState[] supportedDetailedStates = {
    132             NetworkInfo.DetailedState.CONNECTED,
    133             NetworkInfo.DetailedState.SUSPENDED,
    134         };
    135 
    136         LinkProperties oldLp = new LinkProperties(mNai.linkProperties);
    137         for (int type : supportedTypes) {
    138             mNai.networkInfo.setType(type);
    139             for (NetworkInfo.DetailedState state : supportedDetailedStates) {
    140                 mNai.networkInfo.setDetailedState(state, "reason", "extraInfo");
    141 
    142                 mNai.linkProperties.setNat64Prefix(new IpPrefix("2001:db8:0:64::/96"));
    143                 assertRequiresClat(false, mNai);
    144                 assertShouldStartClat(false, mNai);
    145 
    146                 mNai.linkProperties.addLinkAddress(new LinkAddress("fc00::1/64"));
    147                 assertRequiresClat(false, mNai);
    148                 assertShouldStartClat(false, mNai);
    149 
    150                 mNai.linkProperties.addLinkAddress(new LinkAddress("2001:db8::1/64"));
    151                 assertRequiresClat(true, mNai);
    152                 assertShouldStartClat(true, mNai);
    153 
    154                 mMisc.skip464xlat = true;
    155                 assertRequiresClat(false, mNai);
    156                 assertShouldStartClat(false, mNai);
    157 
    158                 mMisc.skip464xlat = false;
    159                 assertRequiresClat(true, mNai);
    160                 assertShouldStartClat(true, mNai);
    161 
    162                 mNai.linkProperties.addLinkAddress(new LinkAddress("192.0.2.2/24"));
    163                 assertRequiresClat(false, mNai);
    164                 assertShouldStartClat(false, mNai);
    165 
    166                 mNai.linkProperties.removeLinkAddress(new LinkAddress("192.0.2.2/24"));
    167                 assertRequiresClat(true, mNai);
    168                 assertShouldStartClat(true, mNai);
    169 
    170                 mNai.linkProperties.setNat64Prefix(null);
    171                 assertRequiresClat(true, mNai);
    172                 assertShouldStartClat(false, mNai);
    173 
    174                 mNai.linkProperties = new LinkProperties(oldLp);
    175             }
    176         }
    177     }
    178 
    179     @Test
    180     public void testNormalStartAndStop() throws Exception {
    181         Nat464Xlat nat = makeNat464Xlat();
    182         ArgumentCaptor<LinkProperties> c = ArgumentCaptor.forClass(LinkProperties.class);
    183 
    184         nat.setNat64Prefix(new IpPrefix(NAT64_PREFIX));
    185 
    186         // Start clat.
    187         nat.start();
    188 
    189         verify(mNms).registerObserver(eq(nat));
    190         verify(mNetd).clatdStart(eq(BASE_IFACE), eq(NAT64_PREFIX));
    191 
    192         // Stacked interface up notification arrives.
    193         nat.interfaceLinkStateChanged(STACKED_IFACE, true);
    194         mLooper.dispatchNext();
    195 
    196         verify(mNms).getInterfaceConfig(eq(STACKED_IFACE));
    197         verify(mConnectivity).handleUpdateLinkProperties(eq(mNai), c.capture());
    198         assertFalse(c.getValue().getStackedLinks().isEmpty());
    199         assertTrue(c.getValue().getAllInterfaceNames().contains(STACKED_IFACE));
    200         assertRunning(nat);
    201 
    202         // Stop clat (Network disconnects, IPv4 addr appears, ...).
    203         nat.stop();
    204 
    205         verify(mNetd).clatdStop(eq(BASE_IFACE));
    206         verify(mConnectivity, times(2)).handleUpdateLinkProperties(eq(mNai), c.capture());
    207         verify(mNms).unregisterObserver(eq(nat));
    208         assertTrue(c.getValue().getStackedLinks().isEmpty());
    209         assertFalse(c.getValue().getAllInterfaceNames().contains(STACKED_IFACE));
    210         verify(mDnsResolver).stopPrefix64Discovery(eq(NETID));
    211         assertIdle(nat);
    212 
    213         // Stacked interface removed notification arrives and is ignored.
    214         nat.interfaceRemoved(STACKED_IFACE);
    215         mLooper.dispatchNext();
    216 
    217         verifyNoMoreInteractions(mNetd, mNms, mConnectivity);
    218     }
    219 
    220     private void checkStartStopStart(boolean interfaceRemovedFirst) throws Exception {
    221         Nat464Xlat nat = makeNat464Xlat();
    222         ArgumentCaptor<LinkProperties> c = ArgumentCaptor.forClass(LinkProperties.class);
    223         InOrder inOrder = inOrder(mNetd, mConnectivity);
    224 
    225         nat.setNat64Prefix(new IpPrefix(NAT64_PREFIX));
    226 
    227         nat.start();
    228 
    229         inOrder.verify(mNetd).clatdStart(eq(BASE_IFACE), eq(NAT64_PREFIX));
    230 
    231         // Stacked interface up notification arrives.
    232         nat.interfaceLinkStateChanged(STACKED_IFACE, true);
    233         mLooper.dispatchNext();
    234 
    235         inOrder.verify(mConnectivity).handleUpdateLinkProperties(eq(mNai), c.capture());
    236         assertFalse(c.getValue().getStackedLinks().isEmpty());
    237         assertTrue(c.getValue().getAllInterfaceNames().contains(STACKED_IFACE));
    238         assertRunning(nat);
    239 
    240         // ConnectivityService stops clat (Network disconnects, IPv4 addr appears, ...).
    241         nat.stop();
    242 
    243         inOrder.verify(mNetd).clatdStop(eq(BASE_IFACE));
    244 
    245         inOrder.verify(mConnectivity, times(1)).handleUpdateLinkProperties(eq(mNai), c.capture());
    246         assertTrue(c.getValue().getStackedLinks().isEmpty());
    247         assertFalse(c.getValue().getAllInterfaceNames().contains(STACKED_IFACE));
    248         assertIdle(nat);
    249 
    250         if (interfaceRemovedFirst) {
    251             // Stacked interface removed notification arrives and is ignored.
    252             nat.interfaceRemoved(STACKED_IFACE);
    253             mLooper.dispatchNext();
    254             nat.interfaceLinkStateChanged(STACKED_IFACE, false);
    255             mLooper.dispatchNext();
    256         }
    257 
    258         assertTrue(c.getValue().getStackedLinks().isEmpty());
    259         assertFalse(c.getValue().getAllInterfaceNames().contains(STACKED_IFACE));
    260         assertIdle(nat);
    261         inOrder.verifyNoMoreInteractions();
    262 
    263         nat.start();
    264 
    265         inOrder.verify(mNetd).clatdStart(eq(BASE_IFACE), eq(NAT64_PREFIX));
    266 
    267         if (!interfaceRemovedFirst) {
    268             // Stacked interface removed notification arrives and is ignored.
    269             nat.interfaceRemoved(STACKED_IFACE);
    270             mLooper.dispatchNext();
    271             nat.interfaceLinkStateChanged(STACKED_IFACE, false);
    272             mLooper.dispatchNext();
    273         }
    274 
    275         // Stacked interface up notification arrives.
    276         nat.interfaceLinkStateChanged(STACKED_IFACE, true);
    277         mLooper.dispatchNext();
    278 
    279         inOrder.verify(mConnectivity).handleUpdateLinkProperties(eq(mNai), c.capture());
    280         assertFalse(c.getValue().getStackedLinks().isEmpty());
    281         assertTrue(c.getValue().getAllInterfaceNames().contains(STACKED_IFACE));
    282         assertRunning(nat);
    283 
    284         // ConnectivityService stops clat again.
    285         nat.stop();
    286 
    287         inOrder.verify(mNetd).clatdStop(eq(BASE_IFACE));
    288 
    289         inOrder.verify(mConnectivity, times(1)).handleUpdateLinkProperties(eq(mNai), c.capture());
    290         assertTrue(c.getValue().getStackedLinks().isEmpty());
    291         assertFalse(c.getValue().getAllInterfaceNames().contains(STACKED_IFACE));
    292         assertIdle(nat);
    293 
    294         inOrder.verifyNoMoreInteractions();
    295     }
    296 
    297     @Test
    298     public void testStartStopStart() throws Exception {
    299         checkStartStopStart(true);
    300     }
    301 
    302     @Test
    303     public void testStartStopStartBeforeInterfaceRemoved() throws Exception {
    304         checkStartStopStart(false);
    305     }
    306 
    307     @Test
    308     public void testClatdCrashWhileRunning() throws Exception {
    309         Nat464Xlat nat = makeNat464Xlat();
    310         ArgumentCaptor<LinkProperties> c = ArgumentCaptor.forClass(LinkProperties.class);
    311 
    312         nat.setNat64Prefix(new IpPrefix(NAT64_PREFIX));
    313 
    314         nat.start();
    315 
    316         verify(mNms).registerObserver(eq(nat));
    317         verify(mNetd).clatdStart(eq(BASE_IFACE), eq(NAT64_PREFIX));
    318 
    319         // Stacked interface up notification arrives.
    320         nat.interfaceLinkStateChanged(STACKED_IFACE, true);
    321         mLooper.dispatchNext();
    322 
    323         verify(mNms).getInterfaceConfig(eq(STACKED_IFACE));
    324         verify(mConnectivity, times(1)).handleUpdateLinkProperties(eq(mNai), c.capture());
    325         assertFalse(c.getValue().getStackedLinks().isEmpty());
    326         assertTrue(c.getValue().getAllInterfaceNames().contains(STACKED_IFACE));
    327         assertRunning(nat);
    328 
    329         // Stacked interface removed notification arrives (clatd crashed, ...).
    330         nat.interfaceRemoved(STACKED_IFACE);
    331         mLooper.dispatchNext();
    332 
    333         verify(mNetd).clatdStop(eq(BASE_IFACE));
    334         verify(mConnectivity, times(2)).handleUpdateLinkProperties(eq(mNai), c.capture());
    335         verify(mNms).unregisterObserver(eq(nat));
    336         verify(mDnsResolver).stopPrefix64Discovery(eq(NETID));
    337         assertTrue(c.getValue().getStackedLinks().isEmpty());
    338         assertFalse(c.getValue().getAllInterfaceNames().contains(STACKED_IFACE));
    339         assertIdle(nat);
    340 
    341         // ConnectivityService stops clat: no-op.
    342         nat.stop();
    343 
    344         verifyNoMoreInteractions(mNetd, mNms, mConnectivity);
    345     }
    346 
    347     @Test
    348     public void testStopBeforeClatdStarts() throws Exception {
    349         Nat464Xlat nat = makeNat464Xlat();
    350 
    351         nat.setNat64Prefix(new IpPrefix(NAT64_PREFIX));
    352 
    353         nat.start();
    354 
    355         verify(mNms).registerObserver(eq(nat));
    356         verify(mNetd).clatdStart(eq(BASE_IFACE), eq(NAT64_PREFIX));
    357 
    358         // ConnectivityService immediately stops clat (Network disconnects, IPv4 addr appears, ...)
    359         nat.stop();
    360 
    361         verify(mNetd).clatdStop(eq(BASE_IFACE));
    362         verify(mNms).unregisterObserver(eq(nat));
    363         verify(mDnsResolver).stopPrefix64Discovery(eq(NETID));
    364         assertIdle(nat);
    365 
    366         // In-flight interface up notification arrives: no-op
    367         nat.interfaceLinkStateChanged(STACKED_IFACE, true);
    368         mLooper.dispatchNext();
    369 
    370         // Interface removed notification arrives after stopClatd() takes effect: no-op.
    371         nat.interfaceRemoved(STACKED_IFACE);
    372         mLooper.dispatchNext();
    373 
    374         assertIdle(nat);
    375 
    376         verifyNoMoreInteractions(mNetd, mNms, mConnectivity);
    377     }
    378 
    379     @Test
    380     public void testStopAndClatdNeverStarts() throws Exception {
    381         Nat464Xlat nat = makeNat464Xlat();
    382 
    383         nat.setNat64Prefix(new IpPrefix(NAT64_PREFIX));
    384 
    385         nat.start();
    386 
    387         verify(mNms).registerObserver(eq(nat));
    388         verify(mNetd).clatdStart(eq(BASE_IFACE), eq(NAT64_PREFIX));
    389 
    390         // ConnectivityService immediately stops clat (Network disconnects, IPv4 addr appears, ...)
    391         nat.stop();
    392 
    393         verify(mNetd).clatdStop(eq(BASE_IFACE));
    394         verify(mNms).unregisterObserver(eq(nat));
    395         verify(mDnsResolver).stopPrefix64Discovery(eq(NETID));
    396         assertIdle(nat);
    397 
    398         verifyNoMoreInteractions(mNetd, mNms, mConnectivity);
    399     }
    400 
    401     static void assertIdle(Nat464Xlat nat) {
    402         assertTrue("Nat464Xlat was not IDLE", !nat.isStarted());
    403     }
    404 
    405     static void assertRunning(Nat464Xlat nat) {
    406         assertTrue("Nat464Xlat was not RUNNING", nat.isRunning());
    407     }
    408 }
    409