1 /*
2  * Copyright (C) 2020 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 
17 package android.net.util
18 
19 import android.Manifest.permission.MANAGE_TEST_NETWORKS
20 import android.content.Context
21 import android.net.InetAddresses.parseNumericAddress
22 import android.net.IpPrefix
23 import android.net.MacAddress
24 import android.net.TestNetworkInterface
25 import android.net.TestNetworkManager
26 import android.net.dhcp.DhcpPacket
27 import android.os.HandlerThread
28 import android.system.ErrnoException
29 import android.system.Os
30 import android.system.OsConstants
31 import android.system.OsConstants.AF_INET
32 import android.system.OsConstants.AF_PACKET
33 import android.system.OsConstants.ETH_P_IPV6
34 import android.system.OsConstants.IPPROTO_UDP
35 import android.system.OsConstants.SOCK_CLOEXEC
36 import android.system.OsConstants.SOCK_DGRAM
37 import android.system.OsConstants.SOCK_NONBLOCK
38 import android.system.OsConstants.SOCK_RAW
39 import android.system.OsConstants.SOL_SOCKET
40 import android.system.OsConstants.SO_RCVTIMEO
41 import android.system.StructTimeval
42 import androidx.test.platform.app.InstrumentationRegistry
43 import com.android.internal.util.HexDump
44 import com.android.net.module.util.InterfaceParams
45 import com.android.net.module.util.IpUtils
46 import com.android.net.module.util.Ipv6Utils
47 import com.android.net.module.util.NetworkStackConstants.ETHER_ADDR_LEN
48 import com.android.net.module.util.NetworkStackConstants.ETHER_HEADER_LEN
49 import com.android.net.module.util.NetworkStackConstants.IPV4_ADDR_ANY
50 import com.android.net.module.util.NetworkStackConstants.IPV4_CHECKSUM_OFFSET
51 import com.android.net.module.util.NetworkStackConstants.IPV4_FLAGS_OFFSET
52 import com.android.net.module.util.NetworkStackConstants.IPV4_FLAG_DF
53 import com.android.net.module.util.NetworkStackConstants.IPV4_FLAG_MF
54 import com.android.net.module.util.NetworkStackConstants.IPV4_HEADER_MIN_LEN
55 import com.android.net.module.util.NetworkStackConstants.IPV6_ADDR_ALL_NODES_MULTICAST
56 import com.android.net.module.util.NetworkStackConstants.UDP_HEADER_LEN
57 import com.android.net.module.util.structs.PrefixInformationOption
58 import com.android.networkstack.util.NetworkStackUtils
59 import com.android.testutils.ArpRequestFilter
60 import com.android.testutils.IPv4UdpFilter
61 import com.android.testutils.PollPacketReader
62 import java.io.FileDescriptor
63 import java.net.Inet4Address
64 import java.net.Inet6Address
65 import java.nio.ByteBuffer
66 import java.util.Arrays
67 import kotlin.reflect.KClass
68 import kotlin.test.assertEquals
69 import kotlin.test.assertNotNull
70 import kotlin.test.assertNull
71 import kotlin.test.assertTrue
72 import kotlin.test.fail
73 import org.junit.After
74 import org.junit.Assert.assertArrayEquals
75 import org.junit.Before
76 import org.junit.Test
77 
78 class NetworkStackUtilsIntegrationTest {
<lambda>null79     private val inst by lazy { InstrumentationRegistry.getInstrumentation() }
<lambda>null80     private val context by lazy { inst.context }
81 
82     private val TEST_TIMEOUT_MS = 10_000L
83     private val TEST_MTU = 1500
84     private val TEST_TARGET_IPV4_ADDR = parseNumericAddress("192.0.2.42") as Inet4Address
85     private val TEST_SRC_MAC = MacAddress.fromString("BA:98:76:54:32:10")
86     private val TEST_TARGET_MAC = MacAddress.fromString("01:23:45:67:89:0A")
87     private val TEST_INET6ADDR_1 = parseNumericAddress("2001:db8::1") as Inet6Address
88     private val TEST_INET6ADDR_2 = parseNumericAddress("2001:db8::2") as Inet6Address
89     private val TEST_INET6ADDR_3 = parseNumericAddress("fd01:db8::3") as Inet6Address
90 
91     // RFC4291 section 2.7.1
92     private val SOLICITED_NODE_MULTICAST_PREFIX = "FF02:0:0:0:0:1:FF00::/104"
93 
94     private val readerHandler = HandlerThread(
95             NetworkStackUtilsIntegrationTest::class.java.simpleName)
96     private lateinit var iface: TestNetworkInterface
97     private lateinit var reader: PollPacketReader
98 
99     @Before
setUpnull100     fun setUp() {
101         inst.uiAutomation.adoptShellPermissionIdentity(MANAGE_TEST_NETWORKS)
102         try {
103             val tnm = context.assertHasService(TestNetworkManager::class)
104             iface = tnm.createTapInterface()
105         } finally {
106             inst.uiAutomation.dropShellPermissionIdentity()
107         }
108         readerHandler.start()
109         reader = PollPacketReader(readerHandler.threadHandler, iface.fileDescriptor.fileDescriptor,
110                 1500 /* maxPacketSize */)
111         readerHandler.threadHandler.post { reader.start() }
112     }
113 
114     @After
tearDownnull115     fun tearDown() {
116         readerHandler.quitSafely()
117         if (this::iface.isInitialized) iface.fileDescriptor.close()
118     }
119 
120     @Test
testAddArpEntrynull121     fun testAddArpEntry() {
122         val socket = Os.socket(AF_INET, SOCK_DGRAM or SOCK_NONBLOCK, IPPROTO_UDP)
123         SocketUtils.bindSocketToInterface(socket, iface.interfaceName)
124 
125         NetworkStackUtils.addArpEntry(TEST_TARGET_IPV4_ADDR, TEST_TARGET_MAC, iface.interfaceName,
126                 socket)
127 
128         // Fake DHCP packet: would not be usable as a DHCP offer (most IPv4 addresses are all-zero,
129         // no gateway or DNS servers, etc).
130         // Using a DHCP packet to replicate actual usage of the API: it is used in DhcpServer to
131         // send packets to clients before their IP address has been assigned.
132         val buffer = DhcpPacket.buildOfferPacket(DhcpPacket.ENCAP_BOOTP, 123 /* transactionId */,
133                 false /* broadcast */, IPV4_ADDR_ANY /* serverIpAddr */,
134                 IPV4_ADDR_ANY /* relayIp */, IPV4_ADDR_ANY /* yourIp */,
135                 TEST_TARGET_MAC.toByteArray(), 3600 /* timeout */, IPV4_ADDR_ANY /* netMask */,
136                 IPV4_ADDR_ANY /* bcAddr */, emptyList<Inet4Address>() /* gateways */,
137                 emptyList<Inet4Address>() /* dnsServers */,
138                 IPV4_ADDR_ANY /* dhcpServerIdentifier */, null /* domainName */,
139                 null /* hostname */, false /* metered */, 1500 /* mtu */,
140                 null /* captivePortalUrl */)
141         // Not using .array as per errorprone "ByteBufferBackingArray" recommendation
142         val originalPacket = buffer.readAsArray()
143 
144         Os.sendto(socket, originalPacket, 0 /* bytesOffset */, originalPacket.size /* bytesCount */,
145                 0 /* flags */, TEST_TARGET_IPV4_ADDR, DhcpPacket.DHCP_CLIENT.toInt() /* port */)
146 
147         // Verify the packet was sent to the mac address specified in the ARP entry
148         // Also accept ARP requests, but expect that none is sent before the UDP packet
149         // IPv6 NS may be sent on the interface but will be filtered out
150         val sentPacket = reader.poll(TEST_TIMEOUT_MS, IPv4UdpFilter().or(ArpRequestFilter()))
151                 ?: fail("Packet was not sent on the interface")
152 
153         val sentTargetAddr = MacAddress.fromBytes(sentPacket.copyOfRange(0, ETHER_ADDR_LEN))
154         assertEquals(TEST_TARGET_MAC, sentTargetAddr, "Destination ethernet address does not match")
155 
156         val sentDhcpPacket = sentPacket.copyOfRange(
157                 ETHER_HEADER_LEN + IPV4_HEADER_MIN_LEN + UDP_HEADER_LEN, sentPacket.size)
158 
159         assertArrayEquals("Sent packet != original packet", originalPacket, sentDhcpPacket)
160     }
161 
doTestAttachRaFilternull162     private fun doTestAttachRaFilter(generic: Boolean) {
163         val socket = Os.socket(AF_PACKET, SOCK_RAW or SOCK_CLOEXEC, 0)
164         val ifParams = InterfaceParams.getByName(iface.interfaceName)
165                 ?: fail("Could not obtain interface params for ${iface.interfaceName}")
166         val socketAddr = SocketUtils.makePacketSocketAddress(ETH_P_IPV6, ifParams.index)
167         Os.bind(socket, socketAddr)
168         Os.setsockoptTimeval(socket, SOL_SOCKET, SO_RCVTIMEO,
169                 StructTimeval.fromMillis(TEST_TIMEOUT_MS))
170 
171         // Verify that before setting any filter, the socket receives pings
172         val echo = Ipv6Utils.buildEchoRequestPacket(TEST_SRC_MAC, TEST_TARGET_MAC, TEST_INET6ADDR_1,
173                 TEST_INET6ADDR_2)
174         reader.sendResponse(echo)
175         echo.rewind()
176         assertNextPacketEquals(socket, echo.readAsArray(), "ICMPv6 echo")
177 
178         if (generic) {
179             NetworkStackUtils.attachControlPacketFilter(socket)
180         } else {
181             NetworkStackUtils.attachRaFilter(socket)
182         }
183         // Send another echo, then an RA. After setting the filter expect only the RA.
184         echo.rewind()
185         reader.sendResponse(echo)
186         val pio = PrefixInformationOption.build(IpPrefix("2001:db8:1::/64"),
187                 0.toByte() /* flags */, 3600 /* validLifetime */, 1800 /* preferredLifetime */)
188         val ra = Ipv6Utils.buildRaPacket(TEST_SRC_MAC, TEST_TARGET_MAC,
189                 TEST_INET6ADDR_1 /* routerAddr */, IPV6_ADDR_ALL_NODES_MULTICAST,
190                 0.toByte() /* flags */, 1800 /* lifetime */, 0 /* reachableTime */,
191                 0 /* retransTimer */, pio)
192         reader.sendResponse(ra)
193         ra.rewind()
194 
195         assertNextPacketEquals(socket, ra.readAsArray(), "ICMPv6 RA")
196     }
197 
198     @Test
testAttachRaFilternull199     fun testAttachRaFilter() {
200         doTestAttachRaFilter(false)
201     }
202 
203     @Test
testRaViaAttachControlPacketFilternull204     fun testRaViaAttachControlPacketFilter() {
205         doTestAttachRaFilter(true)
206     }
207 
assertNextPacketEqualsnull208     private fun assertNextPacketEquals(socket: FileDescriptor, expected: ByteArray, descr: String) {
209         val buffer = ByteArray(TEST_MTU)
210         val readPacket = Os.read(socket, buffer, 0 /* byteOffset */, buffer.size)
211         assertTrue(readPacket > 0, "$descr not received")
212         assertEquals(expected.size, readPacket, "Received packet size does not match for $descr")
213         assertArrayEquals("Received packet != expected $descr",
214                 expected, buffer.copyOfRange(0, readPacket))
215     }
216 
assertSolicitedNodeMulticastAddressnull217     private fun assertSolicitedNodeMulticastAddress(
218         expected: Inet6Address?,
219         unicast: Inet6Address
220     ) {
221         assertNotNull(expected)
222         val prefix = IpPrefix(SOLICITED_NODE_MULTICAST_PREFIX)
223         assertTrue(prefix.contains(expected))
224         assertTrue(expected.isMulticastAddress())
225         // check the last 3 bytes of address
226         assertArrayEquals(Arrays.copyOfRange(expected.getAddress(), 13, 15),
227                 Arrays.copyOfRange(unicast.getAddress(), 13, 15))
228     }
229 
230     @Test
testConvertIpv6AddressToSolicitedNodeMulticastnull231     fun testConvertIpv6AddressToSolicitedNodeMulticast() {
232         val addr1 = NetworkStackUtils.ipv6AddressToSolicitedNodeMulticast(TEST_INET6ADDR_1)
233         assertSolicitedNodeMulticastAddress(addr1, TEST_INET6ADDR_1)
234 
235         val addr2 = NetworkStackUtils.ipv6AddressToSolicitedNodeMulticast(TEST_INET6ADDR_2)
236         assertSolicitedNodeMulticastAddress(addr2, TEST_INET6ADDR_2)
237 
238         val addr3 = NetworkStackUtils.ipv6AddressToSolicitedNodeMulticast(TEST_INET6ADDR_3)
239         assertSolicitedNodeMulticastAddress(addr3, TEST_INET6ADDR_3)
240     }
241 
242     @Test
testConvertMacAddressToEui64null243     fun testConvertMacAddressToEui64() {
244         // MAC address with universal/local bit set (the first byte: 0xBA)
245         var expected = byteArrayOf(
246                 0xB8.toByte(), 0x98.toByte(), 0x76.toByte(), 0xFF.toByte(),
247                 0xFE.toByte(), 0x54.toByte(), 0x32.toByte(), 0x10.toByte())
248         val srcEui64 = NetworkStackUtils.macAddressToEui64(TEST_SRC_MAC)
249         assertArrayEquals(expected, srcEui64)
250 
251         // MAC address with universal/local bit unset (the first byte: 0x01).
252         expected = byteArrayOf(
253                 0x03.toByte(), 0x23.toByte(), 0x45.toByte(), 0xFF.toByte(),
254                 0xFE.toByte(), 0x67.toByte(), 0x89.toByte(), 0x0A.toByte())
255         val targetEui64 = NetworkStackUtils.macAddressToEui64(TEST_TARGET_MAC)
256         assertArrayEquals(expected, targetEui64)
257     }
258 
259     @Test
testGenerateIpv6AddressFromEui64null260     fun testGenerateIpv6AddressFromEui64() {
261         val eui64 = NetworkStackUtils.macAddressToEui64(TEST_SRC_MAC)
262         var prefix = IpPrefix("2001:db8:1::/80")
263         // Don't accept the prefix length larger than 64.
264         assertNull(NetworkStackUtils.createInet6AddressFromEui64(prefix, eui64))
265 
266         // prefix length equals to or less than 64 is acceptable.
267         prefix = IpPrefix("2001:db8:1::/48")
268         // IPv6 address string is formed by combining the IPv6 prefix("2001:db8:1::") and
269         // EUI64 converted from TEST_SRC_MAC, see above test for the output EUI64 example.
270         var expected = parseNumericAddress("2001:db8:1::b898:76ff:fe54:3210") as Inet6Address
271         assertEquals(expected, NetworkStackUtils.createInet6AddressFromEui64(prefix, eui64))
272 
273         prefix = IpPrefix("2001:db8:1:2::/64")
274         expected = parseNumericAddress("2001:db8:1:2:b898:76ff:fe54:3210") as Inet6Address
275         assertEquals(expected, NetworkStackUtils.createInet6AddressFromEui64(prefix, eui64))
276     }
277 
assertSocketReadErrnonull278     private fun assertSocketReadErrno(msg: String, fd: FileDescriptor, errno: Int) {
279         val received = ByteBuffer.allocate(TEST_MTU)
280         try {
281             val len = Os.read(fd, received)
282             fail(msg + ": " + toHexString(received, len))
283         } catch (expected: ErrnoException) {
284             assertEquals(errno.toLong(), expected.errno.toLong())
285         }
286     }
287 
assertNextPacketOnSocketnull288     private fun assertNextPacketOnSocket(fd: FileDescriptor, expectedPacket: ByteBuffer) {
289         val received = ByteBuffer.allocate(TEST_MTU)
290         val len = Os.read(fd, received)
291         assertEquals(toHexString(expectedPacket, expectedPacket.limit()),
292             toHexString(received, len))
293     }
294 
setMfBitnull295     private fun setMfBit(packet: ByteBuffer, set: Boolean) {
296         val offset = ETHER_HEADER_LEN + IPV4_FLAGS_OFFSET
297         var flagOff: Int = packet.getShort(offset).toInt()
298         if (set) {
299             flagOff = (flagOff or IPV4_FLAG_MF) and IPV4_FLAG_DF.inv()
300         } else {
301             flagOff = (flagOff or IPV4_FLAG_DF) and IPV4_FLAG_MF.inv()
302         }
303         packet.putShort(offset, flagOff.toShort())
304         // Recalculate the checksum, which requires first clearing the checksum field.
305         val checksumOffset = ETHER_HEADER_LEN + IPV4_CHECKSUM_OFFSET
306         packet.putShort(checksumOffset, 0)
307         packet.putShort(checksumOffset, IpUtils.ipChecksum(packet, ETHER_HEADER_LEN))
308     }
309 
doTestDhcpResponseWithMfBitDroppednull310     private fun doTestDhcpResponseWithMfBitDropped(generic: Boolean) {
311         val ifindex = InterfaceParams.getByName(iface.interfaceName).index
312         val packetSock = Os.socket(AF_PACKET, SOCK_RAW or SOCK_NONBLOCK, /*protocol=*/0)
313         try {
314             if (generic) {
315                 NetworkStackUtils.attachControlPacketFilter(packetSock)
316             } else {
317                 NetworkStackUtils.attachDhcpFilter(packetSock)
318             }
319             val addr = SocketUtils.makePacketSocketAddress(OsConstants.ETH_P_IP, ifindex)
320             Os.bind(packetSock, addr)
321             val packet = DhcpPacket.buildNakPacket(DhcpPacket.ENCAP_L2, 42,
322                 TEST_TARGET_IPV4_ADDR, /*relayIp=*/ IPV4_ADDR_ANY, TEST_TARGET_MAC.toByteArray(),
323                 /*broadcast=*/ false, "NAK")
324             setMfBit(packet, true)
325             reader.sendResponse(packet)
326 
327             // Packet with MF bit set is not received.
328             assertSocketReadErrno("Packet with MF bit should have been dropped",
329                 packetSock, OsConstants.EAGAIN)
330 
331             // Identical packet, except with MF bit cleared, should be received.
332             setMfBit(packet, false)
333             reader.sendResponse(packet)
334             assertNextPacketOnSocket(packetSock, packet)
335         } finally {
336             Os.close(packetSock)
337         }
338     }
339 
340     @Test
testDhcpResponseWithMfBitDroppednull341     fun testDhcpResponseWithMfBitDropped() {
342         doTestDhcpResponseWithMfBitDropped(false)
343     }
344 
345     @Test
testGenericDhcpResponseWithMfBitDroppednull346     fun testGenericDhcpResponseWithMfBitDropped() {
347         doTestDhcpResponseWithMfBitDropped(true)
348     }
349 }
350 
readAsArraynull351 private fun ByteBuffer.readAsArray(): ByteArray {
352     val out = ByteArray(remaining())
353     get(out)
354     return out
355 }
356 
toHexStringnull357 private fun toHexString(b: ByteBuffer, len: Int): String {
358     return HexDump.toHexString(Arrays.copyOf(b.array(), len))
359 }
360 
assertHasServicenull361 private fun <T : Any> Context.assertHasService(manager: KClass<T>) = getSystemService(manager.java)
362         ?: fail("Could not find service $manager")
363