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