/* * Copyright (C) 2017 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.server.connectivity; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.mockito.Mockito.any; import static org.mockito.Mockito.eq; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyNoMoreInteractions; import static org.mockito.Mockito.when; import android.net.ConnectivityManager; import android.net.InterfaceConfiguration; import android.net.LinkAddress; import android.net.LinkProperties; import android.net.NetworkInfo; import android.os.Handler; import android.os.INetworkManagementService; import android.os.test.TestLooper; import android.support.test.filters.SmallTest; import android.support.test.runner.AndroidJUnit4; import com.android.server.ConnectivityService; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; import org.mockito.Mock; import org.mockito.MockitoAnnotations; @RunWith(AndroidJUnit4.class) @SmallTest public class Nat464XlatTest { static final String BASE_IFACE = "test0"; static final String STACKED_IFACE = "v4-test0"; static final LinkAddress ADDR = new LinkAddress("192.0.2.5/29"); @Mock ConnectivityService mConnectivity; @Mock INetworkManagementService mNms; @Mock InterfaceConfiguration mConfig; @Mock NetworkAgentInfo mNai; TestLooper mLooper; Handler mHandler; Nat464Xlat makeNat464Xlat() { return new Nat464Xlat(mNms, mNai); } @Before public void setUp() throws Exception { mLooper = new TestLooper(); mHandler = new Handler(mLooper.getLooper()); MockitoAnnotations.initMocks(this); mNai.linkProperties = new LinkProperties(); mNai.linkProperties.setInterfaceName(BASE_IFACE); mNai.networkInfo = new NetworkInfo(null); mNai.networkInfo.setType(ConnectivityManager.TYPE_WIFI); when(mNai.connService()).thenReturn(mConnectivity); when(mNai.handler()).thenReturn(mHandler); when(mNms.getInterfaceConfig(eq(STACKED_IFACE))).thenReturn(mConfig); when(mConfig.getLinkAddress()).thenReturn(ADDR); } @Test public void testNormalStartAndStop() throws Exception { Nat464Xlat nat = makeNat464Xlat(); ArgumentCaptor c = ArgumentCaptor.forClass(LinkProperties.class); // ConnectivityService starts clat. nat.start(); verify(mNms).registerObserver(eq(nat)); verify(mNms).startClatd(eq(BASE_IFACE)); // Stacked interface up notification arrives. nat.interfaceLinkStateChanged(STACKED_IFACE, true); mLooper.dispatchNext(); verify(mNms).getInterfaceConfig(eq(STACKED_IFACE)); verify(mNms).setInterfaceIpv6NdOffload(eq(BASE_IFACE), eq(false)); verify(mConnectivity).handleUpdateLinkProperties(eq(mNai), c.capture()); assertFalse(c.getValue().getStackedLinks().isEmpty()); assertTrue(c.getValue().getAllInterfaceNames().contains(STACKED_IFACE)); assertRunning(nat); // ConnectivityService stops clat (Network disconnects, IPv4 addr appears, ...). nat.stop(); verify(mNms).stopClatd(eq(BASE_IFACE)); verify(mNms).setInterfaceIpv6NdOffload(eq(BASE_IFACE), eq(true)); // Stacked interface removed notification arrives. nat.interfaceRemoved(STACKED_IFACE); mLooper.dispatchNext(); verify(mNms).unregisterObserver(eq(nat)); verify(mConnectivity, times(2)).handleUpdateLinkProperties(eq(mNai), c.capture()); assertTrue(c.getValue().getStackedLinks().isEmpty()); assertFalse(c.getValue().getAllInterfaceNames().contains(STACKED_IFACE)); assertIdle(nat); verifyNoMoreInteractions(mNms, mConnectivity); } @Test public void testClatdCrashWhileRunning() throws Exception { Nat464Xlat nat = makeNat464Xlat(); ArgumentCaptor c = ArgumentCaptor.forClass(LinkProperties.class); // ConnectivityService starts clat. nat.start(); verify(mNms).registerObserver(eq(nat)); verify(mNms).startClatd(eq(BASE_IFACE)); // Stacked interface up notification arrives. nat.interfaceLinkStateChanged(STACKED_IFACE, true); mLooper.dispatchNext(); verify(mNms).getInterfaceConfig(eq(STACKED_IFACE)); verify(mNms).setInterfaceIpv6NdOffload(eq(BASE_IFACE), eq(false)); verify(mConnectivity, times(1)).handleUpdateLinkProperties(eq(mNai), c.capture()); assertFalse(c.getValue().getStackedLinks().isEmpty()); assertTrue(c.getValue().getAllInterfaceNames().contains(STACKED_IFACE)); assertRunning(nat); // Stacked interface removed notification arrives (clatd crashed, ...). nat.interfaceRemoved(STACKED_IFACE); mLooper.dispatchNext(); verify(mNms).unregisterObserver(eq(nat)); verify(mNms).stopClatd(eq(BASE_IFACE)); verify(mNms).setInterfaceIpv6NdOffload(eq(BASE_IFACE), eq(true)); verify(mConnectivity, times(2)).handleUpdateLinkProperties(eq(mNai), c.capture()); assertTrue(c.getValue().getStackedLinks().isEmpty()); assertFalse(c.getValue().getAllInterfaceNames().contains(STACKED_IFACE)); assertIdle(nat); // ConnectivityService stops clat: no-op. nat.stop(); verifyNoMoreInteractions(mNms, mConnectivity); } @Test public void testStopBeforeClatdStarts() throws Exception { Nat464Xlat nat = makeNat464Xlat(); // ConnectivityService starts clat. nat.start(); verify(mNms).registerObserver(eq(nat)); verify(mNms).startClatd(eq(BASE_IFACE)); // ConnectivityService immediately stops clat (Network disconnects, IPv4 addr appears, ...) nat.stop(); verify(mNms).unregisterObserver(eq(nat)); verify(mNms).stopClatd(eq(BASE_IFACE)); 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(); assertIdle(nat); verifyNoMoreInteractions(mNms, mConnectivity); } @Test public void testStopAndClatdNeverStarts() throws Exception { Nat464Xlat nat = makeNat464Xlat(); // ConnectivityService starts clat. nat.start(); verify(mNms).registerObserver(eq(nat)); verify(mNms).startClatd(eq(BASE_IFACE)); // ConnectivityService immediately stops clat (Network disconnects, IPv4 addr appears, ...) nat.stop(); verify(mNms).unregisterObserver(eq(nat)); verify(mNms).stopClatd(eq(BASE_IFACE)); assertIdle(nat); verifyNoMoreInteractions(mNms, mConnectivity); } static void assertIdle(Nat464Xlat nat) { assertTrue("Nat464Xlat was not IDLE", !nat.isStarted()); } static void assertRunning(Nat464Xlat nat) { assertTrue("Nat464Xlat was not RUNNING", nat.isRunning()); } }