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