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.Os
29 import android.system.OsConstants.AF_INET
30 import android.system.OsConstants.AF_PACKET
31 import android.system.OsConstants.ARPHRD_ETHER
32 import android.system.OsConstants.ETH_P_IPV6
33 import android.system.OsConstants.IPPROTO_UDP
34 import android.system.OsConstants.SOCK_DGRAM
35 import android.system.OsConstants.SOCK_NONBLOCK
36 import androidx.test.platform.app.InstrumentationRegistry
37 import android.system.OsConstants.SOCK_RAW
38 import android.system.OsConstants.SOL_SOCKET
39 import android.system.OsConstants.SO_RCVTIMEO
40 import android.system.StructTimeval
41 import com.android.net.module.util.Ipv6Utils
42 import com.android.net.module.util.NetworkStackConstants.ETHER_ADDR_LEN
43 import com.android.net.module.util.NetworkStackConstants.IPV4_ADDR_ANY
44 import com.android.net.module.util.NetworkStackConstants.IPV6_ADDR_ALL_NODES_MULTICAST
45 import com.android.net.module.util.structs.PrefixInformationOption
46 import com.android.testutils.ArpRequestFilter
47 import com.android.testutils.ETHER_HEADER_LENGTH
48 import com.android.testutils.IPV4_HEADER_LENGTH
49 import com.android.testutils.IPv4UdpFilter
50 import com.android.testutils.TapPacketReader
51 import com.android.testutils.UDP_HEADER_LENGTH
52 import org.junit.After
53 import org.junit.Assert.assertArrayEquals
54 import org.junit.Before
55 import org.junit.Test
56 import java.io.FileDescriptor
57 import java.net.Inet4Address
58 import kotlin.reflect.KClass
59 import java.net.Inet6Address
60 import java.nio.ByteBuffer
61 import kotlin.test.assertEquals
62 import kotlin.test.assertTrue
63 import kotlin.test.fail
64
65 class NetworkStackUtilsIntegrationTest {
<lambda>null66 private val inst by lazy { InstrumentationRegistry.getInstrumentation() }
<lambda>null67 private val context by lazy { inst.context }
68
69 private val TEST_TIMEOUT_MS = 10_000L
70 private val TEST_MTU = 1500
71 private val TEST_TARGET_IPV4_ADDR = parseNumericAddress("192.0.2.42") as Inet4Address
72 private val TEST_SRC_MAC = MacAddress.fromString("BA:98:76:54:32:10")
73 private val TEST_TARGET_MAC = MacAddress.fromString("01:23:45:67:89:0A")
74 private val TEST_INET6ADDR_1 = parseNumericAddress("2001:db8::1") as Inet6Address
75 private val TEST_INET6ADDR_2 = parseNumericAddress("2001:db8::2") as Inet6Address
76
77 private val readerHandler = HandlerThread(
78 NetworkStackUtilsIntegrationTest::class.java.simpleName)
79 private lateinit var iface: TestNetworkInterface
80 private lateinit var reader: TapPacketReader
81
82 @Before
setUpnull83 fun setUp() {
84 inst.uiAutomation.adoptShellPermissionIdentity(MANAGE_TEST_NETWORKS)
85 try {
86 val tnm = context.assertHasService(TestNetworkManager::class)
87 iface = tnm.createTapInterface()
88 } finally {
89 inst.uiAutomation.dropShellPermissionIdentity()
90 }
91 readerHandler.start()
92 reader = TapPacketReader(readerHandler.threadHandler, iface.fileDescriptor.fileDescriptor,
93 1500 /* maxPacketSize */)
94 readerHandler.threadHandler.post { reader.start() }
95 }
96
97 @After
tearDownnull98 fun tearDown() {
99 readerHandler.quitSafely()
100 if (this::iface.isInitialized) iface.fileDescriptor.close()
101 }
102
103 @Test
testAddArpEntrynull104 fun testAddArpEntry() {
105 val socket = Os.socket(AF_INET, SOCK_DGRAM or SOCK_NONBLOCK, IPPROTO_UDP)
106 SocketUtils.bindSocketToInterface(socket, iface.interfaceName)
107
108 NetworkStackUtils.addArpEntry(TEST_TARGET_IPV4_ADDR, TEST_TARGET_MAC, iface.interfaceName,
109 socket)
110
111 // Fake DHCP packet: would not be usable as a DHCP offer (most IPv4 addresses are all-zero,
112 // no gateway or DNS servers, etc).
113 // Using a DHCP packet to replicate actual usage of the API: it is used in DhcpServer to
114 // send packets to clients before their IP address has been assigned.
115 val buffer = DhcpPacket.buildOfferPacket(DhcpPacket.ENCAP_BOOTP, 123 /* transactionId */,
116 false /* broadcast */, IPV4_ADDR_ANY /* serverIpAddr */,
117 IPV4_ADDR_ANY /* relayIp */, IPV4_ADDR_ANY /* yourIp */,
118 TEST_TARGET_MAC.toByteArray(), 3600 /* timeout */, IPV4_ADDR_ANY /* netMask */,
119 IPV4_ADDR_ANY /* bcAddr */, emptyList<Inet4Address>() /* gateways */,
120 emptyList<Inet4Address>() /* dnsServers */,
121 IPV4_ADDR_ANY /* dhcpServerIdentifier */, null /* domainName */,
122 null /* hostname */, false /* metered */, 1500 /* mtu */,
123 null /* captivePortalUrl */)
124 // Not using .array as per errorprone "ByteBufferBackingArray" recommendation
125 val originalPacket = buffer.readAsArray()
126
127 Os.sendto(socket, originalPacket, 0 /* bytesOffset */, originalPacket.size /* bytesCount */,
128 0 /* flags */, TEST_TARGET_IPV4_ADDR, DhcpPacket.DHCP_CLIENT.toInt() /* port */)
129
130 // Verify the packet was sent to the mac address specified in the ARP entry
131 // Also accept ARP requests, but expect that none is sent before the UDP packet
132 // IPv6 NS may be sent on the interface but will be filtered out
133 val sentPacket = reader.poll(TEST_TIMEOUT_MS, IPv4UdpFilter().or(ArpRequestFilter()))
134 ?: fail("Packet was not sent on the interface")
135
136 val sentTargetAddr = MacAddress.fromBytes(sentPacket.copyOfRange(0, ETHER_ADDR_LEN))
137 assertEquals(TEST_TARGET_MAC, sentTargetAddr, "Destination ethernet address does not match")
138
139 val sentDhcpPacket = sentPacket.copyOfRange(
140 ETHER_HEADER_LENGTH + IPV4_HEADER_LENGTH + UDP_HEADER_LENGTH, sentPacket.size)
141
142 assertArrayEquals("Sent packet != original packet", originalPacket, sentDhcpPacket)
143 }
144
145 @Test
testAttachRaFilternull146 fun testAttachRaFilter() {
147 val socket = Os.socket(AF_PACKET, SOCK_RAW, ETH_P_IPV6)
148 val ifParams = InterfaceParams.getByName(iface.interfaceName)
149 ?: fail("Could not obtain interface params for ${iface.interfaceName}")
150 val socketAddr = SocketUtils.makePacketSocketAddress(ETH_P_IPV6, ifParams.index)
151 Os.bind(socket, socketAddr)
152 Os.setsockoptTimeval(socket, SOL_SOCKET, SO_RCVTIMEO,
153 StructTimeval.fromMillis(TEST_TIMEOUT_MS))
154
155 // Verify that before setting any filter, the socket receives pings
156 val echo = Ipv6Utils.buildEchoRequestPacket(TEST_SRC_MAC, TEST_TARGET_MAC, TEST_INET6ADDR_1,
157 TEST_INET6ADDR_2)
158 reader.sendResponse(echo)
159 echo.rewind()
160 assertNextPacketEquals(socket, echo.readAsArray(), "ICMPv6 echo")
161
162 NetworkStackUtils.attachRaFilter(socket, ARPHRD_ETHER)
163 // Send another echo, then an RA. After setting the filter expect only the RA.
164 echo.rewind()
165 reader.sendResponse(echo)
166 val pio = PrefixInformationOption.build(IpPrefix("2001:db8:1::/64"),
167 0.toByte() /* flags */, 3600 /* validLifetime */, 1800 /* preferredLifetime */)
168 val ra = Ipv6Utils.buildRaPacket(TEST_SRC_MAC, TEST_TARGET_MAC,
169 TEST_INET6ADDR_1 /* routerAddr */, IPV6_ADDR_ALL_NODES_MULTICAST,
170 0.toByte() /* flags */, 1800 /* lifetime */, 0 /* reachableTime */,
171 0 /* retransTimer */, pio)
172 reader.sendResponse(ra)
173 ra.rewind()
174
175 assertNextPacketEquals(socket, ra.readAsArray(), "ICMPv6 RA")
176 }
177
assertNextPacketEqualsnull178 private fun assertNextPacketEquals(socket: FileDescriptor, expected: ByteArray, descr: String) {
179 val buffer = ByteArray(TEST_MTU)
180 val readPacket = Os.read(socket, buffer, 0 /* byteOffset */, buffer.size)
181 assertTrue(readPacket > 0, "$descr not received")
182 assertEquals(expected.size, readPacket, "Received packet size does not match for $descr")
183 assertArrayEquals("Received packet != expected $descr",
184 expected, buffer.copyOfRange(0, readPacket))
185 }
186 }
187
readAsArraynull188 private fun ByteBuffer.readAsArray(): ByteArray {
189 val out = ByteArray(remaining())
190 get(out)
191 return out
192 }
193
assertHasServicenull194 private fun <T : Any> Context.assertHasService(manager: KClass<T>) = getSystemService(manager.java)
195 ?: fail("Could not find service $manager")
196