Skip to content
Snippets Groups Projects
Commit 45ac0444 authored by Jimi Chen's avatar Jimi Chen
Browse files

Use custom defined DNS/HTTP port

Refactor the test case to support listen custom DNS/HTTP port.
Avoid binding system restricted port (<1024).

Bug: 322113686
Test: atest NetworkStackIntegrationTests:android.net.NetworkStatsIntegrationTest
Change-Id: I5d3b966cdadc9321ca2ee0d04a5b8f71ee79bd8a
parent 2fe1db6e
No related branches found
No related tags found
No related merge requests found
/*
* 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 com.android.testutils
import java.io.FileDescriptor
class ExternalPacketForwarder(
srcFd: FileDescriptor,
mtu: Int,
dstFd: FileDescriptor,
forwardMap: Map<Int, Int>
) : PacketForwarderBase(srcFd, mtu, dstFd, forwardMap) {
/**
* Prepares a packet for forwarding by potentially updating the
* source port based on the specified port remapping rules.
*
* @param buf The packet data as a byte array.
* @param version The IP version of the packet (e.g., 4 for IPv4).
*/
override fun remapPort(buf: ByteArray, version: Int) {
val transportOffset = getTransportOffset(version)
val intPort = getRemappedPort(buf, transportOffset)
// Copy remapped source port.
if (intPort != 0) {
setPortAt(intPort, buf, transportOffset)
}
}
}
/*
* 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 com.android.testutils
import java.io.FileDescriptor
class InternalPacketForwarder(
srcFd: FileDescriptor,
mtu: Int,
dstFd: FileDescriptor,
forwardMap: Map<Int, Int>
) : PacketForwarderBase(srcFd, mtu, dstFd, forwardMap) {
/**
* Prepares a packet for forwarding by potentially updating the
* destination port based on the specified port remapping rules.
*
* @param buf The packet data as a byte array.
* @param version The IP version of the packet (e.g., 4 for IPv4).
*/
override fun remapPort(buf: ByteArray, version: Int) {
val transportOffset = getTransportOffset(version) + DESTINATION_PORT_OFFSET
val extPort = getRemappedPort(buf, transportOffset)
// Copy remapped destination port.
if (extPort != 0) {
setPortAt(extPort, buf, transportOffset)
}
}
}
...@@ -40,7 +40,8 @@ import libcore.io.IoUtils ...@@ -40,7 +40,8 @@ import libcore.io.IoUtils
class PacketBridge( class PacketBridge(
context: Context, context: Context,
addresses: List<LinkAddress>, addresses: List<LinkAddress>,
dnsAddr: InetAddress dnsAddr: InetAddress,
portMapping: List<Pair<Int, Int>>
) { ) {
private val binder = Binder() private val binder = Binder()
...@@ -56,6 +57,10 @@ class PacketBridge( ...@@ -56,6 +57,10 @@ class PacketBridge(
// Register test networks to ConnectivityService. // Register test networks to ConnectivityService.
private val internalNetworkCallback: TestableNetworkCallback private val internalNetworkCallback: TestableNetworkCallback
private val externalNetworkCallback: TestableNetworkCallback private val externalNetworkCallback: TestableNetworkCallback
private val internalForwardMap = HashMap<Int, Int>()
private val externalForwardMap = HashMap<Int, Int>()
val internalNetwork: Network val internalNetwork: Network
val externalNetwork: Network val externalNetwork: Network
init { init {
...@@ -65,14 +70,28 @@ class PacketBridge( ...@@ -65,14 +70,28 @@ class PacketBridge(
externalNetworkCallback = exCb externalNetworkCallback = exCb
internalNetwork = inNet internalNetwork = inNet
externalNetwork = exNet externalNetwork = exNet
for (mapping in portMapping) {
internalForwardMap[mapping.first] = mapping.second
externalForwardMap[mapping.second] = mapping.first
}
} }
// Set up the packet bridge. // Set up the packet bridge.
private val internalFd = internalIface.fileDescriptor.fileDescriptor private val internalFd = internalIface.fileDescriptor.fileDescriptor
private val externalFd = externalIface.fileDescriptor.fileDescriptor private val externalFd = externalIface.fileDescriptor.fileDescriptor
private val pr1 = PacketForwarder(internalFd, 1500, externalFd) private val pr1 = InternalPacketForwarder(
private val pr2 = PacketForwarder(externalFd, 1500, internalFd) internalFd,
1500,
externalFd,
internalForwardMap
)
private val pr2 = ExternalPacketForwarder(
externalFd,
1500,
internalFd,
externalForwardMap
)
fun start() { fun start() {
IoUtils.setBlocking(internalFd, true /* blocking */) IoUtils.setBlocking(internalFd, true /* blocking */)
......
...@@ -32,6 +32,7 @@ import android.util.Log; ...@@ -32,6 +32,7 @@ import android.util.Log;
import java.io.FileDescriptor; import java.io.FileDescriptor;
import java.io.IOException; import java.io.IOException;
import java.util.Map;
import java.util.Objects; import java.util.Objects;
/** /**
...@@ -57,8 +58,9 @@ import java.util.Objects; ...@@ -57,8 +58,9 @@ import java.util.Objects;
* from the http server, the same mechanism is applied but in a different direction, * from the http server, the same mechanism is applied but in a different direction,
* where the source and destination will be swapped. * where the source and destination will be swapped.
*/ */
public class PacketForwarder extends Thread { public abstract class PacketForwarderBase extends Thread {
private static final String TAG = "PacketForwarder"; private static final String TAG = "PacketForwarder";
static final int DESTINATION_PORT_OFFSET = 2;
// The source fd to read packets from. // The source fd to read packets from.
@NonNull @NonNull
...@@ -70,8 +72,10 @@ public class PacketForwarder extends Thread { ...@@ -70,8 +72,10 @@ public class PacketForwarder extends Thread {
@NonNull @NonNull
final FileDescriptor mDstFd; final FileDescriptor mDstFd;
@NonNull
final Map<Integer, Integer> mPortRemapRules;
/** /**
* Construct a {@link PacketForwarder}. * Construct a {@link PacketForwarderBase}.
* *
* This class reads packets from {@code srcFd} of a {@link TestNetworkInterface}, and * This class reads packets from {@code srcFd} of a {@link TestNetworkInterface}, and
* forwards them to the {@code dstFd} of another {@link TestNetworkInterface}. * forwards them to the {@code dstFd} of another {@link TestNetworkInterface}.
...@@ -82,13 +86,49 @@ public class PacketForwarder extends Thread { ...@@ -82,13 +86,49 @@ public class PacketForwarder extends Thread {
* @param srcFd {@link FileDescriptor} to read packets from. * @param srcFd {@link FileDescriptor} to read packets from.
* @param mtu MTU of the test network. * @param mtu MTU of the test network.
* @param dstFd {@link FileDescriptor} to write packets to. * @param dstFd {@link FileDescriptor} to write packets to.
* @param portRemapRules port remap rules
*/ */
public PacketForwarder(@NonNull FileDescriptor srcFd, int mtu, public PacketForwarderBase(@NonNull FileDescriptor srcFd, int mtu,
@NonNull FileDescriptor dstFd) { @NonNull FileDescriptor dstFd,
@NonNull Map<Integer, Integer> portRemapRules) {
super(TAG); super(TAG);
mSrcFd = Objects.requireNonNull(srcFd); mSrcFd = Objects.requireNonNull(srcFd);
mBuf = new byte[mtu]; mBuf = new byte[mtu];
mDstFd = Objects.requireNonNull(dstFd); mDstFd = Objects.requireNonNull(dstFd);
mPortRemapRules = Objects.requireNonNull(portRemapRules);
}
/**
* A method to prepare forwarding packets between two instances of {@link TestNetworkInterface},
* which includes ports mapping.
* Subclasses should override this method to implement the needed port remapping.
* For internal forwarder will remapped destination port,
* external forwarder will remapped source port.
* Example:
* An outgoing packet from the internal interface with
* source 1.2.3.4:1234 and destination 8.8.8.8:80
* might be translated to 8.8.8.8:1234 -> 1.2.3.4:8080 before forwarding.
* An outgoing packet from the external interface with
* source 1.2.3.4:8080 and destination 8.8.8.8:1234
* might be translated to 8.8.8.8:80 -> 1.2.3.4:1234 before forwarding.
*/
abstract void remapPort(@NonNull byte[] buf, int version);
/**
* Retrieves a potentially remapped port number from a packet.
*
* @param buf The packet data as a byte array.
* @param transportOffset The offset within the packet where the transport layer port begins.
* @return The remapped port if a mapping exists in the internal forwarding map,
* otherwise returns 0 (indicating no remapping).
*/
int getRemappedPort(@NonNull byte[] buf, int transportOffset) {
int port = PacketReflectorUtil.getPortAt(buf, transportOffset);
return mPortRemapRules.getOrDefault(port, 0);
}
int getTransportOffset(int version) {
return version == 4 ? IPV4_HEADER_LENGTH : IPV6_HEADER_LENGTH;
} }
private void forwardPacket(@NonNull byte[] buf, int len) { private void forwardPacket(@NonNull byte[] buf, int len) {
...@@ -99,7 +139,13 @@ public class PacketForwarder extends Thread { ...@@ -99,7 +139,13 @@ public class PacketForwarder extends Thread {
} }
} }
// Reads one packet from mSrcFd, and writes the packet to the mDstFd for supported protocols. /**
* Reads one packet from mSrcFd, and writes the packet to the mDestFd for supported protocols.
* This includes:
* 1.Address Swapping: Swaps source and destination IP addresses.
* 2.Port Remapping: Remap port if necessary.
* 3.Checksum Recalculation: Updates IP and transport layer checksums to reflect changes.
*/
private void processPacket() { private void processPacket() {
final int len = PacketReflectorUtil.readPacket(mSrcFd, mBuf); final int len = PacketReflectorUtil.readPacket(mSrcFd, mBuf);
if (len < 1) { if (len < 1) {
...@@ -142,13 +188,19 @@ public class PacketForwarder extends Thread { ...@@ -142,13 +188,19 @@ public class PacketForwarder extends Thread {
if (len < ipHdrLen + transportHdrLen) { if (len < ipHdrLen + transportHdrLen) {
throw new IllegalStateException("Unexpected buffer length: " + len); throw new IllegalStateException("Unexpected buffer length: " + len);
} }
// Swap addresses.
// Swap source and destination address.
PacketReflectorUtil.swapAddresses(mBuf, version); PacketReflectorUtil.swapAddresses(mBuf, version);
// Remapping the port.
remapPort(mBuf, version);
// Fix IP and Transport layer checksum.
PacketReflectorUtil.fixPacketChecksum(mBuf, len, version, proto);
// Send the packet to the destination fd. // Send the packet to the destination fd.
forwardPacket(mBuf, len); forwardPacket(mBuf, len);
} }
@Override @Override
public void run() { public void run() {
Log.i(TAG, "starting fd=" + mSrcFd + " valid=" + mSrcFd.valid()); Log.i(TAG, "starting fd=" + mSrcFd + " valid=" + mSrcFd.valid());
......
...@@ -25,8 +25,10 @@ import java.io.IOException ...@@ -25,8 +25,10 @@ import java.io.IOException
* A minimal HTTP server running on a random available port. * A minimal HTTP server running on a random available port.
* *
* @param host The host to listen to, or null to listen on all hosts * @param host The host to listen to, or null to listen on all hosts
* @param port The port to listen to, or 0 to auto select
*/ */
class TestHttpServer(host: String? = null) : NanoHTTPD(host, 0 /* auto-select the port */) { class TestHttpServer
@JvmOverloads constructor(host: String? = null, port: Int = 0) : NanoHTTPD(host, port) {
// Map of URL path -> HTTP response code // Map of URL path -> HTTP response code
private val responses = HashMap<Request, Response>() private val responses = HashMap<Request, Response>()
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment