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