• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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