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