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

Commit f035c807 authored by Xiao Ma's avatar Xiao Ma Committed by Gerrit Code Review
Browse files

Merge "Support IPv6 link-local only mode."

parents b91624c5 326820d1
Loading
Loading
Loading
Loading
+18 −1
Original line number Diff line number Diff line
@@ -92,7 +92,7 @@ public class ProvisioningConfiguration {

    // ipv4ProvisioningMode and ipv6ProvisioningMode members are introduced since
    // networkstack-aidl-interfaces-v12.
    private static final int VERSION_ADDED_PROVISIONING_ENUM = 12;
    public static final int VERSION_ADDED_PROVISIONING_ENUM = 12;

    /**
     * Builder to create a {@link ProvisioningConfiguration}.
@@ -257,10 +257,27 @@ public class ProvisioningConfiguration {
            return this;
        }

        /**
         * Specify that the configuration should enable IPv6 link-local only mode used for
         * WiFi Neighbor Aware Networking and other link-local-only technologies. It's not
         * used by default, and IPv4 must be disabled when this mode is enabled.
         *
         * @note This API is only supported since Android T.
         */
        public Builder withIpv6LinkLocalOnly() {
            mConfig.mIPv6ProvisioningMode = PROV_IPV6_LINKLOCAL;
            return this;
        }

        /**
         * Build the configuration using previously specified parameters.
         */
        public ProvisioningConfiguration build() {
            if (mConfig.mIPv6ProvisioningMode == PROV_IPV6_LINKLOCAL
                    && mConfig.mIPv4ProvisioningMode != PROV_IPV4_DISABLED) {
                throw new IllegalArgumentException("IPv4 must be disabled in IPv6 link-local"
                        + "only mode.");
            }
            return new ProvisioningConfiguration(mConfig);
        }
    }
+53 −13
Original line number Diff line number Diff line
@@ -20,6 +20,7 @@ import static android.net.RouteInfo.RTN_UNICAST;
import static android.net.dhcp.DhcpResultsParcelableUtil.toStableParcelable;
import static android.net.ip.IIpClient.PROV_IPV4_DISABLED;
import static android.net.ip.IIpClient.PROV_IPV6_DISABLED;
import static android.net.ip.IIpClient.PROV_IPV6_LINKLOCAL;
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;
@@ -399,6 +400,24 @@ public class IpClient extends StateMachine {
                log("Failed to call onPreconnectionStart", e);
            }
        }

        /**
         * Get the version of the IIpClientCallbacks AIDL interface.
         */
        public int getInterfaceVersion() {
            log("getInterfaceVersion");
            try {
                return mCallback.getInterfaceVersion();
            } catch (RemoteException e) {
                // This can never happen for callers in the system server, because if the
                // system server crashes, then the networkstack will crash as well. But it can
                // happen for other callers such as bluetooth or telephony (if it starts to use
                // IpClient). 0 will generally work but will assume an old client and disable
                // all new features.
                log("Failed to call getInterfaceVersion", e);
                return 0;
            }
        }
    }

    public static final String DUMP_ARG_CONFIRM = "confirm";
