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 android.net.ip; 18 19 import static android.system.OsConstants.RT_SCOPE_UNIVERSE; 20 21 import static org.junit.Assert.assertArrayEquals; 22 import static org.junit.Assert.assertEquals; 23 import static org.junit.Assert.assertFalse; 24 import static org.junit.Assert.assertTrue; 25 import static org.junit.Assert.fail; 26 import static org.mockito.Mockito.any; 27 import static org.mockito.Mockito.anyString; 28 import static org.mockito.Mockito.doReturn; 29 import static org.mockito.Mockito.eq; 30 import static org.mockito.Mockito.never; 31 import static org.mockito.Mockito.reset; 32 import static org.mockito.Mockito.timeout; 33 import static org.mockito.Mockito.times; 34 import static org.mockito.Mockito.verify; 35 import static org.mockito.Mockito.verifyNoMoreInteractions; 36 import static org.mockito.Mockito.when; 37 38 import static java.util.Collections.emptySet; 39 40 import android.app.AlarmManager; 41 import android.content.ContentResolver; 42 import android.content.Context; 43 import android.content.res.Resources; 44 import android.net.ConnectivityManager; 45 import android.net.INetd; 46 import android.net.InetAddresses; 47 import android.net.IpPrefix; 48 import android.net.LinkAddress; 49 import android.net.LinkProperties; 50 import android.net.MacAddress; 51 import android.net.NetworkStackIpMemoryStore; 52 import android.net.RouteInfo; 53 import android.net.apf.ApfCapabilities; 54 import android.net.apf.ApfFilter.ApfConfiguration; 55 import android.net.ipmemorystore.NetworkAttributes; 56 import android.net.metrics.IpConnectivityLog; 57 import android.net.shared.InitialConfiguration; 58 import android.net.shared.ProvisioningConfiguration; 59 import android.net.util.InterfaceParams; 60 import android.os.Build; 61 62 import androidx.test.filters.SmallTest; 63 import androidx.test.runner.AndroidJUnit4; 64 65 import com.android.networkstack.R; 66 import com.android.server.NetworkObserver; 67 import com.android.server.NetworkObserverRegistry; 68 import com.android.server.NetworkStackService; 69 import com.android.server.connectivity.ipmemorystore.IpMemoryStoreService; 70 import com.android.testutils.DevSdkIgnoreRule; 71 import com.android.testutils.DevSdkIgnoreRule.IgnoreAfter; 72 import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo; 73 import com.android.testutils.HandlerUtils; 74 75 import org.junit.Before; 76 import org.junit.Rule; 77 import org.junit.Test; 78 import org.junit.runner.RunWith; 79 import org.mockito.ArgumentCaptor; 80 import org.mockito.Mock; 81 import org.mockito.MockitoAnnotations; 82 83 import java.net.Inet4Address; 84 import java.net.Inet6Address; 85 import java.net.InetAddress; 86 import java.util.Arrays; 87 import java.util.HashSet; 88 import java.util.List; 89 import java.util.Set; 90 91 92 /** 93 * Tests for IpClient. 94 */ 95 @RunWith(AndroidJUnit4.class) 96 @SmallTest 97 public class IpClientTest { 98 @Rule 99 public final DevSdkIgnoreRule mIgnoreRule = new DevSdkIgnoreRule(); 100 101 private static final String VALID = "VALID"; 102 private static final String INVALID = "INVALID"; 103 private static final String TEST_IFNAME = "test_wlan0"; 104 private static final int TEST_IFINDEX = 1001; 105 // See RFC 7042#section-2.1.2 for EUI-48 documentation values. 106 private static final MacAddress TEST_MAC = MacAddress.fromString("00:00:5E:00:53:01"); 107 private static final int TEST_TIMEOUT_MS = 400; 108 private static final String TEST_L2KEY = "some l2key"; 109 private static final String TEST_CLUSTER = "some cluster"; 110 111 private static final String TEST_GLOBAL_ADDRESS = "1234:4321::548d:2db2:4fcf:ef75/64"; 112 private static final String[] TEST_LOCAL_ADDRESSES = { 113 "fe80::a4be:f92:e1f7:22d1/64", 114 "fe80::f04a:8f6:6a32:d756/64", 115 "fd2c:4e57:8e3c:0:548d:2db2:4fcf:ef75/64" 116 }; 117 private static final String TEST_IPV4_LINKADDRESS = "192.168.42.24/24"; 118 private static final String[] TEST_PREFIXES = { "fe80::/64", "fd2c:4e57:8e3c::/64" }; 119 private static final String[] TEST_DNSES = { "fd2c:4e57:8e3c::42" }; 120 private static final String TEST_IPV6_GATEWAY = "fd2c:4e57:8e3c::43"; 121 private static final String TEST_IPV4_GATEWAY = "192.168.42.11"; 122 private static final long TEST_DNS_LIFETIME = 3600; 123 124 @Mock private Context mContext; 125 @Mock private ConnectivityManager mCm; 126 @Mock private NetworkObserverRegistry mObserverRegistry; 127 @Mock private INetd mNetd; 128 @Mock private Resources mResources; 129 @Mock private IIpClientCallbacks mCb; 130 @Mock private AlarmManager mAlarm; 131 @Mock private IpClient.Dependencies mDependencies; 132 @Mock private ContentResolver mContentResolver; 133 @Mock private NetworkStackService.NetworkStackServiceManager mNetworkStackServiceManager; 134 @Mock private NetworkStackIpMemoryStore mIpMemoryStore; 135 @Mock private IpMemoryStoreService mIpMemoryStoreService; 136 @Mock private InterfaceParams mInterfaceParams; 137 @Mock private IpConnectivityLog mMetricsLog; 138 139 private NetworkObserver mObserver; 140 private InterfaceParams mIfParams; 141 142 @Before setUp()143 public void setUp() throws Exception { 144 MockitoAnnotations.initMocks(this); 145 146 when(mContext.getSystemService(eq(Context.ALARM_SERVICE))).thenReturn(mAlarm); 147 when(mContext.getSystemService(eq(ConnectivityManager.class))).thenReturn(mCm); 148 when(mContext.getResources()).thenReturn(mResources); 149 when(mDependencies.getNetd(any())).thenReturn(mNetd); 150 when(mCm.shouldAvoidBadWifi()).thenReturn(true); 151 when(mContext.getContentResolver()).thenReturn(mContentResolver); 152 when(mNetworkStackServiceManager.getIpMemoryStoreService()) 153 .thenReturn(mIpMemoryStoreService); 154 when(mDependencies.getInterfaceParams(any())).thenReturn(mInterfaceParams); 155 when(mDependencies.getIpMemoryStore(mContext, mNetworkStackServiceManager)) 156 .thenReturn(mIpMemoryStore); 157 when(mDependencies.getIpConnectivityLog()).thenReturn(mMetricsLog); 158 159 mIfParams = null; 160 } 161 setTestInterfaceParams(String ifname)162 private void setTestInterfaceParams(String ifname) { 163 mIfParams = (ifname != null) 164 ? new InterfaceParams(ifname, TEST_IFINDEX, TEST_MAC) 165 : null; 166 when(mDependencies.getInterfaceParams(anyString())).thenReturn(mIfParams); 167 } 168 makeIpClient(String ifname)169 private IpClient makeIpClient(String ifname) throws Exception { 170 setTestInterfaceParams(ifname); 171 final IpClient ipc = new IpClient(mContext, ifname, mCb, mObserverRegistry, 172 mNetworkStackServiceManager, mDependencies); 173 verify(mNetd, timeout(TEST_TIMEOUT_MS).times(1)).interfaceSetEnableIPv6(ifname, false); 174 verify(mNetd, timeout(TEST_TIMEOUT_MS).times(1)).interfaceClearAddrs(ifname); 175 ArgumentCaptor<NetworkObserver> arg = ArgumentCaptor.forClass(NetworkObserver.class); 176 verify(mObserverRegistry, times(1)).registerObserverForNonblockingCallback(arg.capture()); 177 mObserver = arg.getValue(); 178 reset(mObserverRegistry); 179 reset(mNetd); 180 // Verify IpClient doesn't call onLinkPropertiesChange() when it starts. 181 verify(mCb, never()).onLinkPropertiesChange(any()); 182 reset(mCb); 183 return ipc; 184 } 185 makeEmptyLinkProperties(String iface)186 private static LinkProperties makeEmptyLinkProperties(String iface) { 187 final LinkProperties empty = new LinkProperties(); 188 empty.setInterfaceName(iface); 189 return empty; 190 } 191 verifyNetworkAttributesStored(final String l2Key, final NetworkAttributes attributes)192 private void verifyNetworkAttributesStored(final String l2Key, 193 final NetworkAttributes attributes) { 194 // TODO : when storing is implemented, turn this on 195 // verify(mIpMemoryStore).storeNetworkAttributes(eq(l2Key), eq(attributes), any()); 196 } 197 198 @Test testNullInterfaceNameMostDefinitelyThrows()199 public void testNullInterfaceNameMostDefinitelyThrows() throws Exception { 200 setTestInterfaceParams(null); 201 try { 202 final IpClient ipc = new IpClient(mContext, null, mCb, mObserverRegistry, 203 mNetworkStackServiceManager, mDependencies); 204 ipc.shutdown(); 205 fail(); 206 } catch (NullPointerException npe) { 207 // Phew; null interface names not allowed. 208 } 209 } 210 211 @Test testNullCallbackMostDefinitelyThrows()212 public void testNullCallbackMostDefinitelyThrows() throws Exception { 213 final String ifname = "lo"; 214 setTestInterfaceParams(ifname); 215 try { 216 final IpClient ipc = new IpClient(mContext, ifname, null, mObserverRegistry, 217 mNetworkStackServiceManager, mDependencies); 218 ipc.shutdown(); 219 fail(); 220 } catch (NullPointerException npe) { 221 // Phew; null callbacks not allowed. 222 } 223 } 224 225 @Test testInvalidInterfaceDoesNotThrow()226 public void testInvalidInterfaceDoesNotThrow() throws Exception { 227 setTestInterfaceParams(TEST_IFNAME); 228 final IpClient ipc = new IpClient(mContext, TEST_IFNAME, mCb, mObserverRegistry, 229 mNetworkStackServiceManager, mDependencies); 230 verifyNoMoreInteractions(mIpMemoryStore); 231 ipc.shutdown(); 232 } 233 234 @Test testInterfaceNotFoundFailsImmediately()235 public void testInterfaceNotFoundFailsImmediately() throws Exception { 236 setTestInterfaceParams(null); 237 final IpClient ipc = new IpClient(mContext, TEST_IFNAME, mCb, mObserverRegistry, 238 mNetworkStackServiceManager, mDependencies); 239 ipc.startProvisioning(new ProvisioningConfiguration()); 240 verify(mCb, timeout(TEST_TIMEOUT_MS).times(1)).onProvisioningFailure(any()); 241 verify(mIpMemoryStore, never()).storeNetworkAttributes(any(), any(), any()); 242 ipc.shutdown(); 243 } 244 makeIPv6ProvisionedLinkProperties()245 private LinkProperties makeIPv6ProvisionedLinkProperties() { 246 // Add local addresses, and a global address with global scope 247 final Set<LinkAddress> addresses = links(TEST_LOCAL_ADDRESSES); 248 addresses.add(new LinkAddress(TEST_GLOBAL_ADDRESS, 0, RT_SCOPE_UNIVERSE)); 249 250 // Add a route on the interface for each prefix, and a global route 251 final Set<RouteInfo> routes = routes(TEST_PREFIXES); 252 routes.add(defaultIPV6Route(TEST_IPV6_GATEWAY)); 253 254 return linkproperties(addresses, routes, ips(TEST_DNSES)); 255 } 256 doProvisioningWithDefaultConfiguration()257 private IpClient doProvisioningWithDefaultConfiguration() throws Exception { 258 final IpClient ipc = makeIpClient(TEST_IFNAME); 259 260 ProvisioningConfiguration config = new ProvisioningConfiguration.Builder() 261 .withoutIPv4() 262 // TODO: mock IpReachabilityMonitor's dependencies (NetworkInterface, PowerManager) 263 // and enable it in this test 264 .withoutIpReachabilityMonitor() 265 .build(); 266 267 ipc.startProvisioning(config); 268 verify(mCb, timeout(TEST_TIMEOUT_MS).times(1)).setNeighborDiscoveryOffload(true); 269 verify(mCb, timeout(TEST_TIMEOUT_MS).times(1)).setFallbackMulticastFilter(false); 270 271 final LinkProperties lp = makeIPv6ProvisionedLinkProperties(); 272 lp.getRoutes().forEach(mObserver::onRouteUpdated); 273 lp.getLinkAddresses().forEach(la -> mObserver.onInterfaceAddressUpdated(la, TEST_IFNAME)); 274 mObserver.onInterfaceDnsServerInfo(TEST_IFNAME, TEST_DNS_LIFETIME, 275 lp.getDnsServers().stream().map(InetAddress::getHostAddress) 276 .toArray(String[]::new)); 277 278 HandlerUtils.waitForIdle(ipc.getHandler(), TEST_TIMEOUT_MS); 279 verify(mCb, never()).onProvisioningFailure(any()); 280 verify(mIpMemoryStore, never()).storeNetworkAttributes(any(), any(), any()); 281 282 verify(mCb).onProvisioningSuccess(lp); 283 return ipc; 284 } 285 addIPv4Provisioning(LinkProperties lp)286 private void addIPv4Provisioning(LinkProperties lp) { 287 final LinkAddress la = new LinkAddress(TEST_IPV4_LINKADDRESS); 288 final RouteInfo defaultRoute = new RouteInfo(new IpPrefix(Inet4Address.ANY, 0), 289 InetAddresses.parseNumericAddress(TEST_IPV4_GATEWAY), TEST_IFNAME); 290 mObserver.onInterfaceAddressUpdated(la, TEST_IFNAME); 291 mObserver.onRouteUpdated(defaultRoute); 292 293 lp.addLinkAddress(la); 294 lp.addRoute(defaultRoute); 295 } 296 297 /** 298 * Simulate loss of IPv6 provisioning (default route lost). 299 * 300 * @return The expected new LinkProperties. 301 */ doIPv6ProvisioningLoss(LinkProperties lp)302 private void doIPv6ProvisioningLoss(LinkProperties lp) { 303 final RouteInfo defaultRoute = defaultIPV6Route(TEST_IPV6_GATEWAY); 304 mObserver.onRouteRemoved(defaultRoute); 305 306 lp.removeRoute(defaultRoute); 307 } 308 doDefaultIPv6ProvisioningConfigurationAndProvisioningLossTest(boolean avoidBadWifi)309 private void doDefaultIPv6ProvisioningConfigurationAndProvisioningLossTest(boolean avoidBadWifi) 310 throws Exception { 311 when(mCm.shouldAvoidBadWifi()).thenReturn(avoidBadWifi); 312 final IpClient ipc = doProvisioningWithDefaultConfiguration(); 313 final LinkProperties lp = makeIPv6ProvisionedLinkProperties(); 314 315 reset(mCb); 316 doIPv6ProvisioningLoss(lp); 317 HandlerUtils.waitForIdle(ipc.getHandler(), TEST_TIMEOUT_MS); 318 verify(mCb).onProvisioningFailure(lp); 319 verify(mCb).onLinkPropertiesChange(makeEmptyLinkProperties(TEST_IFNAME)); 320 321 verifyShutdown(ipc); 322 } 323 324 @Test testDefaultIPv6ProvisioningConfiguration_AvoidBadWifi()325 public void testDefaultIPv6ProvisioningConfiguration_AvoidBadWifi() throws Exception { 326 doDefaultIPv6ProvisioningConfigurationAndProvisioningLossTest(true /* avoidBadWifi */); 327 } 328 329 @Test testDefaultIPv6ProvisioningConfiguration_StayOnBadWifi()330 public void testDefaultIPv6ProvisioningConfiguration_StayOnBadWifi() throws Exception { 331 // Even when avoidBadWifi=false, if IPv6 only, loss of all provisioning causes 332 // onProvisioningFailure to be called. 333 doDefaultIPv6ProvisioningConfigurationAndProvisioningLossTest(false /* avoidBadWifi */); 334 } 335 doDefaultDualStackProvisioningConfigurationTest( boolean avoidBadWifi)336 private void doDefaultDualStackProvisioningConfigurationTest( 337 boolean avoidBadWifi) throws Exception { 338 when(mCm.shouldAvoidBadWifi()).thenReturn(avoidBadWifi); 339 final IpClient ipc = doProvisioningWithDefaultConfiguration(); 340 final LinkProperties lp = makeIPv6ProvisionedLinkProperties(); 341 addIPv4Provisioning(lp); 342 HandlerUtils.waitForIdle(ipc.getHandler(), TEST_TIMEOUT_MS); 343 344 reset(mCb); 345 doIPv6ProvisioningLoss(lp); 346 HandlerUtils.waitForIdle(ipc.getHandler(), TEST_TIMEOUT_MS); 347 if (avoidBadWifi) { // Provisioning failure is expected only when avoidBadWifi is true 348 verify(mCb).onProvisioningFailure(lp); 349 verify(mCb).onLinkPropertiesChange(makeEmptyLinkProperties(TEST_IFNAME)); 350 } else { 351 verify(mCb, never()).onProvisioningFailure(any()); 352 verify(mCb).onLinkPropertiesChange(lp); 353 } 354 355 verifyShutdown(ipc); 356 } 357 358 @Test testDefaultDualStackProvisioningConfiguration_AvoidBadWifi()359 public void testDefaultDualStackProvisioningConfiguration_AvoidBadWifi() throws Exception { 360 doDefaultDualStackProvisioningConfigurationTest(true /* avoidBadWifi */); 361 } 362 363 @Test testDefaultDualStackProvisioningConfiguration_StayOnBadWifi()364 public void testDefaultDualStackProvisioningConfiguration_StayOnBadWifi() throws Exception { 365 doDefaultDualStackProvisioningConfigurationTest(false /* avoidBadWifi */); 366 } 367 368 @Test testProvisioningWithInitialConfiguration()369 public void testProvisioningWithInitialConfiguration() throws Exception { 370 final String iface = TEST_IFNAME; 371 final IpClient ipc = makeIpClient(iface); 372 final String l2Key = TEST_L2KEY; 373 final String cluster = TEST_CLUSTER; 374 375 ProvisioningConfiguration config = new ProvisioningConfiguration.Builder() 376 .withoutIPv4() 377 .withoutIpReachabilityMonitor() 378 .withInitialConfiguration( 379 conf(links(TEST_LOCAL_ADDRESSES), prefixes(TEST_PREFIXES), ips())) 380 .build(); 381 382 ipc.startProvisioning(config); 383 verify(mCb, timeout(TEST_TIMEOUT_MS).times(1)).setNeighborDiscoveryOffload(true); 384 verify(mCb, timeout(TEST_TIMEOUT_MS).times(1)).setFallbackMulticastFilter(false); 385 verify(mCb, never()).onProvisioningFailure(any()); 386 ipc.setL2KeyAndCluster(l2Key, cluster); 387 388 for (String addr : TEST_LOCAL_ADDRESSES) { 389 String[] parts = addr.split("/"); 390 verify(mNetd, timeout(TEST_TIMEOUT_MS).times(1)) 391 .interfaceAddAddress(iface, parts[0], Integer.parseInt(parts[1])); 392 } 393 394 final int lastAddr = TEST_LOCAL_ADDRESSES.length - 1; 395 396 // Add N - 1 addresses 397 for (int i = 0; i < lastAddr; i++) { 398 mObserver.onInterfaceAddressUpdated(new LinkAddress(TEST_LOCAL_ADDRESSES[i]), iface); 399 verify(mCb, timeout(TEST_TIMEOUT_MS)).onLinkPropertiesChange(any()); 400 reset(mCb); 401 } 402 403 // Add Nth address 404 mObserver.onInterfaceAddressUpdated(new LinkAddress(TEST_LOCAL_ADDRESSES[lastAddr]), iface); 405 LinkProperties want = linkproperties(links(TEST_LOCAL_ADDRESSES), 406 routes(TEST_PREFIXES), emptySet() /* dnses */); 407 want.setInterfaceName(iface); 408 verify(mCb, timeout(TEST_TIMEOUT_MS).times(1)).onProvisioningSuccess(want); 409 verifyNetworkAttributesStored(l2Key, new NetworkAttributes.Builder() 410 .setCluster(cluster) 411 .build()); 412 } 413 verifyShutdown(IpClient ipc)414 private void verifyShutdown(IpClient ipc) throws Exception { 415 ipc.shutdown(); 416 verify(mNetd, timeout(TEST_TIMEOUT_MS).times(1)).interfaceSetEnableIPv6(TEST_IFNAME, false); 417 verify(mNetd, timeout(TEST_TIMEOUT_MS).times(1)).interfaceClearAddrs(TEST_IFNAME); 418 verify(mCb, timeout(TEST_TIMEOUT_MS).times(1)) 419 .onLinkPropertiesChange(makeEmptyLinkProperties(TEST_IFNAME)); 420 verifyNoMoreInteractions(mIpMemoryStore); 421 } 422 423 @Test testIsProvisioned()424 public void testIsProvisioned() throws Exception { 425 InitialConfiguration empty = conf(links(), prefixes()); 426 IsProvisionedTestCase[] testcases = { 427 // nothing 428 notProvisionedCase(links(), routes(), dns(), null), 429 notProvisionedCase(links(), routes(), dns(), empty), 430 431 // IPv4 432 provisionedCase(links("192.0.2.12/24"), routes(), dns(), empty), 433 434 // IPv6 435 notProvisionedCase( 436 links("fe80::a4be:f92:e1f7:22d1/64", "fd2c:4e57:8e3c:0:548d:2db2:4fcf:ef75/64"), 437 routes(), dns(), empty), 438 notProvisionedCase( 439 links("fe80::a4be:f92:e1f7:22d1/64", "fd2c:4e57:8e3c:0:548d:2db2:4fcf:ef75/64"), 440 routes("fe80::/64", "fd2c:4e57:8e3c::/64"), dns("fd00:1234:5678::1000"), empty), 441 provisionedCase( 442 links("2001:db8:dead:beef:f00::a0/64", "fe80::1/64"), 443 routes("::/0"), 444 dns("2001:db8:dead:beef:f00::02"), empty), 445 446 // Initial configuration 447 provisionedCase( 448 links("fe80::e1f7:22d1/64", "fd2c:4e57:8e3c:0:548d:2db2:4fcf:ef75/64"), 449 routes("fe80::/64", "fd2c:4e57:8e3c::/64"), 450 dns(), 451 conf(links("fe80::e1f7:22d1/64", "fd2c:4e57:8e3c:0:548d:2db2:4fcf:ef75/64"), 452 prefixes( "fe80::/64", "fd2c:4e57:8e3c::/64"), ips())) 453 }; 454 455 for (IsProvisionedTestCase testcase : testcases) { 456 if (IpClient.isProvisioned(testcase.lp, testcase.config) != testcase.isProvisioned) { 457 fail(testcase.errorMessage()); 458 } 459 } 460 } 461 462 static class IsProvisionedTestCase { 463 boolean isProvisioned; 464 LinkProperties lp; 465 InitialConfiguration config; 466 errorMessage()467 String errorMessage() { 468 return String.format("expected %s with config %s to be %s, but was %s", 469 lp, config, provisioned(isProvisioned), provisioned(!isProvisioned)); 470 } 471 provisioned(boolean isProvisioned)472 static String provisioned(boolean isProvisioned) { 473 return isProvisioned ? "provisioned" : "not provisioned"; 474 } 475 } 476 provisionedCase(Set<LinkAddress> lpAddrs, Set<RouteInfo> lpRoutes, Set<InetAddress> lpDns, InitialConfiguration config)477 static IsProvisionedTestCase provisionedCase(Set<LinkAddress> lpAddrs, Set<RouteInfo> lpRoutes, 478 Set<InetAddress> lpDns, InitialConfiguration config) { 479 return provisioningTest(true, lpAddrs, lpRoutes, lpDns, config); 480 } 481 notProvisionedCase(Set<LinkAddress> lpAddrs, Set<RouteInfo> lpRoutes, Set<InetAddress> lpDns, InitialConfiguration config)482 static IsProvisionedTestCase notProvisionedCase(Set<LinkAddress> lpAddrs, 483 Set<RouteInfo> lpRoutes, Set<InetAddress> lpDns, InitialConfiguration config) { 484 return provisioningTest(false, lpAddrs, lpRoutes, lpDns, config); 485 } 486 provisioningTest(boolean isProvisioned, Set<LinkAddress> lpAddrs, Set<RouteInfo> lpRoutes, Set<InetAddress> lpDns, InitialConfiguration config)487 static IsProvisionedTestCase provisioningTest(boolean isProvisioned, Set<LinkAddress> lpAddrs, 488 Set<RouteInfo> lpRoutes, Set<InetAddress> lpDns, InitialConfiguration config) { 489 IsProvisionedTestCase testcase = new IsProvisionedTestCase(); 490 testcase.isProvisioned = isProvisioned; 491 testcase.lp = makeEmptyLinkProperties(TEST_IFNAME); 492 testcase.lp.setLinkAddresses(lpAddrs); 493 for (RouteInfo route : lpRoutes) { 494 testcase.lp.addRoute(route); 495 } 496 for (InetAddress dns : lpDns) { 497 testcase.lp.addDnsServer(dns); 498 } 499 testcase.config = config; 500 return testcase; 501 } 502 503 @Test testInitialConfigurations()504 public void testInitialConfigurations() throws Exception { 505 InitialConfigurationTestCase[] testcases = { 506 validConf("valid IPv4 configuration", 507 links("192.0.2.12/24"), prefixes("192.0.2.0/24"), dns("192.0.2.2")), 508 validConf("another valid IPv4 configuration", 509 links("192.0.2.12/24"), prefixes("192.0.2.0/24"), dns()), 510 validConf("valid IPv6 configurations", 511 links("2001:db8:dead:beef:f00::a0/64", "fe80::1/64"), 512 prefixes("2001:db8:dead:beef::/64", "fe80::/64"), 513 dns("2001:db8:dead:beef:f00::02")), 514 validConf("valid IPv6 configurations", 515 links("fe80::1/64"), prefixes("fe80::/64"), dns()), 516 validConf("valid IPv6/v4 configuration", 517 links("2001:db8:dead:beef:f00::a0/48", "192.0.2.12/24"), 518 prefixes("2001:db8:dead:beef::/64", "192.0.2.0/24"), 519 dns("192.0.2.2", "2001:db8:dead:beef:f00::02")), 520 validConf("valid IPv6 configuration without any GUA.", 521 links("fd00:1234:5678::1/48"), 522 prefixes("fd00:1234:5678::/48"), 523 dns("fd00:1234:5678::1000")), 524 525 invalidConf("empty configuration", links(), prefixes(), dns()), 526 invalidConf("v4 addr and dns not in any prefix", 527 links("192.0.2.12/24"), prefixes("198.51.100.0/24"), dns("192.0.2.2")), 528 invalidConf("v4 addr not in any prefix", 529 links("198.51.2.12/24"), prefixes("198.51.100.0/24"), dns("192.0.2.2")), 530 invalidConf("v4 dns addr not in any prefix", 531 links("192.0.2.12/24"), prefixes("192.0.2.0/24"), dns("198.51.100.2")), 532 invalidConf("v6 addr not in any prefix", 533 links("2001:db8:dead:beef:f00::a0/64", "fe80::1/64"), 534 prefixes("2001:db8:dead:beef::/64"), 535 dns("2001:db8:dead:beef:f00::02")), 536 invalidConf("v6 dns addr not in any prefix", 537 links("fe80::1/64"), prefixes("fe80::/64"), dns("2001:db8:dead:beef:f00::02")), 538 invalidConf("default ipv6 route and no GUA", 539 links("fd01:1111:2222:3333::a0/128"), prefixes("::/0"), dns()), 540 invalidConf("invalid v6 prefix length", 541 links("2001:db8:dead:beef:f00::a0/128"), prefixes("2001:db8:dead:beef::/32"), 542 dns()), 543 invalidConf("another invalid v6 prefix length", 544 links("2001:db8:dead:beef:f00::a0/128"), prefixes("2001:db8:dead:beef::/72"), 545 dns()) 546 }; 547 548 for (InitialConfigurationTestCase testcase : testcases) { 549 if (testcase.config.isValid() != testcase.isValid) { 550 fail(testcase.errorMessage()); 551 } 552 } 553 } 554 555 static class InitialConfigurationTestCase { 556 String descr; 557 boolean isValid; 558 InitialConfiguration config; errorMessage()559 public String errorMessage() { 560 return String.format("%s: expected configuration %s to be %s, but was %s", 561 descr, config, validString(isValid), validString(!isValid)); 562 } validString(boolean isValid)563 static String validString(boolean isValid) { 564 return isValid ? VALID : INVALID; 565 } 566 } 567 validConf(String descr, Set<LinkAddress> links, Set<IpPrefix> prefixes, Set<InetAddress> dns)568 static InitialConfigurationTestCase validConf(String descr, Set<LinkAddress> links, 569 Set<IpPrefix> prefixes, Set<InetAddress> dns) { 570 return confTestCase(descr, true, conf(links, prefixes, dns)); 571 } 572 invalidConf(String descr, Set<LinkAddress> links, Set<IpPrefix> prefixes, Set<InetAddress> dns)573 static InitialConfigurationTestCase invalidConf(String descr, Set<LinkAddress> links, 574 Set<IpPrefix> prefixes, Set<InetAddress> dns) { 575 return confTestCase(descr, false, conf(links, prefixes, dns)); 576 } 577 confTestCase( String descr, boolean isValid, InitialConfiguration config)578 static InitialConfigurationTestCase confTestCase( 579 String descr, boolean isValid, InitialConfiguration config) { 580 InitialConfigurationTestCase testcase = new InitialConfigurationTestCase(); 581 testcase.descr = descr; 582 testcase.isValid = isValid; 583 testcase.config = config; 584 return testcase; 585 } 586 linkproperties(Set<LinkAddress> addresses, Set<RouteInfo> routes, Set<InetAddress> dnses)587 static LinkProperties linkproperties(Set<LinkAddress> addresses, 588 Set<RouteInfo> routes, Set<InetAddress> dnses) { 589 LinkProperties lp = makeEmptyLinkProperties(TEST_IFNAME); 590 lp.setLinkAddresses(addresses); 591 routes.forEach(lp::addRoute); 592 dnses.forEach(lp::addDnsServer); 593 return lp; 594 } 595 conf(Set<LinkAddress> links, Set<IpPrefix> prefixes)596 static InitialConfiguration conf(Set<LinkAddress> links, Set<IpPrefix> prefixes) { 597 return conf(links, prefixes, new HashSet<>()); 598 } 599 conf( Set<LinkAddress> links, Set<IpPrefix> prefixes, Set<InetAddress> dns)600 static InitialConfiguration conf( 601 Set<LinkAddress> links, Set<IpPrefix> prefixes, Set<InetAddress> dns) { 602 InitialConfiguration conf = new InitialConfiguration(); 603 conf.ipAddresses.addAll(links); 604 conf.directlyConnectedRoutes.addAll(prefixes); 605 conf.dnsServers.addAll(dns); 606 return conf; 607 } 608 routes(String... routes)609 static Set<RouteInfo> routes(String... routes) { 610 return mapIntoSet(routes, (r) -> new RouteInfo(new IpPrefix(r), null /* gateway */, 611 TEST_IFNAME)); 612 } 613 defaultIPV6Route(String gateway)614 static RouteInfo defaultIPV6Route(String gateway) { 615 return new RouteInfo(new IpPrefix(Inet6Address.ANY, 0), 616 InetAddresses.parseNumericAddress(gateway), TEST_IFNAME); 617 } 618 prefixes(String... prefixes)619 static Set<IpPrefix> prefixes(String... prefixes) { 620 return mapIntoSet(prefixes, IpPrefix::new); 621 } 622 links(String... addresses)623 static Set<LinkAddress> links(String... addresses) { 624 return mapIntoSet(addresses, LinkAddress::new); 625 } 626 ips(String... addresses)627 static Set<InetAddress> ips(String... addresses) { 628 return mapIntoSet(addresses, InetAddress::getByName); 629 } 630 dns(String... addresses)631 static Set<InetAddress> dns(String... addresses) { 632 return ips(addresses); 633 } 634 mapIntoSet(A[] in, Fn<A, B> fn)635 static <A, B> Set<B> mapIntoSet(A[] in, Fn<A, B> fn) { 636 Set<B> out = new HashSet<>(in.length); 637 for (A item : in) { 638 try { 639 out.add(fn.call(item)); 640 } catch (Exception e) { 641 throw new RuntimeException(e); 642 } 643 } 644 return out; 645 } 646 verifyApfFilterCreatedOnStart(IpClient ipc)647 private ApfConfiguration verifyApfFilterCreatedOnStart(IpClient ipc) { 648 ProvisioningConfiguration config = new ProvisioningConfiguration.Builder() 649 .withoutIPv4() 650 .withoutIpReachabilityMonitor() 651 .withInitialConfiguration( 652 conf(links(TEST_LOCAL_ADDRESSES), prefixes(TEST_PREFIXES), ips())) 653 .withApfCapabilities(new ApfCapabilities( 654 4 /* version */, 4096 /* maxProgramSize */, 4 /* format */)) 655 .build(); 656 657 ipc.startProvisioning(config); 658 final ArgumentCaptor<ApfConfiguration> configCaptor = ArgumentCaptor.forClass( 659 ApfConfiguration.class); 660 verify(mDependencies, timeout(TEST_TIMEOUT_MS)).maybeCreateApfFilter( 661 any(), configCaptor.capture(), any(), any()); 662 663 return configCaptor.getValue(); 664 } 665 666 @Test @IgnoreAfter(Build.VERSION_CODES.R) testApfConfiguration_R()667 public void testApfConfiguration_R() throws Exception { 668 final IpClient ipc = makeIpClient(TEST_IFNAME); 669 final ApfConfiguration config = verifyApfFilterCreatedOnStart(ipc); 670 671 assertEquals(ApfCapabilities.getApfDrop8023Frames(), config.ieee802_3Filter); 672 assertArrayEquals(ApfCapabilities.getApfEtherTypeBlackList(), config.ethTypeBlackList); 673 674 verify(mResources, never()).getBoolean(R.bool.config_apfDrop802_3Frames); 675 verify(mResources, never()).getIntArray(R.array.config_apfEthTypeDenyList); 676 677 verifyShutdown(ipc); 678 } 679 680 @Test @IgnoreUpTo(Build.VERSION_CODES.R) testApfConfiguration()681 public void testApfConfiguration() throws Exception { 682 doReturn(true).when(mResources).getBoolean(R.bool.config_apfDrop802_3Frames); 683 final int[] ethTypeDenyList = new int[] { 0x88A2, 0x88A4 }; 684 doReturn(ethTypeDenyList).when(mResources).getIntArray( 685 R.array.config_apfEthTypeDenyList); 686 687 final IpClient ipc = makeIpClient(TEST_IFNAME); 688 final ApfConfiguration config = verifyApfFilterCreatedOnStart(ipc); 689 690 assertTrue(config.ieee802_3Filter); 691 assertArrayEquals(ethTypeDenyList, config.ethTypeBlackList); 692 693 verifyShutdown(ipc); 694 } 695 696 @Test @IgnoreUpTo(Build.VERSION_CODES.R) testApfConfiguration_NoApfDrop8023Frames()697 public void testApfConfiguration_NoApfDrop8023Frames() throws Exception { 698 doReturn(false).when(mResources).getBoolean(R.bool.config_apfDrop802_3Frames); 699 final int[] ethTypeDenyList = new int[] { 0x88A3, 0x88A5 }; 700 doReturn(ethTypeDenyList).when(mResources).getIntArray( 701 R.array.config_apfEthTypeDenyList); 702 703 final IpClient ipc = makeIpClient(TEST_IFNAME); 704 final ApfConfiguration config = verifyApfFilterCreatedOnStart(ipc); 705 706 assertFalse(config.ieee802_3Filter); 707 assertArrayEquals(ethTypeDenyList, config.ethTypeBlackList); 708 709 verifyShutdown(ipc); 710 } 711 712 interface Fn<A,B> { call(A a)713 B call(A a) throws Exception; 714 } 715 716 @Test testAll()717 public void testAll() { 718 List<String> list1 = Arrays.asList(); 719 List<String> list2 = Arrays.asList("foo"); 720 List<String> list3 = Arrays.asList("bar", "baz"); 721 List<String> list4 = Arrays.asList("foo", "bar", "baz"); 722 723 assertTrue(InitialConfiguration.all(list1, (x) -> false)); 724 assertFalse(InitialConfiguration.all(list2, (x) -> false)); 725 assertTrue(InitialConfiguration.all(list3, (x) -> true)); 726 assertTrue(InitialConfiguration.all(list2, (x) -> x.charAt(0) == 'f')); 727 assertFalse(InitialConfiguration.all(list4, (x) -> x.charAt(0) == 'f')); 728 } 729 730 @Test testAny()731 public void testAny() { 732 List<String> list1 = Arrays.asList(); 733 List<String> list2 = Arrays.asList("foo"); 734 List<String> list3 = Arrays.asList("bar", "baz"); 735 List<String> list4 = Arrays.asList("foo", "bar", "baz"); 736 737 assertFalse(InitialConfiguration.any(list1, (x) -> true)); 738 assertTrue(InitialConfiguration.any(list2, (x) -> true)); 739 assertTrue(InitialConfiguration.any(list2, (x) -> x.charAt(0) == 'f')); 740 assertFalse(InitialConfiguration.any(list3, (x) -> x.charAt(0) == 'f')); 741 assertTrue(InitialConfiguration.any(list4, (x) -> x.charAt(0) == 'f')); 742 } 743 744 @Test testFindAll()745 public void testFindAll() { 746 List<String> list1 = Arrays.asList(); 747 List<String> list2 = Arrays.asList("foo"); 748 List<String> list3 = Arrays.asList("foo", "bar", "baz"); 749 750 assertEquals(list1, IpClient.findAll(list1, (x) -> true)); 751 assertEquals(list1, IpClient.findAll(list3, (x) -> false)); 752 assertEquals(list3, IpClient.findAll(list3, (x) -> true)); 753 assertEquals(list2, IpClient.findAll(list3, (x) -> x.charAt(0) == 'f')); 754 } 755 } 756