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

Commit 12e86a09 authored by Lorenzo Colitti's avatar Lorenzo Colitti Committed by Gerrit Code Review
Browse files

Merge changes I354b96d8,I3f8d2fbf

* changes:
  Ignore testPref64Option in AOSP.
  Listen for pref64 RA attributes in IpClientLinkObserver.
parents dc6686c8 7ae39eaa
Loading
Loading
Loading
Loading
+4 −0
Original line number Diff line number Diff line
@@ -106,6 +106,10 @@ public class NetlinkConstants {
    /* see <linux_src>/include/uapi/linux/sock_diag.h */
    public static final short SOCK_DIAG_BY_FAMILY = 20;

    // Netlink groups.
    public static final int RTNLGRP_ND_USEROPT = 20;
    public static final int RTMGRP_ND_USEROPT = 1 << (RTNLGRP_ND_USEROPT - 1);

    public static String stringForNlMsgType(short nlm_type) {
        switch (nlm_type) {
            case NLMSG_NOOP: return "NLMSG_NOOP";
+5 −1
Original line number Diff line number Diff line
@@ -583,7 +583,8 @@ public class IpClient extends StateMachine {

        mLinkObserver = new IpClientLinkObserver(
                mInterfaceName,
                () -> sendMessage(EVENT_NETLINK_LINKPROPERTIES_CHANGED), config) {
                () -> sendMessage(EVENT_NETLINK_LINKPROPERTIES_CHANGED),
                config, getHandler(), mLog) {
            @Override
            public void onInterfaceAdded(String iface) {
                super.onInterfaceAdded(iface);
@@ -1230,6 +1231,7 @@ public class IpClient extends StateMachine {
            newLp.addRoute(route);
        }
        addAllReachableDnsServers(newLp, netlinkLinkProperties.getDnsServers());
        newLp.setNat64Prefix(netlinkLinkProperties.getNat64Prefix());

        // [3] Add in data from DHCPv4, if available.
        //
@@ -1568,6 +1570,7 @@ public class IpClient extends StateMachine {
        public void enter() {
            stopAllIP();

            mLinkObserver.clearInterfaceParams();
            resetLinkProperties();
            if (mStartTimeMillis > 0) {
                // Completed a life-cycle; send a final empty LinkProperties
@@ -1713,6 +1716,7 @@ public class IpClient extends StateMachine {
                transitionTo(mStoppedState);
                return;
            }
            mLinkObserver.setInterfaceParams(mInterfaceParams);
            mCallback.setNeighborDiscoveryOffload(true);
        }

+127 −2
Original line number Diff line number Diff line
@@ -16,12 +16,27 @@

package android.net.ip;

import static android.system.OsConstants.AF_INET6;

import static com.android.server.util.NetworkStackConstants.ICMPV6_ROUTER_ADVERTISEMENT;

import android.net.InetAddresses;
import android.net.IpPrefix;
import android.net.LinkAddress;
import android.net.LinkProperties;
import android.net.RouteInfo;
import android.net.netlink.NduseroptMessage;
import android.net.netlink.NetlinkConstants;
import android.net.netlink.NetlinkMessage;
import android.net.netlink.StructNdOptPref64;
import android.net.util.InterfaceParams;
import android.net.util.SharedLog;
import android.os.Handler;
import android.system.OsConstants;
import android.util.Log;

import com.android.networkstack.apishim.NetworkInformationShim;
import com.android.networkstack.apishim.NetworkInformationShimImpl;
import com.android.server.NetworkObserver;

import java.net.InetAddress;
@@ -31,6 +46,7 @@ import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.TimeUnit;

/**
 * Keeps track of link configuration received from Netd.
@@ -56,6 +72,10 @@ import java.util.Set;
 * - All accesses to mLinkProperties must be synchronized(this). All the other
 *   member variables are immutable once the object is constructed.
 *
 * TODO: Now that all the methods are called on the handler thread, remove synchronization and
 *       pass the LinkProperties to the update() callback.
 * TODO: Stop extending NetworkObserver and get events from netlink directly.
 *
 * @hide
 */
public class IpClientLinkObserver implements NetworkObserver {
@@ -86,16 +106,21 @@ public class IpClientLinkObserver implements NetworkObserver {
    private DnsServerRepository mDnsServerRepository;
    private final Configuration mConfig;

    private final MyNetlinkMonitor mNetlinkMonitor;

    private static final boolean DBG = false;

    public IpClientLinkObserver(String iface, Callback callback, Configuration config) {
        mTag = "NetlinkTracker/" + iface;
    public IpClientLinkObserver(String iface, Callback callback, Configuration config,
            Handler h, SharedLog log) {
        mInterfaceName = iface;
        mTag = "NetlinkTracker/" + mInterfaceName;
        mCallback = callback;
        mLinkProperties = new LinkProperties();
        mLinkProperties.setInterfaceName(mInterfaceName);
        mConfig = config;
        mDnsServerRepository = new DnsServerRepository(config.minRdnssLifetime);
        mNetlinkMonitor = new MyNetlinkMonitor(h, log, mTag);
        h.post(mNetlinkMonitor::start);
    }

    private void maybeLog(String operation, String iface, LinkAddress address) {
@@ -213,6 +238,106 @@ public class IpClientLinkObserver implements NetworkObserver {
        mLinkProperties.setInterfaceName(mInterfaceName);
    }

    /** Notifies this object of new interface parameters. */
    public void setInterfaceParams(InterfaceParams params) {
        mNetlinkMonitor.setIfindex(params.index);
    }

    /** Notifies this object not to listen on any interface. */
    public void clearInterfaceParams() {
        mNetlinkMonitor.setIfindex(0);  // 0 is never a valid ifindex.
    }

    /**
     * Simple NetlinkMonitor. Currently only listens for PREF64 events.
     * All methods except the constructor must be called on the handler thread.
     */
    private class MyNetlinkMonitor extends NetlinkMonitor {
        MyNetlinkMonitor(Handler h, SharedLog log, String tag) {
            super(h, log, tag, OsConstants.NETLINK_ROUTE, NetlinkConstants.RTMGRP_ND_USEROPT);
        }

        private final NetworkInformationShim mShim = NetworkInformationShimImpl.newInstance();

        private long mNat64PrefixExpiry;

        /**
         * Current interface index. Most of this class (and of IpClient), only uses interface names,
         * not interface indices. This means that the interface index can in theory change, and that
         * it's not necessarily correct to get the interface name at object creation time (and in
         * fact, when the object is created, the interface might not even exist).
         * TODO: once all netlink events pass through this class, stop depending on interface names.
         */
        private int mIfindex;

        void setIfindex(int ifindex) {
            mIfindex = ifindex;
        }

        /**
         * Processes a PREF64 ND option.
         *
         * @param prefix The NAT64 prefix.
         * @param now The time (as determined by SystemClock.elapsedRealtime) when the event
         *            that triggered this method was received.
         * @param expiry The time (as determined by SystemClock.elapsedRealtime) when the option
         *               expires.
         */
        private synchronized void updatePref64(IpPrefix prefix, final long now,
                final long expiry) {
            final IpPrefix currentPrefix = mShim.getNat64Prefix(mLinkProperties);

            // If the prefix matches the current prefix, refresh its lifetime.
            if (prefix.equals(currentPrefix)) {
                mNat64PrefixExpiry = expiry;
            }

            // If we already have a prefix, continue using it and ignore the new one. Stopping and
            // restarting clatd is disruptive because it will break existing IPv4 connections.
            if (mNat64PrefixExpiry > now) return;

            // The current prefix has expired. Either replace it with the new one or delete it.
            if (expiry > now) {
                // If expiry > now, then prefix != currentPrefix (due to the return statement above)
                mShim.setNat64Prefix(mLinkProperties, prefix);
                mNat64PrefixExpiry = expiry;
            } else {
                mShim.setNat64Prefix(mLinkProperties, null);
                mNat64PrefixExpiry = 0;
            }

            mCallback.update();

            // TODO: send a delayed message to remove the prefix when it expires.
        }

        private void processPref64Option(StructNdOptPref64 opt, final long now) {
            final long expiry = now + TimeUnit.SECONDS.toMillis(opt.lifetime);
            updatePref64(opt.prefix, now, expiry);
        }

        private void processNduseroptMessage(NduseroptMessage msg, final long whenMs) {
            if (msg.family != AF_INET6 || msg.option == null || msg.ifindex != mIfindex) return;
            if (msg.icmp_type != (byte) ICMPV6_ROUTER_ADVERTISEMENT) return;

            switch (msg.option.type) {
                case StructNdOptPref64.TYPE:
                    processPref64Option((StructNdOptPref64) msg.option, whenMs);
                    break;

                default:
                    // TODO: implement RDNSS and DNSSL.
                    break;
            }
        }

        @Override
        protected void processNetlinkMessage(NetlinkMessage nlMsg, long whenMs) {
            if (!(nlMsg instanceof NduseroptMessage)) return;
            processNduseroptMessage((NduseroptMessage) nlMsg, whenMs);
        }
    }

    /**
     * Tracks DNS server updates received from Netlink.
     *
+88 −2
Original line number Diff line number Diff line
@@ -65,6 +65,7 @@ import static org.mockito.Mockito.argThat;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.eq;
import static org.mockito.Mockito.inOrder;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.spy;
@@ -103,6 +104,7 @@ import android.net.dhcp.DhcpRequestPacket;
import android.net.ipmemorystore.NetworkAttributes;
import android.net.ipmemorystore.OnNetworkAttributesRetrievedListener;
import android.net.ipmemorystore.Status;
import android.net.netlink.StructNdOptPref64;
import android.net.shared.Layer2Information;
import android.net.shared.ProvisioningConfiguration;
import android.net.shared.ProvisioningConfiguration.ScanResultInfo;
@@ -137,10 +139,12 @@ import com.android.testutils.TapPacketReader;

import org.junit.After;
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.InOrder;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.mockito.Spy;
@@ -1278,12 +1282,16 @@ public class IpClientIntegrationTest {
        return packet;
    }

    @Test
    public void testRaRdnss() throws Exception {
    private void disableRouterSolicitationDelay() throws Exception {
        // Speed up the test by removing router_solicitation_delay.
        // We don't need to restore the default value because the interface is removed in tearDown.
        // TODO: speed up further by not waiting for RA but keying off first IPv6 packet.
        mNetd.setProcSysNet(INetd.IPV6, INetd.CONF, mIfaceName, "router_solicitation_delay", "0");
    }

    @Test
    public void testRaRdnss() throws Exception {
        disableRouterSolicitationDelay();

        ProvisioningConfiguration config = new ProvisioningConfiguration.Builder()
                .withoutIpReachabilityMonitor()
@@ -1337,6 +1345,84 @@ public class IpClientIntegrationTest {
        reset(mCb);
    }

    private void expectNat64PrefixUpdate(InOrder inOrder, IpPrefix expected) throws Exception {
        inOrder.verify(mCb, timeout(TEST_TIMEOUT_MS)).onLinkPropertiesChange(
                argThat(lp -> Objects.equals(expected, lp.getNat64Prefix())));

    }

    private void expectNoNat64PrefixUpdate(InOrder inOrder, IpPrefix unchanged) throws Exception {
        HandlerUtilsKt.waitForIdle(mIpc.getHandler(), TEST_TIMEOUT_MS);
        inOrder.verify(mCb, never()).onLinkPropertiesChange(argThat(
                lp -> !Objects.equals(unchanged, lp.getNat64Prefix())));

    }

    @Ignore  // AOSP kernels don't support the PREF64 option yet.
    @Test @IgnoreUpTo(Build.VERSION_CODES.Q)
    public void testPref64Option() throws Exception {
        disableRouterSolicitationDelay();

        ProvisioningConfiguration config = new ProvisioningConfiguration.Builder()
                .withoutIpReachabilityMonitor()
                .withoutIPv4()
                .build();
        mIpc.startProvisioning(config);

        final String dnsServer = "2001:4860:4860::64";
        final IpPrefix prefix = new IpPrefix("64:ff9b::/96");
        final IpPrefix otherPrefix = new IpPrefix("2001:db8:64::/96");

        final ByteBuffer pio = buildPioOption(600, 300, "2001:db8:1::/64");
        ByteBuffer rdnss = buildRdnssOption(600, dnsServer);
        ByteBuffer pref64 = new StructNdOptPref64(prefix, 600).toByteBuffer();
        ByteBuffer ra = buildRaPacket(pio, rdnss, pref64);

        waitForRouterSolicitation();
        mPacketReader.sendResponse(ra);

        InOrder inOrder = inOrder(mCb);
        ArgumentCaptor<LinkProperties> captor = ArgumentCaptor.forClass(LinkProperties.class);
        inOrder.verify(mCb, timeout(TEST_TIMEOUT_MS)).onProvisioningSuccess(captor.capture());

        // The NAT64 prefix might have been detected before or after provisioning success.
        LinkProperties lp = captor.getValue();
        if (lp.getNat64Prefix() != null) {
            assertEquals(prefix, lp.getNat64Prefix());
        } else {
            expectNat64PrefixUpdate(inOrder, prefix);
        }

        // Increase the lifetime and expect the prefix not to change.
        pref64 = new StructNdOptPref64(prefix, 1800).toByteBuffer();
        ra = buildRaPacket(pio, rdnss, pref64);
        mPacketReader.sendResponse(ra);
        expectNoNat64PrefixUpdate(inOrder, prefix);

        // Withdraw the prefix and expect it to be set to null.
        pref64 = new StructNdOptPref64(prefix, 0).toByteBuffer();
        ra = buildRaPacket(pio, rdnss, pref64);
        mPacketReader.sendResponse(ra);
        expectNat64PrefixUpdate(inOrder, null);

        // Re-announce the prefix.
        pref64 = new StructNdOptPref64(prefix, 600).toByteBuffer();
        ra = buildRaPacket(pio, rdnss, pref64);
        mPacketReader.sendResponse(ra);
        expectNat64PrefixUpdate(inOrder, prefix);

        // Announce two prefixes. Don't expect any update because if there is already a NAT64
        // prefix, any new prefix is ignored.
        ByteBuffer otherPref64 = new StructNdOptPref64(otherPrefix, 600).toByteBuffer();
        ra = buildRaPacket(pio, rdnss, pref64, otherPref64);
        mPacketReader.sendResponse(ra);

        pref64 = new StructNdOptPref64(prefix, 0).toByteBuffer();
        ra = buildRaPacket(pio, rdnss, pref64, otherPref64);
        mPacketReader.sendResponse(ra);
        expectNat64PrefixUpdate(inOrder, otherPrefix);
    }

    @Test
    public void testIpClientClearingIpAddressState() throws Exception {
        final long currentTime = System.currentTimeMillis();