Merge "Enable error-prone checking for CtsNetTestCasesDefaults" into main
diff --git a/thread/tests/integration/src/android/net/thread/InternetAccessTest.kt b/thread/tests/integration/src/android/net/thread/InternetAccessTest.kt
index 9b787f5..f3a09a0 100644
--- a/thread/tests/integration/src/android/net/thread/InternetAccessTest.kt
+++ b/thread/tests/integration/src/android/net/thread/InternetAccessTest.kt
@@ -32,6 +32,7 @@
import android.net.thread.utils.IntegrationTestUtils.waitFor
import android.net.thread.utils.OtDaemonController
import android.net.thread.utils.TestDnsServer
+import android.net.thread.utils.TestUdpEchoServer
import android.net.thread.utils.ThreadFeatureCheckerRule
import android.net.thread.utils.ThreadFeatureCheckerRule.RequiresSimulationThreadDevice
import android.net.thread.utils.ThreadFeatureCheckerRule.RequiresThreadFeature
@@ -48,6 +49,7 @@
import com.google.common.truth.Truth.assertThat
import java.net.Inet4Address
import java.net.InetAddress
+import java.net.InetSocketAddress
import java.time.Duration
import org.junit.After
import org.junit.Before
@@ -64,6 +66,7 @@
private val TAG = BorderRoutingTest::class.java.simpleName
private val NUM_FTD = 1
private val DNS_SERVER_ADDR = parseNumericAddress("8.8.8.8") as Inet4Address
+ private val UDP_ECHO_SERVER_ADDRESS = InetSocketAddress(parseNumericAddress("1.2.3.4"), 12345)
private val ANSWER_RECORDS =
listOf(
DnsPacket.DnsRecord.makeAOrAAAARecord(
@@ -94,6 +97,7 @@
private lateinit var infraNetworkReader: PollPacketReader
private lateinit var infraDevice: InfraNetworkDevice
private lateinit var dnsServer: TestDnsServer
+ private lateinit var udpEchoServer: TestUdpEchoServer
@Before
@Throws(Exception::class)
@@ -111,13 +115,16 @@
controller.setEnabledAndWait(true)
controller.joinAndWait(DEFAULT_DATASET)
- // Creates a infra network device.
+ // Create an infra network device.
infraNetworkReader = newPacketReader(infraNetworkTracker.testIface, handler)
infraDevice = startInfraDeviceAndWaitForOnLinkAddr(infraNetworkReader)
// Create a DNS server
dnsServer = TestDnsServer(infraNetworkReader, DNS_SERVER_ADDR, ANSWER_RECORDS)
+ // Create a UDP echo server
+ udpEchoServer = TestUdpEchoServer(infraNetworkReader, UDP_ECHO_SERVER_ADDRESS)
+
// Create Ftds
for (i in 0 until NUM_FTD) {
ftds.add(FullThreadDevice(15 + i /* node ID */))
@@ -132,6 +139,7 @@
tearDownInfraNetwork(infraNetworkTracker)
dnsServer.stop()
+ udpEchoServer.stop()
handlerThread.quitSafely()
handlerThread.join()
@@ -166,6 +174,20 @@
assertThat(ftd.resolveHost("google.com", TYPE_AAAA)).isEmpty()
}
+ @Test
+ fun nat64Enabled_threadDeviceSendsUdpToEchoServer_replyIsReceived() {
+ controller.setNat64EnabledAndWait(true)
+ waitFor({ otCtl.hasNat64PrefixInNetdata() }, Duration.ofSeconds(10))
+ val ftd = ftds[0]
+ joinNetworkAndWaitForOmr(ftd, DEFAULT_DATASET)
+ udpEchoServer.start()
+
+ ftd.udpOpen()
+ ftd.udpSend("Hello,Thread", UDP_ECHO_SERVER_ADDRESS.address, UDP_ECHO_SERVER_ADDRESS.port)
+ val reply = ftd.udpReceive()
+ assertThat(reply).isEqualTo("Hello,Thread")
+ }
+
private fun extractIpv4AddressFromMappedAddress(address: InetAddress): Inet4Address {
return InetAddress.getByAddress(address.address.slice(12 until 16).toByteArray())
as Inet4Address
diff --git a/thread/tests/integration/src/android/net/thread/utils/FullThreadDevice.java b/thread/tests/integration/src/android/net/thread/utils/FullThreadDevice.java
index 5b532c7..209eed6 100644
--- a/thread/tests/integration/src/android/net/thread/utils/FullThreadDevice.java
+++ b/thread/tests/integration/src/android/net/thread/utils/FullThreadDevice.java
@@ -234,8 +234,8 @@
return matcher.group(4);
}
- /** Sends a UDP message to given IPv6 address and port. */
- public void udpSend(String message, Inet6Address serverAddr, int serverPort) {
+ /** Sends a UDP message to given IP address and port. */
+ public void udpSend(String message, InetAddress serverAddr, int serverPort) {
executeCommand("udp send %s %d %s", serverAddr.getHostAddress(), serverPort, message);
}
diff --git a/thread/tests/integration/src/android/net/thread/utils/TestDnsServer.kt b/thread/tests/integration/src/android/net/thread/utils/TestDnsServer.kt
index c52fc49..f97c0f2 100644
--- a/thread/tests/integration/src/android/net/thread/utils/TestDnsServer.kt
+++ b/thread/tests/integration/src/android/net/thread/utils/TestDnsServer.kt
@@ -16,18 +16,16 @@
package android.net.thread.utils
-import android.net.thread.utils.IntegrationTestUtils.pollForPacket
import android.system.OsConstants.IPPROTO_IP
import android.system.OsConstants.IPPROTO_UDP
import com.android.net.module.util.DnsPacket
import com.android.net.module.util.PacketBuilder
-import com.android.net.module.util.Struct
import com.android.net.module.util.structs.Ipv4Header
import com.android.net.module.util.structs.UdpHeader
import com.android.testutils.PollPacketReader
import java.net.InetAddress
+import java.net.InetSocketAddress
import java.nio.ByteBuffer
-import kotlin.concurrent.thread
/**
* A class that simulates a DNS server.
@@ -41,11 +39,12 @@
class TestDnsServer(
private val packetReader: PollPacketReader,
private val serverAddress: InetAddress,
- private val answerRecords: List<DnsPacket.DnsRecord>,
-) {
- private val TAG = TestDnsServer::class.java.simpleName
- private val DNS_UDP_PORT = 53
- private var workerThread: Thread? = null
+ private val serverAnswers: List<DnsPacket.DnsRecord>,
+) : TestUdpServer(packetReader, InetSocketAddress(serverAddress, DNS_UDP_PORT)) {
+ companion object {
+ private val TAG = TestDnsServer::class.java.simpleName
+ private const val DNS_UDP_PORT = 53
+ }
private class TestDnsPacket : DnsPacket {
@@ -61,49 +60,12 @@
val records = super.mRecords
}
- /**
- * Starts the DNS server to respond to DNS requests.
- *
- * <p> The server polls the DNS requests from the {@code packetReader} and responds with the
- * {@code answerRecords}. The server will automatically stop when it fails to poll a DNS request
- * within the timeout (3000 ms, as defined in IntegrationTestUtils).
- */
- fun start() {
- workerThread = thread {
- var requestPacket: ByteArray
- while (true) {
- requestPacket = pollForDnsPacket() ?: break
- val buf = ByteBuffer.wrap(requestPacket)
- packetReader.sendResponse(buildDnsResponse(buf, answerRecords))
- }
- }
- }
-
- /** Stops the DNS server. */
- fun stop() {
- workerThread?.join()
- }
-
- private fun pollForDnsPacket(): ByteArray? {
- val filter =
- fun(packet: ByteArray): Boolean {
- val buf = ByteBuffer.wrap(packet)
- val ipv4Header = Struct.parse(Ipv4Header::class.java, buf) ?: return false
- val udpHeader = Struct.parse(UdpHeader::class.java, buf) ?: return false
- return ipv4Header.dstIp == serverAddress && udpHeader.dstPort == DNS_UDP_PORT
- }
- return pollForPacket(packetReader, filter)
- }
-
- private fun buildDnsResponse(
- requestPacket: ByteBuffer,
- serverAnswers: List<DnsPacket.DnsRecord>,
+ override fun buildResponse(
+ requestIpv4Header: Ipv4Header,
+ requestUdpHeader: UdpHeader,
+ requestUdpPayload: ByteArray,
): ByteBuffer? {
- val requestIpv4Header = Struct.parse(Ipv4Header::class.java, requestPacket) ?: return null
- val requestUdpHeader = Struct.parse(UdpHeader::class.java, requestPacket) ?: return null
- val remainingRequestPacket = ByteArray(requestPacket.remaining())
- requestPacket.get(remainingRequestPacket)
- val requestDnsPacket = TestDnsPacket(remainingRequestPacket)
+ val requestDnsPacket = TestDnsPacket(requestUdpPayload)
val requestDnsHeader = requestDnsPacket.header
val answerRecords =
diff --git a/thread/tests/integration/src/android/net/thread/utils/TestUdpEchoServer.kt b/thread/tests/integration/src/android/net/thread/utils/TestUdpEchoServer.kt
new file mode 100644
index 0000000..9fcd6a4
--- /dev/null
+++ b/thread/tests/integration/src/android/net/thread/utils/TestUdpEchoServer.kt
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.thread.utils
+
+import android.system.OsConstants.IPPROTO_IP
+import android.system.OsConstants.IPPROTO_UDP
+import com.android.net.module.util.PacketBuilder
+import com.android.net.module.util.structs.Ipv4Header
+import com.android.net.module.util.structs.UdpHeader
+import com.android.testutils.PollPacketReader
+import java.net.InetSocketAddress
+import java.nio.ByteBuffer
+
+/**
+ * A class that simulates a UDP echo server that replies to incoming UDP message with the same
+ * payload.
+ *
+ * @param packetReader the packet reader to poll UDP requests from
+ * @param serverAddress the address and port of the UDP server
+ */
+class TestUdpEchoServer(
+ private val packetReader: PollPacketReader,
+ private val serverAddress: InetSocketAddress,
+) : TestUdpServer(packetReader, serverAddress) {
+ companion object {
+ private val TAG = TestUdpEchoServer::class.java.simpleName
+ }
+
+ override fun buildResponse(
+ requestIpv4Header: Ipv4Header,
+ requestUdpHeader: UdpHeader,
+ requestUdpPayload: ByteArray,
+ ): ByteBuffer? {
+ val buf =
+ PacketBuilder.allocate(
+ false /* hasEther */,
+ IPPROTO_IP,
+ IPPROTO_UDP,
+ requestUdpPayload.size,
+ )
+
+ val packetBuilder = PacketBuilder(buf)
+ packetBuilder.writeIpv4Header(
+ requestIpv4Header.tos,
+ requestIpv4Header.id,
+ requestIpv4Header.flagsAndFragmentOffset,
+ 0x40 /* ttl */,
+ IPPROTO_UDP.toByte(),
+ requestIpv4Header.dstIp, /* srcIp */
+ requestIpv4Header.srcIp, /* dstIp */
+ )
+ packetBuilder.writeUdpHeader(
+ requestUdpHeader.dstPort.toShort() /* srcPort */,
+ requestUdpHeader.srcPort.toShort(), /* dstPort */
+ )
+ buf.put(requestUdpPayload)
+
+ return packetBuilder.finalizePacket()
+ }
+}
diff --git a/thread/tests/integration/src/android/net/thread/utils/TestUdpServer.kt b/thread/tests/integration/src/android/net/thread/utils/TestUdpServer.kt
new file mode 100644
index 0000000..fb0942e
--- /dev/null
+++ b/thread/tests/integration/src/android/net/thread/utils/TestUdpServer.kt
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.thread.utils
+
+import android.net.thread.utils.IntegrationTestUtils.pollForPacket
+import com.android.net.module.util.Struct
+import com.android.net.module.util.structs.Ipv4Header
+import com.android.net.module.util.structs.UdpHeader
+import com.android.testutils.PollPacketReader
+import java.net.InetSocketAddress
+import java.nio.ByteBuffer
+import kotlin.concurrent.thread
+
+/**
+ * A class that simulates a UDP server that replies to incoming UDP messages.
+ *
+ * @param packetReader the packet reader to poll UDP requests from
+ * @param serverAddress the address and port of the UDP server
+ */
+abstract class TestUdpServer(
+ private val packetReader: PollPacketReader,
+ private val serverAddress: InetSocketAddress,
+) {
+ private val TAG = TestUdpServer::class.java.simpleName
+ private var workerThread: Thread? = null
+
+ /**
+ * Starts the UDP server to respond to UDP messages.
+ *
+ * <p> The server polls the UDP messages from the {@code packetReader} and responds with a
+ * message built by {@code buildResponse}. The server will automatically stop when it fails to
+ * poll a UDP request within the timeout (3000 ms, as defined in IntegrationTestUtils).
+ */
+ fun start() {
+ workerThread = thread {
+ var requestPacket: ByteArray
+ while (true) {
+ requestPacket = pollForUdpPacket() ?: break
+ val buf = ByteBuffer.wrap(requestPacket)
+ packetReader.sendResponse(buildResponse(buf) ?: break)
+ }
+ }
+ }
+
+ /** Stops the UDP server. */
+ fun stop() {
+ workerThread?.join()
+ }
+
+ /**
+ * Builds the UDP response for the given UDP request.
+ *
+ * @param ipv4Header the IPv4 header of the UDP request
+ * @param udpHeader the UDP header of the UDP request
+ * @param udpPayload the payload of the UDP request
+ * @return the UDP response
+ */
+ abstract fun buildResponse(
+ requestIpv4Header: Ipv4Header,
+ requestUdpHeader: UdpHeader,
+ requestUdpPayload: ByteArray,
+ ): ByteBuffer?
+
+ private fun pollForUdpPacket(): ByteArray? {
+ val filter =
+ fun(packet: ByteArray): Boolean {
+ val buf = ByteBuffer.wrap(packet)
+ val ipv4Header = Struct.parse(Ipv4Header::class.java, buf) ?: return false
+ val udpHeader = Struct.parse(UdpHeader::class.java, buf) ?: return false
+ return ipv4Header.dstIp == serverAddress.address &&
+ udpHeader.dstPort == serverAddress.port
+ }
+ return pollForPacket(packetReader, filter)
+ }
+
+ private fun buildResponse(requestPacket: ByteBuffer): ByteBuffer? {
+ val requestIpv4Header = Struct.parse(Ipv4Header::class.java, requestPacket) ?: return null
+ val requestUdpHeader = Struct.parse(UdpHeader::class.java, requestPacket) ?: return null
+ val remainingRequestPacket = ByteArray(requestPacket.remaining())
+ requestPacket.get(remainingRequestPacket)
+
+ return buildResponse(requestIpv4Header, requestUdpHeader, remainingRequestPacket)
+ }
+}