Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit b10095a4 authored by Remi NGUYEN VAN's avatar Remi NGUYEN VAN
Browse files

Add tests for IPv6 provisioning loss

Verify loss of IPv6-only provisioning, and IPv6 provisioning when
dual-stack, depending on the avoidBadWifi setting.

Test: atest android.net.ip.IpClientTest
Change-Id: Iefd1f12b5bc638a0ccc48e3eb8c271993754f223
parent 1ef8fd87
Loading
Loading
Loading
Loading
+154 −37
Original line number Diff line number Diff line
@@ -16,6 +16,8 @@

package android.net.ip;

import static android.system.OsConstants.RT_SCOPE_UNIVERSE;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
@@ -31,12 +33,15 @@ import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;

import static java.util.Collections.emptySet;

import android.app.AlarmManager;
import android.content.ContentResolver;
import android.content.Context;
import android.content.res.Resources;
import android.net.ConnectivityManager;
import android.net.INetd;
import android.net.InetAddresses;
import android.net.IpPrefix;
import android.net.LinkAddress;
import android.net.LinkProperties;
@@ -51,11 +56,11 @@ import android.net.util.InterfaceParams;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;

import com.android.internal.R;
import com.android.server.NetworkObserver;
import com.android.server.NetworkObserverRegistry;
import com.android.server.NetworkStackService;
import com.android.server.connectivity.ipmemorystore.IpMemoryStoreService;
import com.android.testutils.HandlerUtilsKt;

import org.junit.Before;
import org.junit.Test;
@@ -64,6 +69,8 @@ import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;

import java.net.Inet4Address;
import java.net.Inet6Address;
import java.net.InetAddress;
import java.util.Arrays;
import java.util.HashSet;
@@ -77,8 +84,6 @@ import java.util.Set;
@RunWith(AndroidJUnit4.class)
@SmallTest
public class IpClientTest {
    private static final int DEFAULT_AVOIDBADWIFI_CONFIG_VALUE = 1;

