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