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

Commit 1aecd5e8 authored by Treehugger Robot's avatar Treehugger Robot Committed by Gerrit Code Review
Browse files

Merge "Optimize neighbor unreachability detection probe fallback."

parents 32563f8b 69a936a4
Loading
Loading
Loading
Loading
+75 −8
Original line number Diff line number Diff line
@@ -20,6 +20,8 @@ import static android.net.metrics.IpReachabilityEvent.NUD_FAILED;
import static android.net.metrics.IpReachabilityEvent.NUD_FAILED_ORGANIC;
import static android.net.metrics.IpReachabilityEvent.PROVISIONING_LOST;
import static android.net.metrics.IpReachabilityEvent.PROVISIONING_LOST_ORGANIC;
import static android.net.util.NetworkStackUtils.IP_REACHABILITY_MCAST_RESOLICIT_VERSION;
import static android.provider.DeviceConfig.NAMESPACE_CONNECTIVITY;

import android.content.Context;
import android.net.ConnectivityManager;
@@ -43,8 +45,12 @@ import android.os.SystemClock;
import android.text.TextUtils;
import android.util.Log;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;

import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.Preconditions;
import com.android.net.module.util.DeviceConfigUtils;
import com.android.networkstack.R;

import java.io.PrintWriter;
@@ -143,6 +149,8 @@ public class IpReachabilityMonitor {
    protected static final int MIN_NUD_SOLICIT_NUM = 5;
    protected static final int MAX_NUD_SOLICIT_INTERVAL_MS = 1000;
    protected static final int MIN_NUD_SOLICIT_INTERVAL_MS = 750;
    protected static final int NUD_MCAST_RESOLICIT_NUM = 3;
    private static final int INVALID_NUD_MCAST_RESOLICIT_NUM = -1;

    public interface Callback {
        /**
@@ -161,6 +169,7 @@ public class IpReachabilityMonitor {
    interface Dependencies {
        void acquireWakeLock(long durationMs);
        IpNeighborMonitor makeIpNeighborMonitor(Handler h, SharedLog log, NeighborEventConsumer cb);
        boolean isFeatureEnabled(Context context, String name, boolean defaultEnabled);

        static Dependencies makeDefault(Context context, String iface) {
            final String lockName = TAG + "." + iface;
@@ -176,6 +185,12 @@ public class IpReachabilityMonitor {
                        NeighborEventConsumer cb) {
                    return new IpNeighborMonitor(h, log, cb);
                }

                public boolean isFeatureEnabled(final Context context, final String name,
                        boolean defaultEnabled) {
                    return DeviceConfigUtils.isFeatureEnabled(context, NAMESPACE_CONNECTIVITY, name,
                            defaultEnabled);
                }
            };
        }
    }
@@ -183,7 +198,6 @@ public class IpReachabilityMonitor {
    private final InterfaceParams mInterfaceParams;
    private final IpNeighborMonitor mIpNeighborMonitor;
    private final SharedLog mLog;
    private final Callback mCallback;
    private final Dependencies mDependencies;
    private final boolean mUsingMultinetworkPolicyTracker;
    private final ConnectivityManager mCm;
@@ -196,6 +210,8 @@ public class IpReachabilityMonitor {
    private volatile long mLastProbeTimeMs;
    private int mNumSolicits;
    private int mInterSolicitIntervalMs;
    @NonNull
    private final Callback mCallback;

    public IpReachabilityMonitor(
            Context context, InterfaceParams ifParams, Handler h, SharedLog log, Callback callback,
@@ -225,7 +241,10 @@ public class IpReachabilityMonitor {
        // In case the overylaid parameters specify an invalid configuration, set the parameters
        // to the hardcoded defaults first, then set them to the values used in the steady state.
        try {
            setNeighborParameters(MIN_NUD_SOLICIT_NUM, MIN_NUD_SOLICIT_INTERVAL_MS);
            int numResolicits = isMulticastResolicitEnabled()
                    ? NUD_MCAST_RESOLICIT_NUM
                    : INVALID_NUD_MCAST_RESOLICIT_NUM;
            setNeighborParameters(MIN_NUD_SOLICIT_NUM, MIN_NUD_SOLICIT_INTERVAL_MS, numResolicits);
        } catch (Exception e) {
            Log.e(TAG, "Failed to adjust neighbor parameters with hardcoded defaults");
        }
@@ -241,10 +260,12 @@ public class IpReachabilityMonitor {
                    // TODO: Consider what to do with other states that are not within
                    // NeighborEvent#isValid() (i.e. NUD_NONE, NUD_INCOMPLETE).
                    if (event.nudState == StructNdMsg.NUD_FAILED) {
                        // After both unicast probe and multicast probe(if mcast_resolicit is not 0)
                        // attempts fail, trigger the neighbor lost event and disconnect.
                        mLog.w("ALERT neighbor went from: " + prev + " to: " + event);
                        handleNeighborLost(event);
                    } else if (event.nudState == StructNdMsg.NUD_REACHABLE) {
                        maybeRestoreNeighborParameters();
                        handleNeighborReachable(prev, event);
                    }
                });
        mIpNeighborMonitor.start();
@@ -296,6 +317,26 @@ public class IpReachabilityMonitor {
        return false;
    }

    private boolean hasDefaultRouterNeighborMacAddressChanged(
            @Nullable final NeighborEvent prev, @NonNull final NeighborEvent event) {
        if (prev == null || !isNeighborDefaultRouter(event)) return false;
        return !event.macAddr.equals(prev.macAddr);
    }

    private boolean isNeighborDefaultRouter(@NonNull final NeighborEvent event) {
        // For the IPv6 link-local scoped address, equals() works because the NeighborEvent.ip
        // doesn't have a scope id and Inet6Address#equals doesn't consider scope id neither.
        for (RouteInfo route : mLinkProperties.getRoutes()) {
            if (route.isDefaultRoute() && event.ip.equals(route.getGateway())) return true;
        }
        return false;
    }

    private boolean isMulticastResolicitEnabled() {
        return mDependencies.isFeatureEnabled(mContext, IP_REACHABILITY_MCAST_RESOLICIT_VERSION,
                false /* defaultEnabled */);
    }

    public void updateLinkProperties(LinkProperties lp) {
        if (!mInterfaceParams.name.equals(lp.getInterfaceName())) {
            // TODO: figure out whether / how to cope with interface changes.
@@ -333,6 +374,24 @@ public class IpReachabilityMonitor {
        if (DBG) { Log.d(TAG, "clear: " + describeWatchList()); }
    }

    private void handleNeighborReachable(@Nullable final NeighborEvent prev,
            @NonNull final NeighborEvent event) {
        if (isMulticastResolicitEnabled()
                && hasDefaultRouterNeighborMacAddressChanged(prev, event)) {
            // This implies device has confirmed the neighbor's reachability from
            // other states(e.g., NUD_PROBE or NUD_STALE), checking if the mac
            // address hasn't changed is required. If Mac address does change, then
            // trigger a new neighbor lost event and disconnect.
            final String logMsg = "ALERT neighbor: " + event.ip
                    + " MAC address changed from: " + prev.macAddr
                    + " to: " + event.macAddr;
            mLog.w(logMsg);
            mCallback.notifyLost(event.ip, logMsg);
            return;
        }
        maybeRestoreNeighborParameters();
    }

    private void handleNeighborLost(NeighborEvent event) {
        final LinkProperties whatIfLp = new LinkProperties(mLinkProperties);

@@ -370,12 +429,10 @@ public class IpReachabilityMonitor {
        if (lostProvisioning) {
            final String logMsg = "FAILURE: LOST_PROVISIONING, " + event;
            Log.w(TAG, logMsg);
            if (mCallback != null) {
            // TODO: remove |ip| when the callback signature no longer has
            // an InetAddress argument.
            mCallback.notifyLost(ip, logMsg);
        }
        }
        logNudFailed(lostProvisioning);
    }

@@ -450,6 +507,12 @@ public class IpReachabilityMonitor {

    private void setNeighborParameters(int numSolicits, int interSolicitIntervalMs)
            throws RemoteException, IllegalArgumentException {
        // Do not set mcast_resolicit param by default.
        setNeighborParameters(numSolicits, interSolicitIntervalMs, INVALID_NUD_MCAST_RESOLICIT_NUM);
    }

    private void setNeighborParameters(int numSolicits, int interSolicitIntervalMs,
            int numResolicits) throws RemoteException, IllegalArgumentException {
        Preconditions.checkArgument(numSolicits >= MIN_NUD_SOLICIT_NUM,
                "numSolicits must be at least " + MIN_NUD_SOLICIT_NUM);
        Preconditions.checkArgument(numSolicits <= MAX_NUD_SOLICIT_NUM,
@@ -464,6 +527,10 @@ public class IpReachabilityMonitor {
                    Integer.toString(interSolicitIntervalMs));
            mNetd.setProcSysNet(family, INetd.NEIGH, mInterfaceParams.name, "ucast_solicit",
                    Integer.toString(numSolicits));
            if (numResolicits != INVALID_NUD_MCAST_RESOLICIT_NUM) {
                mNetd.setProcSysNet(family, INetd.NEIGH, mInterfaceParams.name, "mcast_resolicit",
                        Integer.toString(numResolicits));
            }
        }

        mNumSolicits = numSolicits;
+7 −0
Original line number Diff line number Diff line
@@ -255,6 +255,13 @@ public class NetworkStackUtils {
     */
    public static final String IPCLIENT_DISABLE_ACCEPT_RA_VERSION = "ipclient_disable_accept_ra";

    /**
     * Experiment flag to enable "mcast_resolicit" neighbor parameter in IpReachabilityMonitor,
     * set it to 3 by default.
     */
    public static final String IP_REACHABILITY_MCAST_RESOLICIT_VERSION =
            "ip_reachability_mcast_resolicit_version";

    static {
        System.loadLibrary("networkstackutilsjni");
    }
+112 −16
Original line number Diff line number Diff line
@@ -27,6 +27,8 @@ import static android.net.dhcp.DhcpPacket.INADDR_BROADCAST;
import static android.net.dhcp.DhcpPacket.INFINITE_LEASE;
import static android.net.dhcp.DhcpPacket.MIN_V6ONLY_WAIT_MS;
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.system.OsConstants.ETH_P_IPV6;
import static android.system.OsConstants.IFA_F_TEMPORARY;
@@ -49,6 +51,7 @@ import static com.android.net.module.util.NetworkStackConstants.IPV6_ADDR_ALL_NO
import static com.android.net.module.util.NetworkStackConstants.IPV6_ADDR_ALL_ROUTERS_MULTICAST;
import static com.android.net.module.util.NetworkStackConstants.IPV6_HEADER_LEN;
import static com.android.net.module.util.NetworkStackConstants.IPV6_PROTOCOL_OFFSET;
import static com.android.net.module.util.NetworkStackConstants.NEIGHBOR_ADVERTISEMENT_FLAG_OVERRIDE;
import static com.android.net.module.util.NetworkStackConstants.NEIGHBOR_ADVERTISEMENT_FLAG_ROUTER;
import static com.android.net.module.util.NetworkStackConstants.NEIGHBOR_ADVERTISEMENT_FLAG_SOLICITED;
import static com.android.net.module.util.NetworkStackConstants.PIO_FLAG_AUTONOMOUS;
@@ -474,6 +477,11 @@ public abstract class IpClientIntegrationTestCommon {
                        NeighborEventConsumer cb) {
                    return new IpNeighborMonitor(h, log, cb);
                }

                public boolean isFeatureEnabled(final Context context, final String name,
                        boolean defaultEnabled) {
                    return Dependencies.this.isFeatureEnabled(context, name, defaultEnabled);
                }
            };
        }

@@ -3094,7 +3102,50 @@ public abstract class IpClientIntegrationTestCommon {
        assertNeighborSolicitation(ns, target);
    }

    private void assertMulticastNeighborSolicitation(final NeighborSolicitation ns,
            final Inet6Address target) {
        final MacAddress etherMulticast =
                NetworkStackUtils.ipv6MulticastToEthernetMulticast(ns.ipv6Hdr.dstIp);
        assertEquals(etherMulticast, ns.ethHdr.dstMac);
        assertTrue(ns.ipv6Hdr.dstIp.isMulticastAddress());
        assertNeighborSolicitation(ns, target);
    }

    private NeighborSolicitation waitForUnicastNeighborSolicitation(final MacAddress dstMac,
            final Inet6Address dstIp, final Inet6Address targetIp) throws Exception {
        NeighborSolicitation ns;
        while ((ns = getNextNeighborSolicitation()) != null) {
            // Filter out the NSes used for duplicate address detetction, the target address
            // is the global IPv6 address inside these NSes.
            if (ns.nsHdr.target.isLinkLocalAddress()) break;
        }
        assertNotNull("No unicast Neighbor solicitation received on interface within timeout", ns);
        assertUnicastNeighborSolicitation(ns, dstMac, dstIp, targetIp);
        return ns;
    }

    private List<NeighborSolicitation> waitForMultipleNeighborSolicitations() throws Exception {
        NeighborSolicitation ns;
        final List<NeighborSolicitation> nsList = new ArrayList<NeighborSolicitation>();
        while ((ns = getNextNeighborSolicitation()) != null) {
            // Filter out the NSes used for duplicate address detetction, the target address
            // is the global IPv6 address inside these NSes.
            if (ns.nsHdr.target.isLinkLocalAddress()) {
                nsList.add(ns);
            }
        }
        assertFalse(nsList.isEmpty());
        return nsList;
    }

    // Override this function with disabled experiment flag by default, in order not to
    // affect those tests which are just related to basic IpReachabilityMonitor infra.
    private void prepareIpReachabilityMonitorTest() throws Exception {
        prepareIpReachabilityMonitorTest(false /* isMulticastResolicitEnabled */);
    }

    private void prepareIpReachabilityMonitorTest(boolean isMulticastResolicitEnabled)
            throws Exception {
        final ScanResultInfo info = makeScanResultInfo(TEST_DEFAULT_SSID, TEST_DEFAULT_BSSID);
        ProvisioningConfiguration config = new ProvisioningConfiguration.Builder()
                .withLayer2Information(new Layer2Information(TEST_L2KEY, TEST_CLUSTER,
@@ -3103,6 +3154,8 @@ public abstract class IpClientIntegrationTestCommon {
                .withDisplayName(TEST_DEFAULT_SSID)
                .withoutIPv4()
                .build();
        setFeatureEnabled(NetworkStackUtils.IP_REACHABILITY_MCAST_RESOLICIT_VERSION,
                isMulticastResolicitEnabled);
        startIpClientProvisioning(config);
        verify(mCb, timeout(TEST_TIMEOUT_MS)).setFallbackMulticastFilter(false);
        doIpv6OnlyProvisioning();
@@ -3115,16 +3168,8 @@ public abstract class IpClientIntegrationTestCommon {
    public void testIpReachabilityMonitor_probeFailed() throws Exception {
        prepareIpReachabilityMonitorTest();

        NeighborSolicitation packet;
        final List<NeighborSolicitation> nsList = new ArrayList<NeighborSolicitation>();
        while ((packet = getNextNeighborSolicitation()) != null) {
            // Filter out the NSes used for duplicate address detetction, the target address
            // is the global IPv6 address inside these NSes.
            if (packet.nsHdr.target.isLinkLocalAddress()) {
                nsList.add(packet);
            }
        }
        assertEquals(IpReachabilityMonitor.MIN_NUD_SOLICIT_NUM, nsList.size());
        final List<NeighborSolicitation> nsList = waitForMultipleNeighborSolicitations();
        assertEquals(MIN_NUD_SOLICIT_NUM, nsList.size());
        for (NeighborSolicitation ns : nsList) {
            assertUnicastNeighborSolicitation(ns, ROUTER_MAC /* dstMac */,
                    ROUTER_LINK_LOCAL /* dstIp */, ROUTER_LINK_LOCAL /* targetIp */);
@@ -3136,14 +3181,42 @@ public abstract class IpClientIntegrationTestCommon {
    public void testIpReachabilityMonitor_probeReachable() throws Exception {
        prepareIpReachabilityMonitorTest();

        NeighborSolicitation ns;
        while ((ns = getNextNeighborSolicitation()) != null) {
            // Filter out the NSes used for duplicate address detetction, the target address
            // is the global IPv6 address inside these NSes.
            if (ns.nsHdr.target.isLinkLocalAddress()) break;
        final NeighborSolicitation ns = waitForUnicastNeighborSolicitation(ROUTER_MAC /* dstMac */,
                ROUTER_LINK_LOCAL /* dstIp */, ROUTER_LINK_LOCAL /* targetIp */);

        // Reply Neighbor Advertisement and check notifyLost callback won't be triggered.
        int flag = NEIGHBOR_ADVERTISEMENT_FLAG_ROUTER | NEIGHBOR_ADVERTISEMENT_FLAG_SOLICITED;
        final ByteBuffer na = NeighborAdvertisement.build(ROUTER_MAC /* srcMac */,
                ns.ethHdr.srcMac /* dstMac */, ROUTER_LINK_LOCAL /* srcIp */,
                ns.ipv6Hdr.srcIp /* dstIp */, flag, ROUTER_LINK_LOCAL /* target */);
        mPacketReader.sendResponse(na);
        assertNeverNotifyNeighborLost();
    }

    @Test
    public void testIpReachabilityMonitor_mcastResoclicitProbeFailed() throws Exception {
        prepareIpReachabilityMonitorTest(true /* isMulticastResolicitEnabled */);

        final List<NeighborSolicitation> nsList = waitForMultipleNeighborSolicitations();
        int expectedSize = MIN_NUD_SOLICIT_NUM + NUD_MCAST_RESOLICIT_NUM;
        assertEquals(expectedSize, nsList.size()); // 5 unicast NSes + 3 multicast NSes
        for (NeighborSolicitation ns : nsList.subList(0, MIN_NUD_SOLICIT_NUM)) {
            assertUnicastNeighborSolicitation(ns, ROUTER_MAC /* dstMac */,
                    ROUTER_LINK_LOCAL /* dstIp */, ROUTER_LINK_LOCAL /* targetIp */);
        }
        for (NeighborSolicitation ns : nsList.subList(MIN_NUD_SOLICIT_NUM, nsList.size())) {
            assertMulticastNeighborSolicitation(ns, ROUTER_LINK_LOCAL /* targetIp */);
        }
        assertNotifyNeighborLost(ROUTER_LINK_LOCAL /* targetIp */);
    }

    @Test
    public void testIpReachabilityMonitor_mcastResoclicitProbeReachableWithSameLinkLayerAddress()
            throws Exception {
        prepareIpReachabilityMonitorTest(true /* isMulticastResolicitEnabled */);

        final NeighborSolicitation ns = waitForUnicastNeighborSolicitation(ROUTER_MAC /* dstMac */,
                ROUTER_LINK_LOCAL /* dstIp */, ROUTER_LINK_LOCAL /* targetIp */);

        // Reply Neighbor Advertisement and check notifyLost callback won't be triggered.
        int flag = NEIGHBOR_ADVERTISEMENT_FLAG_ROUTER | NEIGHBOR_ADVERTISEMENT_FLAG_SOLICITED;
@@ -3153,4 +3226,27 @@ public abstract class IpClientIntegrationTestCommon {
        mPacketReader.sendResponse(na);
        assertNeverNotifyNeighborLost();
    }

    @Test
    public void testIpReachabilityMonitor_mcastResoclicitProbeReachableWithDiffLinkLayerAddress()
            throws Exception {
        prepareIpReachabilityMonitorTest(true /* isMulticastResolicitEnabled */);

        final NeighborSolicitation ns = waitForUnicastNeighborSolicitation(ROUTER_MAC /* dstMac */,
                ROUTER_LINK_LOCAL /* dstIp */, ROUTER_LINK_LOCAL /* targetIp */);

        // Reply Neighbor Advertisement with a different link-layer address and check notifyLost
        // callback will be triggered. Override flag must be set, which indicates that the
        // advertisement should override an existing cache entry and update the cached link-layer
        // address, otherwise, kernel won't transit to REACHABLE state with a different link-layer
        // address.
        int flag = NEIGHBOR_ADVERTISEMENT_FLAG_ROUTER | NEIGHBOR_ADVERTISEMENT_FLAG_SOLICITED
                | NEIGHBOR_ADVERTISEMENT_FLAG_OVERRIDE;
        final MacAddress newMac = MacAddress.fromString("00:1a:11:22:33:55");
        final ByteBuffer na = NeighborAdvertisement.build(newMac /* srcMac */,
                ns.ethHdr.srcMac /* dstMac */, ROUTER_LINK_LOCAL /* srcIp */,
                ns.ipv6Hdr.srcIp /* dstIp */, flag, ROUTER_LINK_LOCAL /* target */);
        mPacketReader.sendResponse(na);
        assertNotifyNeighborLost(ROUTER_LINK_LOCAL /* targetIp */);
    }
}