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

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

Merge "Optimize neighbor unreachability detection probe fallback." am:...

Merge "Optimize neighbor unreachability detection probe fallback." am: 1aecd5e8 am: e0f24af4 am: 66d744cf

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

Change-Id: Ifd0cb6918d40aa613538f1c36913103537548fc5
parents d42977ad 66d744cf
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 */);
    }
}