    private static final String VALID = "VALID";
    private static final String INVALID = "INVALID";
    private static final String TEST_IFNAME = "test_wlan0";
@@ -89,6 +94,19 @@ public class IpClientTest {
    private static final String TEST_L2KEY = "some l2key";
    private static final String TEST_GROUPHINT = "some grouphint";

    private static final String TEST_GLOBAL_ADDRESS = "1234:4321::548d:2db2:4fcf:ef75/64";
    private static final String[] TEST_LOCAL_ADDRESSES = {
            "fe80::a4be:f92:e1f7:22d1/64",
            "fe80::f04a:8f6:6a32:d756/64",
            "fd2c:4e57:8e3c:0:548d:2db2:4fcf:ef75/64"
    };
    private static final String TEST_IPV4_LINKADDRESS = "192.168.42.24/24";
    private static final String[] TEST_PREFIXES = { "fe80::/64", "fd2c:4e57:8e3c::/64" };
    private static final String[] TEST_DNSES = { "fd2c:4e57:8e3c::42" };
    private static final String TEST_IPV6_GATEWAY = "fd2c:4e57:8e3c::43";
    private static final String TEST_IPV4_GATEWAY = "192.168.42.11";
    private static final long TEST_DNS_LIFETIME = 3600;

    @Mock private Context mContext;
    @Mock private ConnectivityManager mCm;
    @Mock private NetworkObserverRegistry mObserverRegistry;
@@ -114,8 +132,7 @@ public class IpClientTest {
        when(mContext.getSystemService(eq(ConnectivityManager.class))).thenReturn(mCm);
        when(mContext.getResources()).thenReturn(mResources);
        when(mDependencies.getNetd(any())).thenReturn(mNetd);
        when(mResources.getInteger(R.integer.config_networkAvoidBadWifi))
                .thenReturn(DEFAULT_AVOIDBADWIFI_CONFIG_VALUE);
        when(mCm.shouldAvoidBadWifi()).thenReturn(true);
        when(mContext.getContentResolver()).thenReturn(mContentResolver);
        when(mNetworkStackServiceManager.getIpMemoryStoreService())
                .thenReturn(mIpMemoryStoreService);
@@ -209,10 +226,20 @@ public class IpClientTest {
        ipc.shutdown();
    }

    @Test
    public void testDefaultProvisioningConfiguration() throws Exception {
        final String iface = TEST_IFNAME;
        final IpClient ipc = makeIpClient(iface);
    private LinkProperties makeIPv6ProvisionedLinkProperties() {
        // Add local addresses, and a global address with global scope
        final Set<LinkAddress> addresses = links(TEST_LOCAL_ADDRESSES);
        addresses.add(new LinkAddress(TEST_GLOBAL_ADDRESS, 0, RT_SCOPE_UNIVERSE));

        // Add a route on the interface for each prefix, and a global route
        final Set<RouteInfo> routes = routes(TEST_PREFIXES);
        routes.add(defaultIPV6Route(TEST_IPV6_GATEWAY));

        return linkproperties(addresses, routes, ips(TEST_DNSES));
    }

    private IpClient doProvisioningWithDefaultConfiguration() throws Exception {
        final IpClient ipc = makeIpClient(TEST_IFNAME);

        ProvisioningConfiguration config = new ProvisioningConfiguration.Builder()
                .withoutIPv4()
@@ -224,34 +251,115 @@ public class IpClientTest {
        ipc.startProvisioning(config);
        verify(mCb, times(1)).setNeighborDiscoveryOffload(true);
        verify(mCb, timeout(TEST_TIMEOUT_MS).times(1)).setFallbackMulticastFilter(false);

        final LinkProperties lp = makeIPv6ProvisionedLinkProperties();
        lp.getRoutes().forEach(mObserver::onRouteUpdated);
        lp.getLinkAddresses().forEach(la -> mObserver.onInterfaceAddressUpdated(la, TEST_IFNAME));
        mObserver.onInterfaceDnsServerInfo(TEST_IFNAME, TEST_DNS_LIFETIME,
                lp.getDnsServers().stream().map(InetAddress::getHostAddress)
                        .toArray(String[]::new));

        HandlerUtilsKt.waitForIdle(ipc.getHandler(), TEST_TIMEOUT_MS);
        verify(mCb, never()).onProvisioningFailure(any());
        verify(mIpMemoryStore, never()).storeNetworkAttributes(any(), any(), any());

        ipc.shutdown();
        verify(mNetd, timeout(TEST_TIMEOUT_MS).times(1)).interfaceSetEnableIPv6(iface, false);
        verify(mNetd, timeout(TEST_TIMEOUT_MS).times(1)).interfaceClearAddrs(iface);
        verify(mCb, timeout(TEST_TIMEOUT_MS).times(1))
                .onLinkPropertiesChange(makeEmptyLinkProperties(iface));
        verify(mCb).onProvisioningSuccess(lp);
        return ipc;
    }

    private void addIPv4Provisioning(LinkProperties lp) {
        final LinkAddress la = new LinkAddress(TEST_IPV4_LINKADDRESS);
        final RouteInfo defaultRoute = new RouteInfo(new IpPrefix(Inet4Address.ANY, 0),
                InetAddresses.parseNumericAddress(TEST_IPV4_GATEWAY), TEST_IFNAME);
        mObserver.onInterfaceAddressUpdated(la, TEST_IFNAME);
        mObserver.onRouteUpdated(defaultRoute);

        lp.addLinkAddress(la);
        lp.addRoute(defaultRoute);
    }

    /**
     * Simulate loss of IPv6 provisioning (default route lost).
     *
     * @return The expected new LinkProperties.
     */
    private void doIPv6ProvisioningLoss(LinkProperties lp) {
        final RouteInfo defaultRoute = defaultIPV6Route(TEST_IPV6_GATEWAY);
        mObserver.onRouteRemoved(defaultRoute);

        lp.removeRoute(defaultRoute);
    }

    private void doDefaultIPv6ProvisioningConfigurationAndProvisioningLossTest(boolean avoidBadWifi)
            throws Exception {
        when(mCm.shouldAvoidBadWifi()).thenReturn(avoidBadWifi);
        final IpClient ipc = doProvisioningWithDefaultConfiguration();
        final LinkProperties lp = makeIPv6ProvisionedLinkProperties();

        reset(mCb);
        doIPv6ProvisioningLoss(lp);
        HandlerUtilsKt.waitForIdle(ipc.getHandler(), TEST_TIMEOUT_MS);
        verify(mCb).onProvisioningFailure(lp);
        verify(mCb).onLinkPropertiesChange(makeEmptyLinkProperties(TEST_IFNAME));

        verifyShutdown(ipc);
    }

    @Test
    public void testDefaultIPv6ProvisioningConfiguration_AvoidBadWifi() throws Exception {
        doDefaultIPv6ProvisioningConfigurationAndProvisioningLossTest(true /* avoidBadWifi */);
    }

    @Test
    public void testDefaultIPv6ProvisioningConfiguration_StayOnBadWifi() throws Exception {
        // Even when avoidBadWifi=false, if IPv6 only, loss of all provisioning causes
        // onProvisioningFailure to be called.
        doDefaultIPv6ProvisioningConfigurationAndProvisioningLossTest(false /* avoidBadWifi */);
    }

    private void doDefaultDualStackProvisioningConfigurationTest(
            boolean avoidBadWifi) throws Exception {
        when(mCm.shouldAvoidBadWifi()).thenReturn(avoidBadWifi);
        final IpClient ipc = doProvisioningWithDefaultConfiguration();
        final LinkProperties lp = makeIPv6ProvisionedLinkProperties();
        addIPv4Provisioning(lp);
        HandlerUtilsKt.waitForIdle(ipc.getHandler(), TEST_TIMEOUT_MS);

        reset(mCb);
        doIPv6ProvisioningLoss(lp);
        HandlerUtilsKt.waitForIdle(ipc.getHandler(), TEST_TIMEOUT_MS);
        if (avoidBadWifi) { // Provisioning failure is expected only when avoidBadWifi is true
            verify(mCb).onProvisioningFailure(lp);
            verify(mCb).onLinkPropertiesChange(makeEmptyLinkProperties(TEST_IFNAME));
        } else {
            verify(mCb, never()).onProvisioningFailure(any());
            verify(mCb).onLinkPropertiesChange(lp);
        }

        verifyShutdown(ipc);
    }

    @Test
    public void testDefaultDualStackProvisioningConfiguration_AvoidBadWifi() throws Exception {
        doDefaultDualStackProvisioningConfigurationTest(true /* avoidBadWifi */);
    }

    @Test
    public void testDefaultDualStackProvisioningConfiguration_StayOnBadWifi() throws Exception {
        doDefaultDualStackProvisioningConfigurationTest(false /* avoidBadWifi */);
    }

    public void testProvisioningWithInitialConfiguration() throws Exception {
        final String iface = TEST_IFNAME;
        final IpClient ipc = makeIpClient(iface);
        final String l2Key = TEST_L2KEY;
        final String groupHint = TEST_GROUPHINT;

        String[] addresses = {
            "fe80::a4be:f92:e1f7:22d1/64",
            "fe80::f04a:8f6:6a32:d756/64",
            "fd2c:4e57:8e3c:0:548d:2db2:4fcf:ef75/64"
        };
        String[] prefixes = { "fe80::/64", "fd2c:4e57:8e3c::/64" };

        ProvisioningConfiguration config = new ProvisioningConfiguration.Builder()
                .withoutIPv4()
                .withoutIpReachabilityMonitor()
                .withInitialConfiguration(conf(links(addresses), prefixes(prefixes), ips()))
                .withInitialConfiguration(
                        conf(links(TEST_LOCAL_ADDRESSES), prefixes(TEST_PREFIXES), ips()))
                .build();

        ipc.startProvisioning(config);
@@ -260,35 +368,38 @@ public class IpClientTest {
        verify(mCb, never()).onProvisioningFailure(any());
        ipc.setL2KeyAndGroupHint(l2Key, groupHint);

        for (String addr : addresses) {
        for (String addr : TEST_LOCAL_ADDRESSES) {
            String[] parts = addr.split("/");
            verify(mNetd, timeout(TEST_TIMEOUT_MS).times(1))
                    .interfaceAddAddress(iface, parts[0], Integer.parseInt(parts[1]));
        }

        final int lastAddr = addresses.length - 1;
        final int lastAddr = TEST_LOCAL_ADDRESSES.length - 1;

        // Add N - 1 addresses
        for (int i = 0; i < lastAddr; i++) {
            mObserver.onInterfaceAddressUpdated(new LinkAddress(addresses[i]), iface);
            mObserver.onInterfaceAddressUpdated(new LinkAddress(TEST_LOCAL_ADDRESSES[i]), iface);
            verify(mCb, timeout(TEST_TIMEOUT_MS)).onLinkPropertiesChange(any());
            reset(mCb);
        }

        // Add Nth address
        mObserver.onInterfaceAddressUpdated(new LinkAddress(addresses[lastAddr]), iface);
        LinkProperties want = linkproperties(links(addresses), routes(prefixes));
        mObserver.onInterfaceAddressUpdated(new LinkAddress(TEST_LOCAL_ADDRESSES[lastAddr]), iface);
        LinkProperties want = linkproperties(links(TEST_LOCAL_ADDRESSES),
                routes(TEST_PREFIXES), emptySet() /* dnses */);
        want.setInterfaceName(iface);
        verify(mCb, timeout(TEST_TIMEOUT_MS).times(1)).onProvisioningSuccess(want);
        verifyNetworkAttributesStored(l2Key, new NetworkAttributes.Builder()
                .setGroupHint(groupHint)
                .build());
    }

    private void verifyShutdown(IpClient ipc) throws Exception {
        ipc.shutdown();
        verify(mNetd, timeout(TEST_TIMEOUT_MS).times(1)).interfaceSetEnableIPv6(iface, false);
        verify(mNetd, timeout(TEST_TIMEOUT_MS).times(1)).interfaceClearAddrs(iface);
        verify(mNetd, timeout(TEST_TIMEOUT_MS).times(1)).interfaceSetEnableIPv6(TEST_IFNAME, false);
        verify(mNetd, timeout(TEST_TIMEOUT_MS).times(1)).interfaceClearAddrs(TEST_IFNAME);
        verify(mCb, timeout(TEST_TIMEOUT_MS).times(1))
                .onLinkPropertiesChange(makeEmptyLinkProperties(iface));
                .onLinkPropertiesChange(makeEmptyLinkProperties(TEST_IFNAME));
        verifyNoMoreInteractions(mIpMemoryStore);
    }

@@ -360,7 +471,7 @@ public class IpClientTest {
            Set<RouteInfo> lpRoutes, Set<InetAddress> lpDns, InitialConfiguration config) {
        IsProvisionedTestCase testcase = new IsProvisionedTestCase();
        testcase.isProvisioned = isProvisioned;
        testcase.lp = new LinkProperties();
        testcase.lp = makeEmptyLinkProperties(TEST_IFNAME);
        testcase.lp.setLinkAddresses(lpAddrs);
        for (RouteInfo route : lpRoutes) {
            testcase.lp.addRoute(route);
@@ -456,12 +567,12 @@ public class IpClientTest {
        return testcase;
    }

    static LinkProperties linkproperties(Set<LinkAddress> addresses, Set<RouteInfo> routes) {
        LinkProperties lp = new LinkProperties();
    static LinkProperties linkproperties(Set<LinkAddress> addresses,
            Set<RouteInfo> routes, Set<InetAddress> dnses) {
        LinkProperties lp = makeEmptyLinkProperties(TEST_IFNAME);
        lp.setLinkAddresses(addresses);
        for (RouteInfo route : routes) {
            lp.addRoute(route);
        }
        routes.forEach(lp::addRoute);
        dnses.forEach(lp::addDnsServer);
        return lp;
    }

@@ -479,7 +590,13 @@ public class IpClientTest {
    }

    static Set<RouteInfo> routes(String... routes) {
        return mapIntoSet(routes, (r) -> new RouteInfo(new IpPrefix(r)));
        return mapIntoSet(routes, (r) -> new RouteInfo(new IpPrefix(r), null /* gateway */,
                TEST_IFNAME));
    }

    static RouteInfo defaultIPV6Route(String gateway) {
        return new RouteInfo(new IpPrefix(Inet6Address.ANY, 0),
                InetAddresses.parseNumericAddress(gateway), TEST_IFNAME);
    }

    static Set<IpPrefix> prefixes(String... prefixes) {