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

Commit af40c15e authored by Treehugger Robot's avatar Treehugger Robot Committed by Automerger Merge Worker
Browse files

Merge "Disable IPv6 stack when IPv6 provisioning is lost but IPv4 is still...

Merge "Disable IPv6 stack when IPv6 provisioning is lost but IPv4 is still alive." am: dd369468 am: e32892e9 am: 62c43b4f

Original change: https://android-review.googlesource.com/c/platform/packages/modules/NetworkStack/+/1315221

Change-Id: Ic717a0a406a84ab57b84b461215ddce802a32214
parents 4b9060d9 62c43b4f
Loading
Loading
Loading
Loading
+3 −3
Original line number Diff line number Diff line
@@ -503,9 +503,9 @@ public class DhcpClient extends StateMachine {
     * check whether or not to support caching the last lease info and INIT-REBOOT state.
     *
     * INIT-REBOOT state is supported on Android R by default if there is no experiment flag set to
     * disable this feature explicitly, meanwhile we still hope to be able to control this feature
     * on/off by pushing experiment flag for A/B testing and metrics collection on both of Android
     * Q and R version, however it's disbled on Android Q by default.
     * disable this feature explicitly, meanwhile turning this feature on/off by pushing experiment
     * flag makes it possible to do A/B test and metrics collection on both of Android Q and R, but
     * it's disabled on Android Q by default.
     */
    public boolean isDhcpLeaseCacheEnabled() {
        final boolean defaultEnabled =
+22 −4
Original line number Diff line number Diff line
@@ -482,6 +482,7 @@ public class IpClient extends StateMachine {
    private boolean mMulticastFiltering;
    private long mStartTimeMillis;
    private MacAddress mCurrentBssid;
    private boolean mHasDisabledIPv6OnProvLoss;

    /**
     * Reading the snapshot is an asynchronous operation initiated by invoking
@@ -1137,9 +1138,9 @@ public class IpClient extends StateMachine {
        // Note that we can still be disconnected by IpReachabilityMonitor
        // if the IPv6 default gateway (but not the IPv6 DNS servers; see
        // accompanying code in IpReachabilityMonitor) is unreachable.
        final boolean ignoreIPv6ProvisioningLoss =
                mConfiguration != null && mConfiguration.mUsingMultinetworkPolicyTracker
                && !mCm.shouldAvoidBadWifi();
        final boolean ignoreIPv6ProvisioningLoss = mHasDisabledIPv6OnProvLoss
                || (mConfiguration != null && mConfiguration.mUsingMultinetworkPolicyTracker
                        && !mCm.shouldAvoidBadWifi());

        // Additionally:
        //
@@ -1163,8 +1164,24 @@ public class IpClient extends StateMachine {
        // IPv6 default route then also consider the loss of that default route
        // to be a loss of provisioning. See b/27962810.
        if (oldLp.hasGlobalIpv6Address() && (lostIPv6Router && !ignoreIPv6ProvisioningLoss)) {
            // Although link properties have lost IPv6 default route in this case, if IPv4 is still
            // working with appropriate routes and DNS servers, we can keep the current connection
            // without disconnecting from the network, just disable IPv6 on that given network until
            // to the next provisioning. Disabling IPv6 will result in all IPv6 connectivity torn
            // down and all IPv6 sockets being closed, the non-routable IPv6 DNS servers will be
            // stripped out, so applications will be able to reconnect immediately over IPv4. See
            // b/131781810.
            if (newLp.isIpv4Provisioned()) {
                mInterfaceCtrl.disableIPv6();
                mHasDisabledIPv6OnProvLoss = true;
                delta = PROV_CHANGE_STILL_PROVISIONED;
                if (DBG) {
                    mLog.log("Disable IPv6 stack completely when the default router has gone");
                }
            } else {
                delta = PROV_CHANGE_LOST_PROVISIONING;
            }
        }

        return delta;
    }
@@ -1591,6 +1608,7 @@ public class IpClient extends StateMachine {
        @Override
        public void enter() {
            stopAllIP();
            mHasDisabledIPv6OnProvLoss = false;

            mLinkObserver.clearInterfaceParams();
            resetLinkProperties();
+88 −5
Original line number Diff line number Diff line
@@ -171,6 +171,7 @@ import java.util.HashMap;
import java.util.List;
import java.util.Objects;
import java.util.Random;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
@@ -994,7 +995,7 @@ public class IpClientIntegrationTest {
            assertEquals(5, packetList.size());
            assertArpProbe(packetList.get(0));
            assertArpAnnounce(packetList.get(3));

        } else {
            verifyIPv4OnlyProvisioningSuccess(Collections.singletonList(CLIENT_ADDR));
            assertIpMemoryStoreNetworkAttributes(TEST_LEASE_DURATION_S, currentTime,
                    TEST_DEFAULT_MTU);
@@ -1245,11 +1246,11 @@ public class IpClientIntegrationTest {
        fail("No router solicitation received on interface within timeout");
    }

    private void sendBasicRouterAdvertisement(boolean waitForRs) throws Exception {
    private void sendRouterAdvertisement(boolean waitForRs, short lifetime) throws Exception {
        final String dnsServer = "2001:4860:4860::64";
        final ByteBuffer pio = buildPioOption(3600, 1800, "2001:db8:1::/64");
        ByteBuffer rdnss = buildRdnssOption(3600, dnsServer);
        ByteBuffer ra = buildRaPacket(pio, rdnss);
        ByteBuffer ra = buildRaPacket(lifetime, pio, rdnss);

        if (waitForRs) {
            waitForRouterSolicitation();
@@ -1258,6 +1259,14 @@ public class IpClientIntegrationTest {
        mPacketReader.sendResponse(ra);
    }

    private void sendBasicRouterAdvertisement(boolean waitForRs) throws Exception {
        sendRouterAdvertisement(waitForRs, (short) 1800);
    }

    private void sendRouterAdvertisementWithZeroLifetime() throws Exception {
        sendRouterAdvertisement(false /* waitForRs */, (short) 0);
    }

    // TODO: move this and the following method to a common location and use them in ApfTest.
    private static ByteBuffer buildPioOption(int valid, int preferred, String prefixString)
            throws Exception {
@@ -1319,7 +1328,8 @@ public class IpClientIntegrationTest {
        return checksumAdjust(checksum, (short) IPPROTO_TCP, (short) IPPROTO_ICMPV6);
    }

    private static ByteBuffer buildRaPacket(ByteBuffer... options) throws Exception {
    private static ByteBuffer buildRaPacket(short lifetime, ByteBuffer... options)
            throws Exception {
        final MacAddress srcMac = MacAddress.fromString("33:33:00:00:00:01");
        final MacAddress dstMac = MacAddress.fromString("01:02:03:04:05:06");
        final byte[] routerLinkLocal = InetAddresses.parseNumericAddress("fe80::1").getAddress();
@@ -1347,7 +1357,7 @@ public class IpClientIntegrationTest {
        packet.putShort((short) 0);                      // Checksum, TBD
        packet.put((byte) 0);                            // Hop limit, unspecified
        packet.put((byte) 0);                            // M=0, O=0
        packet.putShort((short) 1800);                   // Router lifetime
        packet.putShort(lifetime);                       // Router lifetime
        packet.putInt(0);                                // Reachable time, unspecified
        packet.putInt(100);                              // Retrans time 100ms.

@@ -1367,6 +1377,10 @@ public class IpClientIntegrationTest {
        return packet;
    }

    private static ByteBuffer buildRaPacket(ByteBuffer... options) throws Exception {
        return buildRaPacket((short) 1800, options);
    }

    private void disableIpv6ProvisioningDelays() throws Exception {
        // Speed up the test by disabling DAD and removing router_solicitation_delay.
        // We don't need to restore the default value because the interface is removed in tearDown.
@@ -2153,4 +2167,73 @@ public class IpClientIntegrationTest {
        doDhcpRoamingTest(true /* hasMismatchedIpAddress */, "\"0001docomo\"" /* display name */,
                TEST_DHCP_ROAM_SSID, TEST_DEFAULT_BSSID, true /* expectRoaming */);
    }

    private void doDualStackProvisioning() throws Exception {
        when(mCm.shouldAvoidBadWifi()).thenReturn(true);

        final ProvisioningConfiguration config = new ProvisioningConfiguration.Builder()
                .withoutIpReachabilityMonitor()
                .build();
        // Accelerate DHCP handshake to shorten test duration, not strictly necessary.
        mDependencies.setDhcpRapidCommitEnabled(true);
        mIpc.startProvisioning(config);

        final InOrder inOrder = inOrder(mCb);
        final CompletableFuture<LinkProperties> lpFuture = new CompletableFuture<>();
        final String dnsServer = "2001:4860:4860::64";
        final ByteBuffer pio = buildPioOption(3600, 1800, "2001:db8:1::/64");
        final ByteBuffer rdnss = buildRdnssOption(3600, dnsServer);
        final ByteBuffer ra = buildRaPacket(pio, rdnss);

        doIpv6OnlyProvisioning(inOrder, ra);

        // Start IPv4 provisioning and wait until entire provisioning completes.
        handleDhcpPackets(true /* isSuccessLease */, TEST_LEASE_DURATION_S,
                true /* shouldReplyRapidCommitAck */, TEST_DEFAULT_MTU, null /* serverSentUrl */);
        verify(mCb, timeout(TEST_TIMEOUT_MS).atLeastOnce()).onLinkPropertiesChange(argThat(x -> {
            if (!x.isIpv4Provisioned() || !x.isIpv6Provisioned()) return false;
            lpFuture.complete(x);
            return true;
        }));

        final LinkProperties lp = lpFuture.get(TEST_TIMEOUT_MS, TimeUnit.MILLISECONDS);
        assertNotNull(lp);
        assertTrue(lp.getDnsServers().contains(InetAddress.getByName(dnsServer)));
        assertTrue(lp.getDnsServers().contains(SERVER_ADDR));

        reset(mCb);
    }

    @Test
    public void testIgnoreIpv6ProvisioningLoss() throws Exception {
        doDualStackProvisioning();

        final CompletableFuture<LinkProperties> lpFuture = new CompletableFuture<>();

        // Send RA with 0-lifetime and wait until all IPv6-related default route and DNS servers
        // have been removed, then verify if there is IPv4-only info left in the LinkProperties.
        sendRouterAdvertisementWithZeroLifetime();
        verify(mCb, timeout(TEST_TIMEOUT_MS).atLeastOnce()).onLinkPropertiesChange(
                argThat(x -> {
                    final boolean isOnlyIPv4Provisioned = (x.getLinkAddresses().size() == 1
                            && x.getDnsServers().size() == 1
                            && x.getAddresses().get(0) instanceof Inet4Address
                            && x.getDnsServers().get(0) instanceof Inet4Address);

                    if (!isOnlyIPv4Provisioned) return false;
                    lpFuture.complete(x);
                    return true;
                }));
        final LinkProperties lp = lpFuture.get(TEST_TIMEOUT_MS, TimeUnit.MILLISECONDS);
        assertNotNull(lp);
        assertEquals(lp.getAddresses().get(0), CLIENT_ADDR);
        assertEquals(lp.getDnsServers().get(0), SERVER_ADDR);
    }

    @Test
    public void testDualStackProvisioning() throws Exception {
        doDualStackProvisioning();

        verify(mCb, never()).onProvisioningFailure(any());
    }
}