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