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

Commit d018ce3b authored by Xiao Ma's avatar Xiao Ma Committed by Automerger Merge Worker
Browse files

Disable accept_ra upon IPv6 default route has gone for dual-stack devices. am: d63c1377

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

Change-Id: I36924f716c48ed3797dbb4e434096fbfe0e1b59f
parents 6de2a9a4 d63c1377
Loading
Loading
Loading
Loading
+46 −11
Original line number Diff line number Diff line
@@ -18,6 +18,7 @@ package android.net.ip;

import static android.net.RouteInfo.RTN_UNICAST;
import static android.net.dhcp.DhcpResultsParcelableUtil.toStableParcelable;
import static android.net.util.NetworkStackUtils.IPCLIENT_DISABLE_ACCEPT_RA_VERSION;
import static android.net.util.NetworkStackUtils.IPCLIENT_GARP_NA_ROAMING_VERSION;
import static android.net.util.NetworkStackUtils.IPCLIENT_GRATUITOUS_NA_VERSION;
import static android.net.util.SocketUtils.makePacketSocketAddress;
@@ -527,7 +528,7 @@ public class IpClient extends StateMachine {
    private boolean mMulticastFiltering;
    private long mStartTimeMillis;
    private MacAddress mCurrentBssid;
    private boolean mHasDisabledIPv6OnProvLoss;
    private boolean mHasDisabledIpv6OrAcceptRaOnProvLoss;

    /**
     * Reading the snapshot is an asynchronous operation initiated by invoking
@@ -881,6 +882,11 @@ public class IpClient extends StateMachine {
        }
    }

    private boolean shouldDisableAcceptRaOnProvisioningLoss() {
        return mDependencies.isFeatureEnabled(mContext, IPCLIENT_DISABLE_ACCEPT_RA_VERSION,
                true /* defaultEnabled */);
    }

    @Override
    protected void onQuitting() {
        mCallback.onQuit();
@@ -1198,6 +1204,21 @@ public class IpClient extends StateMachine {
        return config.isProvisionedBy(lp.getLinkAddresses(), lp.getRoutes());
    }

    private void setIpv6AcceptRa(int acceptRa) {
        try {
            mNetd.setProcSysNet(INetd.IPV6, INetd.CONF, mInterfaceParams.name, "accept_ra",
                    Integer.toString(acceptRa));
        } catch (Exception e) {
            Log.e(mTag, "Failed to set accept_ra to " + acceptRa);
        }
    }

    private void restartIpv6WithAcceptRaDisabled() {
        mInterfaceCtrl.disableIPv6();
        setIpv6AcceptRa(0 /* accept_ra */);
        startIPv6();
    }

    // TODO: Investigate folding all this into the existing static function
    // LinkProperties.compareProvisioning() or some other single function that
    // takes two LinkProperties objects and returns a ProvisioningChange
@@ -1247,7 +1268,7 @@ 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 = mHasDisabledIPv6OnProvLoss
        final boolean ignoreIPv6ProvisioningLoss = mHasDisabledIpv6OrAcceptRaOnProvLoss
                || (mConfiguration != null && mConfiguration.mUsingMultinetworkPolicyTracker
                        && !mCm.shouldAvoidBadWifi());

@@ -1275,18 +1296,31 @@ public class IpClient extends StateMachine {
        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.
            // without disconnecting from the network, just disable IPv6 or accept_ra parameter on
            // that given network until to the next provisioning.
            //
            // Disabling IPv6 stack 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.
            //
            // Sometimes disabling IPv6 stack might introduce other issues(see b/179222860),
            // instead disabling accept_ra will result in only IPv4 provisioning and IPv6 link
            // local address left on the interface, so applications will be able to reconnect
            // immediately over IPv4 and keep IPv6 link-local capable.
            if (newLp.isIpv4Provisioned()) {
                if (shouldDisableAcceptRaOnProvisioningLoss()) {
                    restartIpv6WithAcceptRaDisabled();
                } else {
                    mInterfaceCtrl.disableIPv6();
                }
                mNetworkQuirkMetrics.setEvent(NetworkQuirkEvent.QE_IPV6_PROVISIONING_ROUTER_LOST);
                mNetworkQuirkMetrics.statsWrite();
                mHasDisabledIPv6OnProvLoss = true;
                mHasDisabledIpv6OrAcceptRaOnProvLoss = true;
                delta = PROV_CHANGE_STILL_PROVISIONED;
                mLog.log("Disable IPv6 stack completely when the default router has gone");
                mLog.log(shouldDisableAcceptRaOnProvisioningLoss()
                        ? "Disabled accept_ra parameter "
                        : "Disabled IPv6 stack completely "
                        + "when the IPv6 default router has gone");
            } else {
                delta = PROV_CHANGE_LOST_PROVISIONING;
            }
@@ -1839,7 +1873,8 @@ public class IpClient extends StateMachine {
        @Override
        public void enter() {
            stopAllIP();
            mHasDisabledIPv6OnProvLoss = false;
            setIpv6AcceptRa(2 /* accept_ra */);
            mHasDisabledIpv6OrAcceptRaOnProvLoss = false;
            mGratuitousNaTargetAddresses.clear();

            mLinkObserver.clearInterfaceParams();
+6 −0
Original line number Diff line number Diff line
@@ -249,6 +249,12 @@ public class NetworkStackUtils {
    public static final String IPCLIENT_GARP_NA_ROAMING_VERSION =
            "ipclient_garp_na_roaming_version";

    /**
     * Experiment flag to disable accept_ra parameter when IPv6 provisioning loss happens due to
     * the default route has gone.
     */
    public static final String IPCLIENT_DISABLE_ACCEPT_RA_VERSION = "ipclient_disable_accept_ra";

    static {
        System.loadLibrary("networkstackutilsjni");
    }
+46 −7
Original line number Diff line number Diff line
@@ -2404,12 +2404,15 @@ public abstract class IpClientIntegrationTestCommon {
        reset(mCb);
    }

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

        final ProvisioningConfiguration config = new ProvisioningConfiguration.Builder()
                .withoutIpReachabilityMonitor()
                .build();

        setFeatureEnabled(NetworkStackUtils.IPCLIENT_DISABLE_ACCEPT_RA_VERSION,
                shouldDisableAcceptRa);
        // Enable rapid commit to accelerate DHCP handshake to shorten test duration,
        // not strictly necessary.
        setDhcpFeatures(false /* isDhcpLeaseCacheEnabled */, true /* isRapidCommitEnabled */,
@@ -2419,9 +2422,9 @@ public abstract class IpClientIntegrationTestCommon {
        performDualStackProvisioning();
    }

    @Test @SignatureRequiredTest(reason = "TODO: evaluate whether signature perms are required")
    public void testIgnoreIpv6ProvisioningLoss() throws Exception {
        doDualStackProvisioning();
    @Test @SignatureRequiredTest(reason = "signature perms are required due to mocked callabck")
    public void testIgnoreIpv6ProvisioningLoss_disableIPv6Stack() throws Exception {
        doDualStackProvisioning(false /* shouldDisableAcceptRa */);

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

@@ -2450,9 +2453,45 @@ public abstract class IpClientIntegrationTestCommon {
                (long) NetworkQuirkEvent.QE_IPV6_PROVISIONING_ROUTER_LOST.ordinal());
    }

    @Test @SignatureRequiredTest(reason = "signature perms are required due to mocked callabck")
    public void testIgnoreIpv6ProvisioningLoss_disableAcceptRa() throws Exception {
        doDualStackProvisioning(true /* shouldDisableAcceptRa */);

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

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

                    if (!isIPv6LinkLocalAndIPv4OnlyProvisioned) 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);
        assertTrue(lp.getAddresses().get(1).isLinkLocalAddress());

        reset(mCb);

        // Send an RA to verify that global IPv6 addresses won't be configured on the interface.
        sendBasicRouterAdvertisement(false /* waitForRs */);
        verify(mCb, timeout(TEST_TIMEOUT_MS).times(0)).onLinkPropertiesChange(any());
    }

    @Test @SignatureRequiredTest(reason = "TODO: evaluate whether signature perms are required")
    public void testDualStackProvisioning() throws Exception {
        doDualStackProvisioning();
        doDualStackProvisioning(false /* shouldDisableAcceptRa */);

        verify(mCb, never()).onProvisioningFailure(any());
    }
@@ -2672,7 +2711,7 @@ public abstract class IpClientIntegrationTestCommon {
    public void testNoFdLeaks() throws Exception {
        // Shut down and restart IpClient once to ensure that any fds that are opened the first
        // time it runs do not cause the test to fail.
        doDualStackProvisioning();
        doDualStackProvisioning(false /* shouldDisableAcceptRa */);
        shutdownAndRecreateIpClient();

        // Unfortunately we cannot use a large number of iterations as it would make the test run
@@ -2680,7 +2719,7 @@ public abstract class IpClientIntegrationTestCommon {
        final int iterations = 10;
        final int before = getNumOpenFds();
        for (int i = 0; i < iterations; i++) {
            doDualStackProvisioning();
            doDualStackProvisioning(false /* shouldDisableAcceptRa */);
            shutdownAndRecreateIpClient();
            // The last time this loop runs, mIpc will be shut down in tearDown.
        }