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.assertFalse; 20 import static org.junit.Assert.assertNull; 21 import static org.junit.Assert.assertTrue; 22 import static org.mockito.Mockito.any; 23 import static org.mockito.Mockito.eq; 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.InterfaceConfiguration; 31 import android.net.LinkAddress; 32 import android.net.LinkProperties; 33 import android.net.NetworkInfo; 34 import android.os.Handler; 35 import android.os.INetworkManagementService; 36 import android.os.test.TestLooper; 37 import android.support.test.filters.SmallTest; 38 import android.support.test.runner.AndroidJUnit4; 39 40 import com.android.server.ConnectivityService; 41 42 import org.junit.Before; 43 import org.junit.Test; 44 import org.junit.runner.RunWith; 45 import org.mockito.ArgumentCaptor; 46 import org.mockito.Mock; 47 import org.mockito.MockitoAnnotations; 48 49 @RunWith(AndroidJUnit4.class) 50 @SmallTest 51 public class Nat464XlatTest { 52 53 static final String BASE_IFACE = "test0"; 54 static final String STACKED_IFACE = "v4-test0"; 55 static final LinkAddress ADDR = new LinkAddress("192.0.2.5/29"); 56 57 @Mock ConnectivityService mConnectivity; 58 @Mock INetworkManagementService mNms; 59 @Mock InterfaceConfiguration mConfig; 60 @Mock NetworkAgentInfo mNai; 61 62 TestLooper mLooper; 63 Handler mHandler; 64 makeNat464Xlat()65 Nat464Xlat makeNat464Xlat() { 66 return new Nat464Xlat(mNms, mNai); 67 } 68 69 @Before setUp()70 public void setUp() throws Exception { 71 mLooper = new TestLooper(); 72 mHandler = new Handler(mLooper.getLooper()); 73 74 MockitoAnnotations.initMocks(this); 75 76 mNai.linkProperties = new LinkProperties(); 77 mNai.linkProperties.setInterfaceName(BASE_IFACE); 78 mNai.networkInfo = new NetworkInfo(null); 79 mNai.networkInfo.setType(ConnectivityManager.TYPE_WIFI); 80 when(mNai.connService()).thenReturn(mConnectivity); 81 when(mNai.handler()).thenReturn(mHandler); 82 83 when(mNms.getInterfaceConfig(eq(STACKED_IFACE))).thenReturn(mConfig); 84 when(mConfig.getLinkAddress()).thenReturn(ADDR); 85 } 86 87 @Test testNormalStartAndStop()88 public void testNormalStartAndStop() throws Exception { 89 Nat464Xlat nat = makeNat464Xlat(); 90 ArgumentCaptor<LinkProperties> c = ArgumentCaptor.forClass(LinkProperties.class); 91 92 // ConnectivityService starts clat. 93 nat.start(); 94 95 verify(mNms).registerObserver(eq(nat)); 96 verify(mNms).startClatd(eq(BASE_IFACE)); 97 98 // Stacked interface up notification arrives. 99 nat.interfaceLinkStateChanged(STACKED_IFACE, true); 100 mLooper.dispatchNext(); 101 102 verify(mNms).getInterfaceConfig(eq(STACKED_IFACE)); 103 verify(mNms).setInterfaceIpv6NdOffload(eq(BASE_IFACE), eq(false)); 104 verify(mConnectivity).handleUpdateLinkProperties(eq(mNai), c.capture()); 105 assertFalse(c.getValue().getStackedLinks().isEmpty()); 106 assertTrue(c.getValue().getAllInterfaceNames().contains(STACKED_IFACE)); 107 assertRunning(nat); 108 109 // ConnectivityService stops clat (Network disconnects, IPv4 addr appears, ...). 110 nat.stop(); 111 112 verify(mNms).stopClatd(eq(BASE_IFACE)); 113 verify(mNms).setInterfaceIpv6NdOffload(eq(BASE_IFACE), eq(true)); 114 115 // Stacked interface removed notification arrives. 116 nat.interfaceRemoved(STACKED_IFACE); 117 mLooper.dispatchNext(); 118 119 verify(mNms).unregisterObserver(eq(nat)); 120 verify(mConnectivity, times(2)).handleUpdateLinkProperties(eq(mNai), c.capture()); 121 assertTrue(c.getValue().getStackedLinks().isEmpty()); 122 assertFalse(c.getValue().getAllInterfaceNames().contains(STACKED_IFACE)); 123 assertIdle(nat); 124 125 verifyNoMoreInteractions(mNms, mConnectivity); 126 } 127 128 @Test testClatdCrashWhileRunning()129 public void testClatdCrashWhileRunning() throws Exception { 130 Nat464Xlat nat = makeNat464Xlat(); 131 ArgumentCaptor<LinkProperties> c = ArgumentCaptor.forClass(LinkProperties.class); 132 133 // ConnectivityService starts clat. 134 nat.start(); 135 136 verify(mNms).registerObserver(eq(nat)); 137 verify(mNms).startClatd(eq(BASE_IFACE)); 138 139 // Stacked interface up notification arrives. 140 nat.interfaceLinkStateChanged(STACKED_IFACE, true); 141 mLooper.dispatchNext(); 142 143 verify(mNms).getInterfaceConfig(eq(STACKED_IFACE)); 144 verify(mNms).setInterfaceIpv6NdOffload(eq(BASE_IFACE), eq(false)); 145 verify(mConnectivity, times(1)).handleUpdateLinkProperties(eq(mNai), c.capture()); 146 assertFalse(c.getValue().getStackedLinks().isEmpty()); 147 assertTrue(c.getValue().getAllInterfaceNames().contains(STACKED_IFACE)); 148 assertRunning(nat); 149 150 // Stacked interface removed notification arrives (clatd crashed, ...). 151 nat.interfaceRemoved(STACKED_IFACE); 152 mLooper.dispatchNext(); 153 154 verify(mNms).unregisterObserver(eq(nat)); 155 verify(mNms).stopClatd(eq(BASE_IFACE)); 156 verify(mNms).setInterfaceIpv6NdOffload(eq(BASE_IFACE), eq(true)); 157 verify(mConnectivity, times(2)).handleUpdateLinkProperties(eq(mNai), c.capture()); 158 assertTrue(c.getValue().getStackedLinks().isEmpty()); 159 assertFalse(c.getValue().getAllInterfaceNames().contains(STACKED_IFACE)); 160 assertIdle(nat); 161 162 // ConnectivityService stops clat: no-op. 163 nat.stop(); 164 165 verifyNoMoreInteractions(mNms, mConnectivity); 166 } 167 168 @Test testStopBeforeClatdStarts()169 public void testStopBeforeClatdStarts() throws Exception { 170 Nat464Xlat nat = makeNat464Xlat(); 171 172 // ConnectivityService starts clat. 173 nat.start(); 174 175 verify(mNms).registerObserver(eq(nat)); 176 verify(mNms).startClatd(eq(BASE_IFACE)); 177 178 // ConnectivityService immediately stops clat (Network disconnects, IPv4 addr appears, ...) 179 nat.stop(); 180 181 verify(mNms).unregisterObserver(eq(nat)); 182 verify(mNms).stopClatd(eq(BASE_IFACE)); 183 assertIdle(nat); 184 185 // In-flight interface up notification arrives: no-op 186 nat.interfaceLinkStateChanged(STACKED_IFACE, true); 187 mLooper.dispatchNext(); 188 189 190 // Interface removed notification arrives after stopClatd() takes effect: no-op. 191 nat.interfaceRemoved(STACKED_IFACE); 192 mLooper.dispatchNext(); 193 194 assertIdle(nat); 195 196 verifyNoMoreInteractions(mNms, mConnectivity); 197 } 198 199 @Test testStopAndClatdNeverStarts()200 public void testStopAndClatdNeverStarts() throws Exception { 201 Nat464Xlat nat = makeNat464Xlat(); 202 203 // ConnectivityService starts clat. 204 nat.start(); 205 206 verify(mNms).registerObserver(eq(nat)); 207 verify(mNms).startClatd(eq(BASE_IFACE)); 208 209 // ConnectivityService immediately stops clat (Network disconnects, IPv4 addr appears, ...) 210 nat.stop(); 211 212 verify(mNms).unregisterObserver(eq(nat)); 213 verify(mNms).stopClatd(eq(BASE_IFACE)); 214 assertIdle(nat); 215 216 verifyNoMoreInteractions(mNms, mConnectivity); 217 } 218 assertIdle(Nat464Xlat nat)219 static void assertIdle(Nat464Xlat nat) { 220 assertTrue("Nat464Xlat was not IDLE", !nat.isStarted()); 221 } 222 assertRunning(Nat464Xlat nat)223 static void assertRunning(Nat464Xlat nat) { 224 assertTrue("Nat464Xlat was not RUNNING", nat.isRunning()); 225 } 226 } 227