| /* |
| * Copyright (C) 2017 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| package android.net.ip |
| |
| import android.annotation.SuppressLint |
| import android.content.Context |
| import android.net.ip.IpNeighborMonitor.NeighborEventConsumer |
| import android.net.INetd |
| import android.net.InetAddresses.parseNumericAddress |
| import android.net.IpPrefix |
| import android.net.LinkAddress |
| import android.net.LinkProperties |
| import android.net.RouteInfo |
| import android.net.metrics.IpConnectivityLog |
| import android.net.util.InterfaceParams |
| import android.net.util.NetworkStackUtils.IP_REACHABILITY_MCAST_RESOLICIT_VERSION |
| import android.net.util.SharedLog |
| import android.os.Handler |
| import android.os.HandlerThread |
| import android.os.MessageQueue |
| import android.os.MessageQueue.OnFileDescriptorEventListener |
| import android.stats.connectivity.IpType |
| import android.stats.connectivity.IpType.IPV4 |
| import android.stats.connectivity.IpType.IPV6 |
| import android.stats.connectivity.NudEventType |
| import android.stats.connectivity.NudEventType.NUD_CONFIRM_FAILED |
| import android.stats.connectivity.NudEventType.NUD_CONFIRM_FAILED_CRITICAL |
| import android.stats.connectivity.NudEventType.NUD_CONFIRM_MAC_ADDRESS_CHANGED |
| import android.stats.connectivity.NudEventType.NUD_POST_ROAMING_FAILED |
| import android.stats.connectivity.NudEventType.NUD_POST_ROAMING_FAILED_CRITICAL |
| import android.stats.connectivity.NudEventType.NUD_POST_ROAMING_MAC_ADDRESS_CHANGED |
| import android.stats.connectivity.NudEventType.NUD_ORGANIC_FAILED |
| import android.stats.connectivity.NudEventType.NUD_ORGANIC_FAILED_CRITICAL |
| import android.stats.connectivity.NudEventType.NUD_ORGANIC_MAC_ADDRESS_CHANGED |
| import android.stats.connectivity.NudNeighborType |
| import android.stats.connectivity.NudNeighborType.NUD_NEIGHBOR_BOTH |
| import android.stats.connectivity.NudNeighborType.NUD_NEIGHBOR_DNS |
| import android.stats.connectivity.NudNeighborType.NUD_NEIGHBOR_GATEWAY |
| import android.system.ErrnoException |
| import android.system.OsConstants.EAGAIN |
| import androidx.test.filters.SmallTest |
| import androidx.test.runner.AndroidJUnit4 |
| import com.android.networkstack.metrics.IpReachabilityMonitorMetrics |
| import com.android.net.module.util.netlink.StructNdMsg.NUD_FAILED |
| import com.android.net.module.util.netlink.StructNdMsg.NUD_REACHABLE |
| import com.android.net.module.util.netlink.StructNdMsg.NUD_STALE |
| import com.android.testutils.makeNewNeighMessage |
| import com.android.testutils.waitForIdle |
| import org.junit.After |
| import org.junit.Before |
| import org.junit.Test |
| import org.junit.runner.RunWith |
| import org.mockito.ArgumentCaptor |
| import org.mockito.ArgumentMatchers.any |
| import org.mockito.ArgumentMatchers.anyBoolean |
| import org.mockito.ArgumentMatchers.anyInt |
| import org.mockito.ArgumentMatchers.anyObject |
| import org.mockito.ArgumentMatchers.anyString |
| import org.mockito.ArgumentMatchers.eq |
| import org.mockito.Mockito.doAnswer |
| import org.mockito.Mockito.doReturn |
| import org.mockito.Mockito.mock |
| import org.mockito.Mockito.never |
| import org.mockito.Mockito.timeout |
| import org.mockito.Mockito.verify |
| import java.io.FileDescriptor |
| import java.net.Inet4Address |
| import java.net.Inet6Address |
| import java.net.InetAddress |
| import java.util.concurrent.CompletableFuture |
| import java.util.concurrent.ConcurrentLinkedQueue |
| import java.util.concurrent.TimeUnit |
| import kotlin.test.assertTrue |
| import kotlin.test.fail |
| |
| private const val TEST_TIMEOUT_MS = 10_000L |
| |
| private val TEST_IPV4_GATEWAY = parseNumericAddress("192.168.222.3") as Inet4Address |
| private val TEST_IPV6_GATEWAY = parseNumericAddress("2001:db8::1") as Inet6Address |
| |
| // IPv4 gateway is also DNS server. |
| private val TEST_IPV4_GATEWAY_DNS = parseNumericAddress("192.168.222.100") as Inet4Address |
| |
| private val TEST_IPV4_LINKADDR = LinkAddress("192.168.222.123/24") |
| private val TEST_IPV6_LINKADDR = LinkAddress("2001:db8::123/64") |
| |
| private val TEST_IPV6_LINKLOCAL_LINKADDR = LinkAddress("fe80::123/64") |
| private val TEST_IPV6_LINKLOCAL_GATEWAY = parseNumericAddress("fe80::1") as Inet6Address |
| private val TEST_IPV6_LINKLOCAL_SCOPED_GATEWAY = parseNumericAddress("fe80::1%21") as Inet6Address |
| |
| // DNSes inside IP prefix |
| private val TEST_IPV4_DNS = parseNumericAddress("192.168.222.1") as Inet4Address |
| private val TEST_IPV6_DNS = parseNumericAddress("2001:db8::321") as Inet6Address |
| private val TEST_IPV6_DNS2 = parseNumericAddress("2001:db8::456") as Inet6Address |
| |
| private val TEST_IFACE = InterfaceParams("fake0", 21, null) |
| |
| @SuppressLint("NewApi") |
| private val TEST_LINK_PROPERTIES = LinkProperties().apply { |
| interfaceName = TEST_IFACE.name |
| addLinkAddress(TEST_IPV4_LINKADDR) |
| addLinkAddress(TEST_IPV6_LINKADDR) |
| |
| // Add on link routes |
| addRoute(RouteInfo(TEST_IPV4_LINKADDR, null /* gateway */, TEST_IFACE.name)) |
| addRoute(RouteInfo(TEST_IPV6_LINKADDR, null /* gateway */, TEST_IFACE.name)) |
| |
| // Add default routes |
| addRoute(RouteInfo(IpPrefix(parseNumericAddress("0.0.0.0"), 0), TEST_IPV4_GATEWAY)) |
| addRoute(RouteInfo(IpPrefix(parseNumericAddress("::"), 0), TEST_IPV6_GATEWAY)) |
| |
| addDnsServer(TEST_IPV4_DNS) |
| addDnsServer(TEST_IPV6_DNS) |
| } |
| |
| @SuppressLint("NewApi") |
| private val TEST_IPV4_ONLY_LINK_PROPERTIES = LinkProperties().apply { |
| interfaceName = TEST_IFACE.name |
| addLinkAddress(TEST_IPV4_LINKADDR) |
| |
| // Add on link routes |
| addRoute(RouteInfo(TEST_IPV4_LINKADDR, null /* gateway */, TEST_IFACE.name)) |
| |
| // Add default routes |
| addRoute(RouteInfo(IpPrefix(parseNumericAddress("0.0.0.0"), 0), TEST_IPV4_GATEWAY_DNS)) |
| |
| addDnsServer(TEST_IPV4_GATEWAY_DNS) |
| } |
| |
| @SuppressLint("NewApi") |
| private val TEST_IPV6_LINKLOCAL_SCOPED_LINK_PROPERTIES = LinkProperties().apply { |
| interfaceName = TEST_IFACE.name |
| addLinkAddress(TEST_IPV6_LINKADDR) |
| addLinkAddress(TEST_IPV6_LINKLOCAL_LINKADDR) |
| |
| // Add on link routes |
| addRoute(RouteInfo(TEST_IPV6_LINKADDR, null /* gateway */, TEST_IFACE.name)) |
| addRoute(RouteInfo(TEST_IPV6_LINKLOCAL_LINKADDR, null /* gateway */, TEST_IFACE.name)) |
| |
| // Add default routes |
| addRoute(RouteInfo(IpPrefix(parseNumericAddress("::"), 0), TEST_IPV6_LINKLOCAL_SCOPED_GATEWAY)) |
| |
| addDnsServer(TEST_IPV6_DNS) |
| } |
| |
| @SuppressLint("NewApi") |
| private val TEST_DUAL_LINK_PROPERTIES = LinkProperties().apply { |
| interfaceName = TEST_IFACE.name |
| addLinkAddress(TEST_IPV4_LINKADDR) |
| addLinkAddress(TEST_IPV6_LINKADDR) |
| addLinkAddress(TEST_IPV6_LINKLOCAL_LINKADDR) |
| |
| // Add on link routes |
| addRoute(RouteInfo(TEST_IPV4_LINKADDR, null /* gateway */, TEST_IFACE.name)) |
| addRoute(RouteInfo(TEST_IPV6_LINKADDR, null /* gateway */, TEST_IFACE.name)) |
| addRoute(RouteInfo(TEST_IPV6_LINKLOCAL_LINKADDR, null /* gateway */, TEST_IFACE.name)) |
| |
| // Add default routes |
| addRoute(RouteInfo(IpPrefix(parseNumericAddress("0.0.0.0"), 0), TEST_IPV4_GATEWAY)) |
| addRoute(RouteInfo(IpPrefix(parseNumericAddress("::"), 0), TEST_IPV6_LINKLOCAL_SCOPED_GATEWAY)) |
| |
| addDnsServer(TEST_IPV4_DNS) |
| addDnsServer(TEST_IPV6_DNS) |
| addDnsServer(TEST_IPV6_DNS2) |
| } |
| |
| /** |
| * Tests for IpReachabilityMonitor. |
| */ |
| @RunWith(AndroidJUnit4::class) |
| @SmallTest |
| class IpReachabilityMonitorTest { |
| private val callback = mock(IpReachabilityMonitor.Callback::class.java) |
| private val dependencies = mock(IpReachabilityMonitor.Dependencies::class.java) |
| private val log = mock(SharedLog::class.java) |
| private val context = mock(Context::class.java) |
| private val netd = mock(INetd::class.java) |
| private val fd = mock(FileDescriptor::class.java) |
| private val metricsLog = mock(IpConnectivityLog::class.java) |
| private val mIpReachabilityMonitorMetrics = mock(IpReachabilityMonitorMetrics::class.java) |
| |
| private val handlerThread = HandlerThread(IpReachabilityMonitorTest::class.simpleName) |
| private val handler by lazy { Handler(handlerThread.looper) } |
| |
| private lateinit var reachabilityMonitor: IpReachabilityMonitor |
| private lateinit var neighborMonitor: TestIpNeighborMonitor |
| |
| /** |
| * A version of [IpNeighborMonitor] that overrides packet reading from a socket, and instead |
| * allows the test to enqueue test packets via [enqueuePacket]. |
| */ |
| private class TestIpNeighborMonitor( |
| handler: Handler, |
| log: SharedLog, |
| cb: NeighborEventConsumer, |
| private val fd: FileDescriptor |
| ) : IpNeighborMonitor(handler, log, cb) { |
| |
| private val pendingPackets = ConcurrentLinkedQueue<ByteArray>() |
| val msgQueue = mock(MessageQueue::class.java) |
| |
| private var eventListener: OnFileDescriptorEventListener? = null |
| |
| override fun createFd() = fd |
| override fun getMessageQueue() = msgQueue |
| |
| fun enqueuePacket(packet: ByteArray) { |
| val listener = eventListener ?: fail("IpNeighborMonitor was not yet started") |
| pendingPackets.add(packet) |
| handler.post { |
| listener.onFileDescriptorEvents(fd, OnFileDescriptorEventListener.EVENT_INPUT) |
| } |
| } |
| |
| override fun readPacket(fd: FileDescriptor, packetBuffer: ByteArray): Int { |
| val packet = pendingPackets.poll() ?: throw ErrnoException("No pending packet", EAGAIN) |
| if (packet.size > packetBuffer.size) { |
| fail("Buffer (${packetBuffer.size}) is too small for packet (${packet.size})") |
| } |
| System.arraycopy(packet, 0, packetBuffer, 0, packet.size) |
| return packet.size |
| } |
| |
| override fun onStart() { |
| super.onStart() |
| |
| // Find the file descriptor listener that was registered on the instrumented queue |
| val captor = ArgumentCaptor.forClass(OnFileDescriptorEventListener::class.java) |
| verify(msgQueue).addOnFileDescriptorEventListener( |
| eq(fd), anyInt(), captor.capture()) |
| eventListener = captor.value |
| } |
| } |
| |
| @Before |
| fun setUp() { |
| doReturn(log).`when`(log).forSubComponent(anyString()) |
| doReturn(true).`when`(fd).valid() |
| handlerThread.start() |
| |
| doAnswer { inv -> |
| val handler = inv.getArgument<Handler>(0) |
| val log = inv.getArgument<SharedLog>(1) |
| val cb = inv.getArgument<IpNeighborMonitor.NeighborEventConsumer>(2) |
| neighborMonitor = TestIpNeighborMonitor(handler, log, cb, fd) |
| neighborMonitor |
| }.`when`(dependencies).makeIpNeighborMonitor(any(), any(), any()) |
| doReturn(mIpReachabilityMonitorMetrics) |
| .`when`(dependencies).getIpReachabilityMonitorMetrics() |
| |
| val monitorFuture = CompletableFuture<IpReachabilityMonitor>() |
| // IpReachabilityMonitor needs to be started from the handler thread |
| handler.post { |
| monitorFuture.complete(IpReachabilityMonitor( |
| context, |
| TEST_IFACE, |
| handler, |
| log, |
| callback, |
| false /* useMultinetworkPolicyTracker */, |
| dependencies, |
| metricsLog, |
| netd)) |
| } |
| reachabilityMonitor = monitorFuture.get(TEST_TIMEOUT_MS, TimeUnit.MILLISECONDS) |
| assertTrue(::neighborMonitor.isInitialized, |
| "IpReachabilityMonitor did not call makeIpNeighborMonitor") |
| } |
| |
| @After |
| fun tearDown() { |
| // Ensure the handler thread is not accessing the fd while changing its mock |
| handlerThread.waitForIdle(TEST_TIMEOUT_MS) |
| doReturn(false).`when`(fd).valid() |
| handlerThread.quitSafely() |
| } |
| |
| @Test |
| fun testLoseProvisioning_FirstProbeIsFailed() { |
| reachabilityMonitor.updateLinkProperties(TEST_LINK_PROPERTIES) |
| |
| neighborMonitor.enqueuePacket(makeNewNeighMessage(TEST_IPV4_DNS, NUD_FAILED)) |
| verify(callback, timeout(TEST_TIMEOUT_MS)).notifyLost(eq(TEST_IPV4_DNS), anyString(), |
| eq(NUD_ORGANIC_FAILED_CRITICAL)) |
| } |
| |
| private fun runLoseProvisioningTest( |
| newLp: LinkProperties, |
| lostNeighbor: InetAddress, |
| eventType: NudEventType |
| ) { |
| reachabilityMonitor.updateLinkProperties(newLp) |
| |
| neighborMonitor.enqueuePacket(makeNewNeighMessage(TEST_IPV4_GATEWAY, NUD_STALE)) |
| neighborMonitor.enqueuePacket(makeNewNeighMessage(TEST_IPV6_GATEWAY, NUD_STALE)) |
| neighborMonitor.enqueuePacket(makeNewNeighMessage(TEST_IPV4_DNS, NUD_STALE)) |
| neighborMonitor.enqueuePacket(makeNewNeighMessage(TEST_IPV6_DNS, NUD_STALE)) |
| |
| neighborMonitor.enqueuePacket(makeNewNeighMessage(lostNeighbor, NUD_FAILED)) |
| verify(callback, timeout(TEST_TIMEOUT_MS)).notifyLost(eq(lostNeighbor), anyString(), |
| eq(eventType)) |
| } |
| |
| private fun verifyNudFailureMetrics( |
| eventType: NudEventType, |
| ipType: IpType, |
| lostNeighborType: NudNeighborType |
| ) { |
| verify(mIpReachabilityMonitorMetrics, timeout(TEST_TIMEOUT_MS)).setNudIpType(eq(ipType)) |
| verify(mIpReachabilityMonitorMetrics, timeout(TEST_TIMEOUT_MS)) |
| .setNudEventType(eq(eventType)) |
| verify(mIpReachabilityMonitorMetrics, timeout(TEST_TIMEOUT_MS)) |
| .setNudNeighborType(eq(lostNeighborType)) |
| } |
| |
| // Verify if the notifyLost will be called when one neighbor has lost but it's still |
| // provisioned. |
| private fun runLoseNeighborStillProvisionedTest( |
| newLp: LinkProperties, |
| lostNeighbor: InetAddress, |
| eventType: NudEventType, |
| ipType: IpType, |
| lostNeighborType: NudNeighborType |
| ) { |
| reachabilityMonitor.updateLinkProperties(newLp) |
| |
| neighborMonitor.enqueuePacket(makeNewNeighMessage(lostNeighbor, NUD_FAILED)) |
| handlerThread.waitForIdle(TEST_TIMEOUT_MS) |
| verify(callback, never()).notifyLost(any(), anyString(), any(NudEventType::class.java)) |
| verifyNudFailureMetrics(eventType, ipType, lostNeighborType) |
| } |
| |
| private fun prepareNeighborReachableButMacAddrChangedTest( |
| newLp: LinkProperties, |
| neighbor: InetAddress |
| ) { |
| doReturn(true).`when`(dependencies).isFeatureEnabled(anyObject(), |
| eq(IP_REACHABILITY_MCAST_RESOLICIT_VERSION), anyBoolean()) |
| |
| reachabilityMonitor.updateLinkProperties(newLp) |
| |
| neighborMonitor.enqueuePacket(makeNewNeighMessage(neighbor, NUD_REACHABLE, |
| "001122334455" /* oldMac */)) |
| handlerThread.waitForIdle(TEST_TIMEOUT_MS) |
| verify(callback, never()).notifyLost(eq(neighbor), anyString(), |
| any(NudEventType::class.java)) |
| } |
| |
| @Test |
| fun testLoseProvisioning_Ipv4DnsLost() { |
| runLoseProvisioningTest(TEST_LINK_PROPERTIES, TEST_IPV4_DNS, NUD_ORGANIC_FAILED_CRITICAL) |
| } |
| |
| @Test |
| fun testLoseProvisioning_Ipv6DnsLost() { |
| runLoseProvisioningTest(TEST_LINK_PROPERTIES, TEST_IPV6_DNS, NUD_ORGANIC_FAILED_CRITICAL) |
| } |
| |
| @Test |
| fun testLoseProvisioning_Ipv4GatewayLost() { |
| runLoseProvisioningTest(TEST_LINK_PROPERTIES, TEST_IPV4_GATEWAY, |
| NUD_ORGANIC_FAILED_CRITICAL) |
| } |
| |
| @Test |
| fun testLoseProvisioning_Ipv6GatewayLost() { |
| runLoseProvisioningTest(TEST_LINK_PROPERTIES, TEST_IPV6_GATEWAY, |
| NUD_ORGANIC_FAILED_CRITICAL) |
| } |
| |
| private fun runNudProbeFailureMetricsTest( |
| lp: LinkProperties, |
| lostNeighbor: InetAddress, |
| eventType: NudEventType, |
| ipType: IpType, |
| lostNeighborType: NudNeighborType |
| ) { |
| runLoseProvisioningTest(lp, lostNeighbor, eventType) |
| verifyNudFailureMetrics(eventType, ipType, lostNeighborType) |
| } |
| |
| @Test |
| fun testNudProbeFailedMetrics_Ipv6GatewayLostPostRoaming() { |
| reachabilityMonitor.probeAll(true /* dueToRoam */) |
| runNudProbeFailureMetricsTest(TEST_LINK_PROPERTIES, TEST_IPV6_GATEWAY, |
| NUD_POST_ROAMING_FAILED_CRITICAL, IPV6, NUD_NEIGHBOR_GATEWAY) |
| } |
| |
| @Test |
| fun testNudProbeFailedMetrics_Ipv4GatewayLostPostRoaming() { |
| reachabilityMonitor.probeAll(true /* dueToRoam */) |
| runNudProbeFailureMetricsTest(TEST_LINK_PROPERTIES, TEST_IPV4_GATEWAY, |
| NUD_POST_ROAMING_FAILED_CRITICAL, IPV4, NUD_NEIGHBOR_GATEWAY) |
| } |
| |
| @Test |
| fun testNudProbeFailedMetrics_Ipv6DnsLostPostRoaming() { |
| reachabilityMonitor.probeAll(true /* dueToRoam */) |
| runNudProbeFailureMetricsTest(TEST_LINK_PROPERTIES, TEST_IPV6_DNS, |
| NUD_POST_ROAMING_FAILED_CRITICAL, IPV6, NUD_NEIGHBOR_DNS) |
| } |
| |
| @Test |
| fun testNudProbeFailedMetrics_Ipv4DnsLostPostRoaming() { |
| reachabilityMonitor.probeAll(true /* dueToRoam */) |
| runNudProbeFailureMetricsTest(TEST_LINK_PROPERTIES, TEST_IPV4_DNS, |
| NUD_POST_ROAMING_FAILED_CRITICAL, IPV4, NUD_NEIGHBOR_DNS) |
| } |
| |
| @Test |
| fun testNudProbeFailedMetrics_IPv4BothGatewayAndDnsLostPostRoaming() { |
| reachabilityMonitor.probeAll(true /* dueToRoam */) |
| runNudProbeFailureMetricsTest(TEST_IPV4_ONLY_LINK_PROPERTIES, TEST_IPV4_GATEWAY_DNS, |
| NUD_POST_ROAMING_FAILED_CRITICAL, IPV4, NUD_NEIGHBOR_BOTH) |
| } |
| |
| @Test |
| fun testNudProbeFailedMetrics_IPv6LinklocalScopedGatewayLostPostRoaming() { |
| reachabilityMonitor.probeAll(true /* dueToRoam */) |
| runNudProbeFailureMetricsTest(TEST_IPV6_LINKLOCAL_SCOPED_LINK_PROPERTIES, |
| TEST_IPV6_LINKLOCAL_SCOPED_GATEWAY, NUD_POST_ROAMING_FAILED_CRITICAL, IPV6, |
| NUD_NEIGHBOR_GATEWAY) |
| } |
| |
| @Test |
| fun testNudProbeFailedMetrics_Ipv6GatewayLostAfterConfirm() { |
| reachabilityMonitor.probeAll(false /* dueToRoam */) |
| runNudProbeFailureMetricsTest(TEST_LINK_PROPERTIES, TEST_IPV6_GATEWAY, |
| NUD_CONFIRM_FAILED_CRITICAL, IPV6, NUD_NEIGHBOR_GATEWAY) |
| } |
| |
| @Test |
| fun testNudProbeFailedMetrics_Ipv4GatewayLostAfterConfirm() { |
| reachabilityMonitor.probeAll(false /* dueToRoam */) |
| runNudProbeFailureMetricsTest(TEST_LINK_PROPERTIES, TEST_IPV4_GATEWAY, |
| NUD_CONFIRM_FAILED_CRITICAL, IPV4, NUD_NEIGHBOR_GATEWAY) |
| } |
| |
| @Test |
| fun testNudProbeFailedMetrics_Ipv6DnsLostAfterConfirm() { |
| reachabilityMonitor.probeAll(false /* dueToRoam */) |
| runNudProbeFailureMetricsTest(TEST_LINK_PROPERTIES, TEST_IPV6_DNS, |
| NUD_CONFIRM_FAILED_CRITICAL, IPV6, NUD_NEIGHBOR_DNS) |
| } |
| |
| @Test |
| fun testNudProbeFailedMetrics_Ipv4DnsLostAfterConfirm() { |
| reachabilityMonitor.probeAll(false /* dueToRoam */) |
| runNudProbeFailureMetricsTest(TEST_LINK_PROPERTIES, TEST_IPV4_DNS, |
| NUD_CONFIRM_FAILED_CRITICAL, IPV4, NUD_NEIGHBOR_DNS) |
| } |
| |
| @Test |
| fun testNudProbeFailedMetrics_IPv4BothGatewayAndDnsLostAfterConfirm() { |
| reachabilityMonitor.probeAll(false /* dueToRoam */) |
| runNudProbeFailureMetricsTest(TEST_IPV4_ONLY_LINK_PROPERTIES, TEST_IPV4_GATEWAY_DNS, |
| NUD_CONFIRM_FAILED_CRITICAL, IPV4, NUD_NEIGHBOR_BOTH) |
| } |
| |
| @Test |
| fun testNudProbeFailedMetrics_IPv6LinklocalScopedGatewayLostAfterConfirm() { |
| reachabilityMonitor.probeAll(false /* dueToRoam */) |
| runNudProbeFailureMetricsTest(TEST_IPV6_LINKLOCAL_SCOPED_LINK_PROPERTIES, |
| TEST_IPV6_LINKLOCAL_SCOPED_GATEWAY, NUD_CONFIRM_FAILED_CRITICAL, IPV6, |
| NUD_NEIGHBOR_GATEWAY) |
| } |
| |
| @Test |
| fun testNudProbeFailedMetrics_IPv6GatewayLostOrganic() { |
| runNudProbeFailureMetricsTest(TEST_LINK_PROPERTIES, TEST_IPV6_GATEWAY, |
| NUD_ORGANIC_FAILED_CRITICAL, IPV6, NUD_NEIGHBOR_GATEWAY) |
| } |
| |
| @Test |
| fun testNudProbeFailedMetrics_IPv4GatewayLostOrganic() { |
| runNudProbeFailureMetricsTest(TEST_LINK_PROPERTIES, TEST_IPV4_GATEWAY, |
| NUD_ORGANIC_FAILED_CRITICAL, IPV4, NUD_NEIGHBOR_GATEWAY) |
| } |
| |
| @Test |
| fun testNudProbeFailedMetrics_IPv6DnsLostOrganic() { |
| runNudProbeFailureMetricsTest(TEST_LINK_PROPERTIES, TEST_IPV6_DNS, |
| NUD_ORGANIC_FAILED_CRITICAL, IPV6, NUD_NEIGHBOR_DNS) |
| } |
| |
| @Test |
| fun testNudProbeFailedMetrics_IPv4DnsLostOrganic() { |
| runNudProbeFailureMetricsTest(TEST_LINK_PROPERTIES, TEST_IPV4_DNS, |
| NUD_ORGANIC_FAILED_CRITICAL, IPV4, NUD_NEIGHBOR_DNS) |
| } |
| |
| @Test |
| fun testNudProbeFailedMetrics_IPv4BothGatewayAndDnsLostOrganic() { |
| runNudProbeFailureMetricsTest(TEST_IPV4_ONLY_LINK_PROPERTIES, TEST_IPV4_GATEWAY_DNS, |
| NUD_ORGANIC_FAILED_CRITICAL, IPV4, NUD_NEIGHBOR_BOTH) |
| } |
| |
| @Test |
| fun testNudProbeFailedMetrics_IPv6LinklocalScopedGatewayLostOrganic() { |
| runNudProbeFailureMetricsTest(TEST_IPV6_LINKLOCAL_SCOPED_LINK_PROPERTIES, |
| TEST_IPV6_LINKLOCAL_SCOPED_GATEWAY, NUD_ORGANIC_FAILED_CRITICAL, IPV6, |
| NUD_NEIGHBOR_GATEWAY) |
| } |
| |
| @Test |
| fun testNudProbeFailedMetrics_IPv6OneDnsNeighborLostPostRoaming() { |
| reachabilityMonitor.probeAll(true /* dueToRoam */) |
| runLoseNeighborStillProvisionedTest(TEST_DUAL_LINK_PROPERTIES, TEST_IPV6_DNS, |
| NUD_POST_ROAMING_FAILED, IPV6, NUD_NEIGHBOR_DNS) |
| } |
| |
| @Test |
| fun testNudProbeFailedMetrics_IPv6OneDnsNeighborLostAfterConfirm() { |
| reachabilityMonitor.probeAll(false /* dueToRoam */) |
| runLoseNeighborStillProvisionedTest(TEST_DUAL_LINK_PROPERTIES, TEST_IPV6_DNS, |
| NUD_CONFIRM_FAILED, IPV6, NUD_NEIGHBOR_DNS) |
| } |
| |
| @Test |
| fun testNudProbeFailedMetrics_IPv6OneDnsNeighborLostOrganic() { |
| runLoseNeighborStillProvisionedTest(TEST_DUAL_LINK_PROPERTIES, TEST_IPV6_DNS, |
| NUD_ORGANIC_FAILED, IPV6, NUD_NEIGHBOR_DNS) |
| } |
| |
| @Test |
| fun testNudProbeFailedMetrics_multipleProbesFromRoamFirst() { |
| reachabilityMonitor.probeAll(true /* dueToRoam */) |
| handlerThread.waitForIdle(TEST_TIMEOUT_MS) |
| Thread.sleep(2) |
| reachabilityMonitor.probeAll(false /* dueToRoam */) |
| runLoseProvisioningTest(TEST_LINK_PROPERTIES, TEST_IPV6_GATEWAY, |
| NUD_POST_ROAMING_FAILED_CRITICAL) |
| |
| verifyNudFailureMetrics(NUD_POST_ROAMING_FAILED_CRITICAL, IPV6, NUD_NEIGHBOR_GATEWAY) |
| } |
| |
| @Test |
| fun testNudProbeFailedMetrics_multipleProbesFromConfirmFirst() { |
| reachabilityMonitor.probeAll(false /* dueToRoam */) |
| handlerThread.waitForIdle(TEST_TIMEOUT_MS) |
| Thread.sleep(2) |
| reachabilityMonitor.probeAll(true /* dueToRoam */) |
| runLoseProvisioningTest(TEST_LINK_PROPERTIES, TEST_IPV6_GATEWAY, |
| NUD_CONFIRM_FAILED_CRITICAL) |
| |
| verifyNudFailureMetrics(NUD_CONFIRM_FAILED_CRITICAL, IPV6, NUD_NEIGHBOR_GATEWAY) |
| } |
| |
| private fun verifyNudMacAddrChangedType( |
| neighbor: InetAddress, |
| eventType: NudEventType, |
| ipType: IpType |
| ) { |
| neighborMonitor.enqueuePacket(makeNewNeighMessage(neighbor, NUD_REACHABLE, |
| "1122334455aa" /* newMac */)) |
| verify(callback, timeout(TEST_TIMEOUT_MS)).notifyLost(eq(neighbor), anyString(), |
| eq(eventType)) |
| verifyNudFailureMetrics(eventType, ipType, NUD_NEIGHBOR_GATEWAY) |
| } |
| |
| @Test |
| fun testNudProbeFailedMetrics_defaultIPv6GatewayMacAddrChangedAfterRoaming() { |
| prepareNeighborReachableButMacAddrChangedTest(TEST_LINK_PROPERTIES, TEST_IPV6_GATEWAY) |
| |
| reachabilityMonitor.probeAll(true /* dueToRoam */) |
| verifyNudMacAddrChangedType(TEST_IPV6_GATEWAY, NUD_POST_ROAMING_MAC_ADDRESS_CHANGED, IPV6) |
| } |
| |
| @Test |
| fun testNudProbeFailedMetrics_defaultIPv4GatewayMacAddrChangedAfterRoaming() { |
| prepareNeighborReachableButMacAddrChangedTest(TEST_LINK_PROPERTIES, TEST_IPV4_GATEWAY) |
| |
| reachabilityMonitor.probeAll(true /* dueToRoam */) |
| verifyNudMacAddrChangedType(TEST_IPV4_GATEWAY, NUD_POST_ROAMING_MAC_ADDRESS_CHANGED, IPV4) |
| } |
| |
| @Test |
| fun testNudProbeFailedMetrics_defaultIPv6GatewayMacAddrChangedAfterConfirm() { |
| prepareNeighborReachableButMacAddrChangedTest(TEST_LINK_PROPERTIES, TEST_IPV6_GATEWAY) |
| |
| reachabilityMonitor.probeAll(false /* dueToRoam */) |
| verifyNudMacAddrChangedType(TEST_IPV6_GATEWAY, NUD_CONFIRM_MAC_ADDRESS_CHANGED, IPV6) |
| } |
| |
| @Test |
| fun testNudProbeFailedMetrics_defaultIPv6GatewayMacAddrChangedAfterOrganic() { |
| prepareNeighborReachableButMacAddrChangedTest(TEST_LINK_PROPERTIES, TEST_IPV6_GATEWAY) |
| |
| verifyNudMacAddrChangedType(TEST_IPV6_GATEWAY, NUD_ORGANIC_MAC_ADDRESS_CHANGED, IPV6) |
| } |
| } |