@@ -498,7 +517,6 @@ public class IpClient extends StateMachine {
    private final String mClatInterfaceName;
    @VisibleForTesting
    protected final IpClientCallbacksWrapper mCallback;
    private final IIpClientCallbacks mIpClientCallback;
    private final Dependencies mDependencies;
    private final CountDownLatch mShutdownLatch;
    private final ConnectivityManager mCm;
@@ -678,7 +696,6 @@ public class IpClient extends StateMachine {
        sPktLogs.putIfAbsent(mInterfaceName, new LocalLog(MAX_PACKET_RECORDS));
        mConnectivityPacketLog = sPktLogs.get(mInterfaceName);
        mMsgStateLogger = new MessageHandlingLogger();
        mIpClientCallback = callback;
        mCallback = new IpClientCallbacksWrapper(callback, mLog, mShim);

        // TODO: Consider creating, constructing, and passing in some kind of
@@ -799,14 +816,8 @@ public class IpClient extends StateMachine {
        @Override
        public void startProvisioning(ProvisioningConfigurationParcelable req) {
            enforceNetworkStackCallingPermission();
            int interfaceVersion = 0;
            try {
                interfaceVersion = mIpClientCallback.getInterfaceVersion();
            } catch (RemoteException e) {
                mLog.e("Fail to get the remote IIpClientCallbacks interface version", e);
            }
            IpClient.this.startProvisioning(ProvisioningConfiguration.fromStableParcelable(req,
                    interfaceVersion));
                    mCallback.getInterfaceVersion()));
        }
        @Override
        public void stop() {
@@ -1238,12 +1249,41 @@ public class IpClient extends StateMachine {
        transitionTo(mStoppingState);
    }

    private static boolean hasIpv6LinkLocalInterfaceRoute(final LinkProperties lp) {
        for (RouteInfo r : lp.getRoutes()) {
            if (r.getDestination().equals(new IpPrefix("fe80::/64"))
                    && r.getGateway().isAnyLocalAddress()) {
                return true;
            }
        }
        return false;
    }

    private static boolean hasIpv6LinkLocalAddress(final LinkProperties lp) {
        for (LinkAddress address : lp.getLinkAddresses()) {
            if (address.isIpv6() && address.getAddress().isLinkLocalAddress()) {
                return true;
            }
        }
        return false;
    }

    // LinkProperties has a link-local (fe80::xxx) IPv6 address and route to fe80::/64 destination.
    private boolean isIpv6LinkLocalProvisioned(final LinkProperties lp) {
        if (mConfiguration == null
                || mConfiguration.mIPv6ProvisioningMode != PROV_IPV6_LINKLOCAL) return false;
        if (hasIpv6LinkLocalAddress(lp) && hasIpv6LinkLocalInterfaceRoute(lp)) return true;
        return false;
    }

    // For now: use WifiStateMachine's historical notion of provisioned.
    @VisibleForTesting
    static boolean isProvisioned(LinkProperties lp, InitialConfiguration config) {
        // For historical reasons, we should connect even if all we have is
        // an IPv4 address and nothing else.
        if (lp.hasIpv4Address() || lp.isProvisioned()) {
    boolean isProvisioned(final LinkProperties lp, final InitialConfiguration config) {
        // For historical reasons, we should connect even if all we have is an IPv4
        // address and nothing else. If IPv6 link-local only mode is enabled and
        // it's provisioned without IPv4, then still connecting once IPv6 link-local
        // address is ready to use and route to fe80::/64 destination is up.
        if (lp.hasIpv4Address() || lp.isProvisioned() || isIpv6LinkLocalProvisioned(lp)) {
            return true;
        }
        if (config == null) {
+39 −0
Original line number Diff line number Diff line
@@ -30,6 +30,7 @@ import static android.net.dhcp.DhcpResultsParcelableUtil.fromStableParcelable;
import static android.net.ip.IpReachabilityMonitor.MIN_NUD_SOLICIT_NUM;
import static android.net.ip.IpReachabilityMonitor.NUD_MCAST_RESOLICIT_NUM;
import static android.net.ipmemorystore.Status.SUCCESS;
import static android.net.shared.ProvisioningConfiguration.VERSION_ADDED_PROVISIONING_ENUM;
import static android.system.OsConstants.ETH_P_IPV6;
import static android.system.OsConstants.IFA_F_TEMPORARY;
import static android.system.OsConstants.IPPROTO_ICMPV6;
@@ -56,6 +57,7 @@ import static com.android.net.module.util.NetworkStackConstants.NEIGHBOR_ADVERTI
import static com.android.net.module.util.NetworkStackConstants.NEIGHBOR_ADVERTISEMENT_FLAG_SOLICITED;
import static com.android.net.module.util.NetworkStackConstants.PIO_FLAG_AUTONOMOUS;
import static com.android.net.module.util.NetworkStackConstants.PIO_FLAG_ON_LINK;
import static com.android.testutils.MiscAsserts.assertThrows;

import static junit.framework.Assert.fail;

@@ -106,6 +108,7 @@ import android.net.LinkAddress;
import android.net.LinkProperties;
import android.net.MacAddress;
import android.net.NetworkStackIpMemoryStore;
import android.net.RouteInfo;
import android.net.TestNetworkInterface;
import android.net.TestNetworkManager;
import android.net.Uri;
@@ -570,6 +573,7 @@ public abstract class IpClientIntegrationTestCommon {
        when(mContext.getContentResolver()).thenReturn(mContentResolver);
        when(mNetworkStackServiceManager.getIpMemoryStoreService())
                .thenReturn(mIpMemoryStoreService);
        when(mCb.getInterfaceVersion()).thenReturn(VERSION_ADDED_PROVISIONING_ENUM);

        mDependencies.setDeviceConfigProperty(IpClient.CONFIG_MIN_RDNSS_LIFETIME, 67);
        mDependencies.setDeviceConfigProperty(DhcpClient.DHCP_RESTART_CONFIG_DELAY, 10);
@@ -3458,4 +3462,39 @@ public abstract class IpClientIntegrationTestCommon {
        mPacketReader.sendResponse(na);
        assertNotifyNeighborLost(ROUTER_LINK_LOCAL /* targetIp */);
    }

    @Test
    public void testIPv6LinkLocalOnly() throws Exception {
        ProvisioningConfiguration config = new ProvisioningConfiguration.Builder()
                .withoutIPv4()
                .withIpv6LinkLocalOnly()
                .withRandomMacAddress()
                .build();
        startIpClientProvisioning(config);

        final ArgumentCaptor<LinkProperties> captor = ArgumentCaptor.forClass(LinkProperties.class);
        verify(mCb, timeout(TEST_TIMEOUT_MS)).onProvisioningSuccess(captor.capture());
        final LinkProperties lp = captor.getValue();
        assertNotNull(lp);
        assertEquals(0, lp.getDnsServers().size());
        final List<LinkAddress> addresses = lp.getLinkAddresses();
        assertEquals(1, addresses.size());
        assertTrue(addresses.get(0).getAddress().isLinkLocalAddress());
        assertEquals(1, lp.getRoutes().size());
        final RouteInfo route = lp.getRoutes().get(0);
        assertNotNull(route);
        assertTrue(route.getDestination().equals(new IpPrefix("fe80::/64")));
        assertTrue(route.getGateway().isAnyLocalAddress());
    }

    @Test
    public void testIPv6LinkLocalOnly_enableBothIPv4andIPv6LinkLocalOnly() throws Exception {
        assertThrows(IllegalArgumentException.class,
                () -> new ProvisioningConfiguration.Builder()
                        .withoutIpReachabilityMonitor()
                        .withIpv6LinkLocalOnly()
                        .withRandomMacAddress()
                        .build()
        );
    }
}
+1 −0
Original line number Diff line number Diff line
@@ -153,6 +153,7 @@ class IpClientRootTest : IpClientIntegrationTestCommon() {
            IIpClientCallbacks.Stub(), IIpClientCallbacks by base {
        // asBinder is implemented by both base class and delegate: specify explicitly
        override fun asBinder() = super.asBinder()
        override fun getInterfaceVersion() = IIpClientCallbacks.VERSION
    }

    @After
+2 −1
Original line number Diff line number Diff line
@@ -431,6 +431,7 @@ public class IpClientTest {

    @Test
    public void testIsProvisioned() throws Exception {
        final IpClient ipc = makeIpClient(TEST_IFNAME);
        InitialConfiguration empty = conf(links(), prefixes());
        IsProvisionedTestCase[] testcases = {
            // nothing
@@ -462,7 +463,7 @@ public class IpClientTest {
        };

        for (IsProvisionedTestCase testcase : testcases) {
            if (IpClient.isProvisioned(testcase.lp, testcase.config) != testcase.isProvisioned) {
            if (ipc.isProvisioned(testcase.lp, testcase.config) != testcase.isProvisioned) {
                fail(testcase.errorMessage());
            }
        }
Loading