• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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