[Thread] add a NAT64 test case for UDP echo server
This CL adds a test case for UDP traffic in InternetAccessTest. The test case verifies that a Thread device can send a UDP message to a UDP echo server and receive a reply.
Bug: 374232259
Change-Id: Id138248f1ed26c1cc5728cc06b42e3223c449125
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/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()
+ }
+}