1 /*
<lambda>null2  * Copyright (C) 2017 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package android.net.ip
17 
18 import android.content.Context
19 import android.net.INetd
20 import android.net.InetAddresses.parseNumericAddress
21 import android.net.IpPrefix
22 import android.net.LinkAddress
23 import android.net.LinkProperties
24 import android.net.RouteInfo
25 import android.net.metrics.IpConnectivityLog
26 import android.net.netlink.StructNdMsg.NUD_FAILED
27 import android.net.netlink.StructNdMsg.NUD_STALE
28 import android.net.netlink.makeNewNeighMessage
29 import android.net.util.InterfaceParams
30 import android.net.util.SharedLog
31 import android.os.Handler
32 import android.os.HandlerThread
33 import android.os.MessageQueue
34 import android.os.MessageQueue.OnFileDescriptorEventListener
35 import android.system.ErrnoException
36 import android.system.OsConstants.EAGAIN
37 import androidx.test.filters.SmallTest
38 import androidx.test.runner.AndroidJUnit4
39 import com.android.testutils.waitForIdle
40 import org.junit.After
41 import org.junit.Before
42 import org.junit.Test
43 import org.junit.runner.RunWith
44 import org.mockito.ArgumentCaptor
45 import org.mockito.ArgumentMatchers.any
46 import org.mockito.ArgumentMatchers.anyInt
47 import org.mockito.ArgumentMatchers.anyString
48 import org.mockito.ArgumentMatchers.eq
49 import org.mockito.Mockito.doAnswer
50 import org.mockito.Mockito.doReturn
51 import org.mockito.Mockito.mock
52 import org.mockito.Mockito.timeout
53 import org.mockito.Mockito.verify
54 import java.io.FileDescriptor
55 import java.net.Inet4Address
56 import java.net.Inet6Address
57 import java.net.InetAddress
58 import java.util.concurrent.CompletableFuture
59 import java.util.concurrent.ConcurrentLinkedQueue
60 import java.util.concurrent.TimeUnit
61 import kotlin.test.assertTrue
62 import kotlin.test.fail
63 
64 private const val TEST_TIMEOUT_MS = 10_000L
65 
66 private val TEST_IPV4_GATEWAY = parseNumericAddress("192.168.222.3") as Inet4Address
67 private val TEST_IPV6_GATEWAY = parseNumericAddress("2001:db8::1") as Inet6Address
68 
69 private val TEST_IPV4_LINKADDR = LinkAddress("192.168.222.123/24")
70 private val TEST_IPV6_LINKADDR = LinkAddress("2001:db8::123/64")
71 
72 // DNSes inside IP prefix
73 private val TEST_IPV4_DNS = parseNumericAddress("192.168.222.1") as Inet4Address
74 private val TEST_IPV6_DNS = parseNumericAddress("2001:db8::321") as Inet6Address
75 
76 private val TEST_IFACE = InterfaceParams("fake0", 21, null)
77 private val TEST_LINK_PROPERTIES = LinkProperties().apply {
78     interfaceName = TEST_IFACE.name
79     addLinkAddress(TEST_IPV4_LINKADDR)
80     addLinkAddress(TEST_IPV6_LINKADDR)
81 
82     // Add on link routes
83     addRoute(RouteInfo(TEST_IPV4_LINKADDR, null /* gateway */, TEST_IFACE.name))
84     addRoute(RouteInfo(TEST_IPV6_LINKADDR, null /* gateway */, TEST_IFACE.name))
85 
86     // Add default routes
87     addRoute(RouteInfo(IpPrefix(parseNumericAddress("0.0.0.0"), 0), TEST_IPV4_GATEWAY))
88     addRoute(RouteInfo(IpPrefix(parseNumericAddress("::"), 0), TEST_IPV6_GATEWAY))
89 
90     addDnsServer(TEST_IPV4_DNS)
91     addDnsServer(TEST_IPV6_DNS)
92 }
93 
94 /**
95  * Tests for IpReachabilityMonitor.
96  */
97 @RunWith(AndroidJUnit4::class)
98 @SmallTest
99 class IpReachabilityMonitorTest {
100     private val callback = mock(IpReachabilityMonitor.Callback::class.java)
101     private val dependencies = mock(IpReachabilityMonitor.Dependencies::class.java)
102     private val log = mock(SharedLog::class.java)
103     private val context = mock(Context::class.java)
104     private val netd = mock(INetd::class.java)
105     private val fd = mock(FileDescriptor::class.java)
106     private val metricsLog = mock(IpConnectivityLog::class.java)
107 
108     private val handlerThread = HandlerThread(IpReachabilityMonitorTest::class.simpleName)
<lambda>null109     private val handler by lazy { Handler(handlerThread.looper) }
110 
111     private lateinit var reachabilityMonitor: IpReachabilityMonitor
112     private lateinit var neighborMonitor: TestIpNeighborMonitor
113 
114     /**
115      * A version of [IpNeighborMonitor] that overrides packet reading from a socket, and instead
116      * allows the test to enqueue test packets via [enqueuePacket].
117      */
118     private class TestIpNeighborMonitor(
119         handler: Handler,
120         log: SharedLog,
121         cb: NeighborEventConsumer,
122         private val fd: FileDescriptor
123     ) : IpNeighborMonitor(handler, log, cb) {
124 
125         private val pendingPackets = ConcurrentLinkedQueue<ByteArray>()
126         val msgQueue = mock(MessageQueue::class.java)
127 
128         private var eventListener: OnFileDescriptorEventListener? = null
129 
createFdnull130         override fun createFd() = fd
131         override fun getMessageQueue() = msgQueue
132 
133         fun enqueuePacket(packet: ByteArray) {
134             val listener = eventListener ?: fail("IpNeighborMonitor was not yet started")
135             pendingPackets.add(packet)
136             handler.post {
137                 listener.onFileDescriptorEvents(fd, OnFileDescriptorEventListener.EVENT_INPUT)
138             }
139         }
140 
readPacketnull141         override fun readPacket(fd: FileDescriptor, packetBuffer: ByteArray): Int {
142             val packet = pendingPackets.poll() ?: throw ErrnoException("No pending packet", EAGAIN)
143             if (packet.size > packetBuffer.size) {
144                 fail("Buffer (${packetBuffer.size}) is too small for packet (${packet.size})")
145             }
146             System.arraycopy(packet, 0, packetBuffer, 0, packet.size)
147             return packet.size
148         }
149 
onStartnull150         override fun onStart() {
151             super.onStart()
152 
153             // Find the file descriptor listener that was registered on the instrumented queue
154             val captor = ArgumentCaptor.forClass(OnFileDescriptorEventListener::class.java)
155             verify(msgQueue).addOnFileDescriptorEventListener(
156                     eq(fd), anyInt(), captor.capture())
157             eventListener = captor.value
158         }
159     }
160 
161     @Before
setUpnull162     fun setUp() {
163         doReturn(log).`when`(log).forSubComponent(anyString())
164         doReturn(true).`when`(fd).valid()
165         handlerThread.start()
166 
167         doAnswer { inv ->
168             val handler = inv.getArgument<Handler>(0)
169             val log = inv.getArgument<SharedLog>(1)
170             val cb = inv.getArgument<IpNeighborMonitor.NeighborEventConsumer>(2)
171             neighborMonitor = TestIpNeighborMonitor(handler, log, cb, fd)
172             neighborMonitor
173         }.`when`(dependencies).makeIpNeighborMonitor(any(), any(), any())
174 
175         val monitorFuture = CompletableFuture<IpReachabilityMonitor>()
176         // IpReachabilityMonitor needs to be started from the handler thread
177         handler.post {
178             monitorFuture.complete(IpReachabilityMonitor(
179                     context,
180                     TEST_IFACE,
181                     handler,
182                     log,
183                     callback,
184                     false /* useMultinetworkPolicyTracker */,
185                     dependencies,
186                     metricsLog,
187                     netd))
188         }
189         reachabilityMonitor = monitorFuture.get(TEST_TIMEOUT_MS, TimeUnit.MILLISECONDS)
190         assertTrue(::neighborMonitor.isInitialized,
191                 "IpReachabilityMonitor did not call makeIpNeighborMonitor")
192     }
193 
194     @After
tearDownnull195     fun tearDown() {
196         // Ensure the handler thread is not accessing the fd while changing its mock
197         handlerThread.waitForIdle(TEST_TIMEOUT_MS)
198         doReturn(false).`when`(fd).valid()
199         handlerThread.quitSafely()
200     }
201 
202     @Test
testLoseProvisioning_FirstProbeIsFailednull203     fun testLoseProvisioning_FirstProbeIsFailed() {
204         reachabilityMonitor.updateLinkProperties(TEST_LINK_PROPERTIES)
205 
206         neighborMonitor.enqueuePacket(makeNewNeighMessage(TEST_IPV4_DNS, NUD_FAILED))
207         verify(callback, timeout(TEST_TIMEOUT_MS)).notifyLost(eq(TEST_IPV4_DNS), anyString())
208     }
209 
runLoseProvisioningTestnull210     private fun runLoseProvisioningTest(lostNeighbor: InetAddress) {
211         reachabilityMonitor.updateLinkProperties(TEST_LINK_PROPERTIES)
212 
213         neighborMonitor.enqueuePacket(makeNewNeighMessage(TEST_IPV4_GATEWAY, NUD_STALE))
214         neighborMonitor.enqueuePacket(makeNewNeighMessage(TEST_IPV6_GATEWAY, NUD_STALE))
215         neighborMonitor.enqueuePacket(makeNewNeighMessage(TEST_IPV4_DNS, NUD_STALE))
216         neighborMonitor.enqueuePacket(makeNewNeighMessage(TEST_IPV6_DNS, NUD_STALE))
217 
218         neighborMonitor.enqueuePacket(makeNewNeighMessage(lostNeighbor, NUD_FAILED))
219         verify(callback, timeout(TEST_TIMEOUT_MS)).notifyLost(eq(lostNeighbor), anyString())
220     }
221 
222     @Test
testLoseProvisioning_Ipv4DnsLostnull223     fun testLoseProvisioning_Ipv4DnsLost() {
224         runLoseProvisioningTest(TEST_IPV4_DNS)
225     }
226 
227     @Test
testLoseProvisioning_Ipv6DnsLostnull228     fun testLoseProvisioning_Ipv6DnsLost() {
229         runLoseProvisioningTest(TEST_IPV6_DNS)
230     }
231 
232     @Test
testLoseProvisioning_Ipv4GatewayLostnull233     fun testLoseProvisioning_Ipv4GatewayLost() {
234         runLoseProvisioningTest(TEST_IPV4_GATEWAY)
235     }
236 
237     @Test
testLoseProvisioning_Ipv6GatewayLostnull238     fun testLoseProvisioning_Ipv6GatewayLost() {
239         runLoseProvisioningTest(TEST_IPV6_GATEWAY)
240     }
241 }