Loading src/android/net/ip/IpReachabilityMonitor.java +75 −8 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; Loading Loading @@ -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 { /** Loading @@ -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; Loading @@ -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); } }; } } Loading @@ -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; Loading @@ -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, Loading Loading @@ -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"); } Loading @@ -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(); Loading Loading @@ -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. Loading Loading @@ -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); Loading Loading @@ -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); } Loading Loading @@ -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, Loading @@ -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; Loading src/android/net/util/NetworkStackUtils.java +7 −0 Original line number Diff line number Diff line Loading @@ -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"); } Loading tests/integration/src/android/net/ip/IpClientIntegrationTestCommon.java +112 −16 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; Loading Loading @@ -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); } }; } Loading Loading @@ -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, Loading @@ -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(); Loading @@ -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 */); Loading @@ -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; Loading @@ -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 */); } } Loading
src/android/net/ip/IpReachabilityMonitor.java +75 −8 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; Loading Loading @@ -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 { /** Loading @@ -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; Loading @@ -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); } }; } } Loading @@ -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; Loading @@ -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, Loading Loading @@ -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"); } Loading @@ -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(); Loading Loading @@ -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. Loading Loading @@ -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); Loading Loading @@ -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); } Loading Loading @@ -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, Loading @@ -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; Loading
src/android/net/util/NetworkStackUtils.java +7 −0 Original line number Diff line number Diff line Loading @@ -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"); } Loading
tests/integration/src/android/net/ip/IpClientIntegrationTestCommon.java +112 −16 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; Loading Loading @@ -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); } }; } Loading Loading @@ -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, Loading @@ -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(); Loading @@ -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 */); Loading @@ -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; Loading @@ -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 */); } }