Remove NAPT from PacketForwarder
Consider below scenario: Internal address: 1.2.3.4 External address: 5.6.7.8 Remote address: 8.8.8.8 When a packet was sent from 1.2.3.4 to 8.8.8.8, the current design translate it to a packet from 8.8.8.8 to 5.6.7.8 and foward it to the external interface. However, when the response is received from the external interface, there is a need to remember what is the original address to forward to. Thus, an address translation mechanism was introduced in current design. This change simplify PacketForwarder by removing the address translation. Consider, if the 2 interfaces are using the same address. Internal address: 1.2.3.4 External address: 1.2.3.4 Remote address: 8.8.8.8 When a packet was sent from 1.2.3.4 to 8.8.8.8, simply swap source and destination and and foward it to the external interface. When there is a response from the external interface, the same process can be done, this removes the need of address translation. Test: atest NetworkStackIntegrationTests:android.net.NetworkStatsIntegrationTest Test: atest CtsHostsideNetworkTests:com.android.cts.net.HostsideVpnTests Bug: N/A Change-Id: I86f2e774e9112905e5255b5a28c927562a42ab84
This commit is contained in:
parent
c45efc312e
commit
74acb88d03
6 changed files with 57 additions and 316 deletions
|
|
@ -1,81 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2023 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
|
||||
import java.net.InetAddress
|
||||
|
||||
/**
|
||||
* A class that forwards packets from the external {@link TestNetworkInterface} to the internal
|
||||
* {@link TestNetworkInterface} with NAT. See {@link NatPacketForwarderBase} for detail.
|
||||
*/
|
||||
class NatExternalPacketForwarder(
|
||||
srcFd: FileDescriptor,
|
||||
mtu: Int,
|
||||
dstFd: FileDescriptor,
|
||||
extAddr: InetAddress,
|
||||
natMap: PacketBridge.NatMap
|
||||
) : NatPacketForwarderBase(srcFd, mtu, dstFd, extAddr, natMap) {
|
||||
|
||||
/**
|
||||
* Rewrite addresses, ports and fix up checksums for packets received on the external
|
||||
* interface.
|
||||
*
|
||||
* Incoming response from external interface which is being forwarded to the internal
|
||||
* interface with translated address, e.g. 1.2.3.4:80 -> 8.8.8.8:1234
|
||||
* will be translated into 8.8.8.8:80 -> 192.168.1.1:5678.
|
||||
*
|
||||
* For packets that are not an incoming response, do not forward them to the
|
||||
* internal interface.
|
||||
*/
|
||||
override fun preparePacketForForwarding(buf: ByteArray, len: Int, version: Int, proto: Int) {
|
||||
val (addrPos, addrLen) = getAddressPositionAndLength(version)
|
||||
|
||||
// TODO: support one external address per ip version.
|
||||
val extAddrBuf = mExtAddr.address
|
||||
if (addrLen != extAddrBuf.size) throw IllegalStateException("Packet IP version mismatch")
|
||||
|
||||
// Get internal address by port.
|
||||
val transportOffset =
|
||||
if (version == 4) PacketReflector.IPV4_HEADER_LENGTH
|
||||
else PacketReflector.IPV6_HEADER_LENGTH
|
||||
val dstPort = getPortAt(buf, transportOffset + DESTINATION_PORT_OFFSET)
|
||||
val intAddrInfo = synchronized(mNatMap) { mNatMap.fromExternalPort(dstPort) }
|
||||
// No mapping, skip. This usually happens if the connection is initiated directly on
|
||||
// the external interface, e.g. DNS64 resolution, network validation, etc.
|
||||
if (intAddrInfo == null) return
|
||||
|
||||
val intAddrBuf = intAddrInfo.address.address
|
||||
val intPort = intAddrInfo.port
|
||||
|
||||
// Copy the original destination to into the source address.
|
||||
for (i in 0 until addrLen) {
|
||||
buf[addrPos + i] = buf[addrPos + addrLen + i]
|
||||
}
|
||||
|
||||
// Copy the internal address into the destination address.
|
||||
for (i in 0 until addrLen) {
|
||||
buf[addrPos + addrLen + i] = intAddrBuf[i]
|
||||
}
|
||||
|
||||
// Copy the internal port into the destination port.
|
||||
setPortAt(intPort, buf, transportOffset + DESTINATION_PORT_OFFSET)
|
||||
|
||||
// Fix IP and Transport layer checksum.
|
||||
fixPacketChecksum(buf, len, version, proto.toByte())
|
||||
}
|
||||
}
|
||||
|
|
@ -1,78 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2023 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
|
||||
import java.net.InetAddress
|
||||
|
||||
/**
|
||||
* A class that forwards packets from the internal {@link TestNetworkInterface} to the external
|
||||
* {@link TestNetworkInterface} with NAT. See {@link NatPacketForwarderBase} for detail.
|
||||
*/
|
||||
class NatInternalPacketForwarder(
|
||||
srcFd: FileDescriptor,
|
||||
mtu: Int,
|
||||
dstFd: FileDescriptor,
|
||||
extAddr: InetAddress,
|
||||
natMap: PacketBridge.NatMap
|
||||
) : NatPacketForwarderBase(srcFd, mtu, dstFd, extAddr, natMap) {
|
||||
|
||||
/**
|
||||
* Rewrite addresses, ports and fix up checksums for packets received on the internal
|
||||
* interface.
|
||||
*
|
||||
* Outgoing packet from the internal interface which is being forwarded to the
|
||||
* external interface with translated address, e.g. 192.168.1.1:5678 -> 8.8.8.8:80
|
||||
* will be translated into 8.8.8.8:1234 -> 1.2.3.4:80.
|
||||
*
|
||||
* The external port, e.g. 1234 in the above example, is the port number assigned by
|
||||
* the forwarder when creating the mapping to identify the source address and port when
|
||||
* the response is coming from the external interface. See {@link PacketBridge.NatMap}
|
||||
* for detail.
|
||||
*/
|
||||
override fun preparePacketForForwarding(buf: ByteArray, len: Int, version: Int, proto: Int) {
|
||||
val (addrPos, addrLen) = getAddressPositionAndLength(version)
|
||||
|
||||
// TODO: support one external address per ip version.
|
||||
val extAddrBuf = mExtAddr.address
|
||||
if (addrLen != extAddrBuf.size) throw IllegalStateException("Packet IP version mismatch")
|
||||
|
||||
val srcAddr = getInetAddressAt(buf, addrPos, addrLen)
|
||||
|
||||
// Copy the original destination to into the source address.
|
||||
for (i in 0 until addrLen) {
|
||||
buf[addrPos + i] = buf[addrPos + addrLen + i]
|
||||
}
|
||||
|
||||
// Copy the external address into the destination address.
|
||||
for (i in 0 until addrLen) {
|
||||
buf[addrPos + addrLen + i] = extAddrBuf[i]
|
||||
}
|
||||
|
||||
// Add an entry to NAT mapping table.
|
||||
val transportOffset =
|
||||
if (version == 4) PacketReflector.IPV4_HEADER_LENGTH
|
||||
else PacketReflector.IPV6_HEADER_LENGTH
|
||||
val srcPort = getPortAt(buf, transportOffset)
|
||||
val extPort = synchronized(mNatMap) { mNatMap.toExternalPort(srcAddr, srcPort, proto) }
|
||||
// Copy the external port to into the source port.
|
||||
setPortAt(extPort, buf, transportOffset)
|
||||
|
||||
// Fix IP and Transport layer checksum.
|
||||
fixPacketChecksum(buf, len, version, proto.toByte())
|
||||
}
|
||||
}
|
||||
|
|
@ -16,6 +16,7 @@
|
|||
|
||||
package com.android.testutils
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.Context
|
||||
import android.net.ConnectivityManager
|
||||
import android.net.LinkAddress
|
||||
|
|
@ -31,29 +32,27 @@ import com.android.testutils.RecorderCallback.CallbackEntry.Available
|
|||
import java.net.InetAddress
|
||||
import libcore.io.IoUtils
|
||||
|
||||
private const val MIN_PORT_NUMBER = 1025
|
||||
private const val MAX_PORT_NUMBER = 65535
|
||||
|
||||
/**
|
||||
* A class that set up two {@link TestNetworkInterface} with NAT, and forward packets between them.
|
||||
* A class that set up two {@link TestNetworkInterface}, and forward packets between them.
|
||||
*
|
||||
* See {@link NatPacketForwarder} for more detailed information.
|
||||
* See {@link PacketForwarder} for more detailed information.
|
||||
*/
|
||||
// TODO: Support multiple addresses.
|
||||
class PacketBridge(
|
||||
context: Context,
|
||||
internalAddr: LinkAddress,
|
||||
externalAddr: LinkAddress,
|
||||
address: LinkAddress,
|
||||
dnsAddr: InetAddress
|
||||
) {
|
||||
private val natMap = NatMap()
|
||||
private val binder = Binder()
|
||||
|
||||
private val cm = context.getSystemService(ConnectivityManager::class.java)!!
|
||||
private val tnm = context.getSystemService(TestNetworkManager::class.java)!!
|
||||
|
||||
// Create test networks.
|
||||
private val internalIface = tnm.createTunInterface(listOf(internalAddr))
|
||||
private val externalIface = tnm.createTunInterface(listOf(externalAddr))
|
||||
// Create test networks. The needed permissions should be supplied by the callers.
|
||||
@SuppressLint("MissingPermission")
|
||||
private val internalIface = tnm.createTunInterface(listOf(address))
|
||||
@SuppressLint("MissingPermission")
|
||||
private val externalIface = tnm.createTunInterface(listOf(address))
|
||||
|
||||
// Register test networks to ConnectivityService.
|
||||
private val internalNetworkCallback: TestableNetworkCallback
|
||||
|
|
@ -61,32 +60,20 @@ class PacketBridge(
|
|||
val internalNetwork: Network
|
||||
val externalNetwork: Network
|
||||
init {
|
||||
val (inCb, inNet) = createTestNetwork(internalIface, internalAddr, dnsAddr)
|
||||
val (exCb, exNet) = createTestNetwork(externalIface, externalAddr, dnsAddr)
|
||||
val (inCb, inNet) = createTestNetwork(internalIface, address, dnsAddr)
|
||||
val (exCb, exNet) = createTestNetwork(externalIface, address, dnsAddr)
|
||||
internalNetworkCallback = inCb
|
||||
externalNetworkCallback = exCb
|
||||
internalNetwork = inNet
|
||||
externalNetwork = exNet
|
||||
}
|
||||
|
||||
// Setup the packet bridge.
|
||||
// Set up the packet bridge.
|
||||
private val internalFd = internalIface.fileDescriptor.fileDescriptor
|
||||
private val externalFd = externalIface.fileDescriptor.fileDescriptor
|
||||
|
||||
private val pr1 = NatInternalPacketForwarder(
|
||||
internalFd,
|
||||
1500,
|
||||
externalFd,
|
||||
externalAddr.address,
|
||||
natMap
|
||||
)
|
||||
private val pr2 = NatExternalPacketForwarder(
|
||||
externalFd,
|
||||
1500,
|
||||
internalFd,
|
||||
externalAddr.address,
|
||||
natMap
|
||||
)
|
||||
private val pr1 = PacketForwarder(internalFd, 1500, externalFd)
|
||||
private val pr2 = PacketForwarder(externalFd, 1500, internalFd)
|
||||
|
||||
fun start() {
|
||||
IoUtils.setBlocking(internalFd, true /* blocking */)
|
||||
|
|
@ -130,44 +117,4 @@ class PacketBridge(
|
|||
val network = testCb.expect<Available>().network
|
||||
return testCb to network
|
||||
}
|
||||
|
||||
/**
|
||||
* A helper class to maintain the mappings between internal addresses/ports and external
|
||||
* ports.
|
||||
*
|
||||
* This class assigns an unused external port number if the mapping between
|
||||
* srcaddress:srcport:protocol and the external port does not exist yet.
|
||||
*
|
||||
* Note that this class is not thread-safe. The instance of the class needs to be
|
||||
* synchronized in the callers when being used in multiple threads.
|
||||
*/
|
||||
class NatMap {
|
||||
data class AddressInfo(val address: InetAddress, val port: Int, val protocol: Int)
|
||||
|
||||
private val mToExternalPort = HashMap<AddressInfo, Int>()
|
||||
private val mFromExternalPort = HashMap<Int, AddressInfo>()
|
||||
|
||||
// Skip well-known port 0~1024.
|
||||
private var nextExternalPort = MIN_PORT_NUMBER
|
||||
|
||||
fun toExternalPort(addr: InetAddress, port: Int, protocol: Int): Int {
|
||||
val info = AddressInfo(addr, port, protocol)
|
||||
val extPort: Int
|
||||
if (!mToExternalPort.containsKey(info)) {
|
||||
extPort = nextExternalPort++
|
||||
if (nextExternalPort > MAX_PORT_NUMBER) {
|
||||
throw IllegalStateException("Available ports are exhausted")
|
||||
}
|
||||
mToExternalPort[info] = extPort
|
||||
mFromExternalPort[extPort] = info
|
||||
} else {
|
||||
extPort = mToExternalPort[info]!!
|
||||
}
|
||||
return extPort
|
||||
}
|
||||
|
||||
fun fromExternalPort(port: Int): AddressInfo? {
|
||||
return mFromExternalPort[port]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,160 @@
|
|||
/*
|
||||
* Copyright (C) 2023 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 static com.android.testutils.PacketReflector.IPPROTO_TCP;
|
||||
import static com.android.testutils.PacketReflector.IPPROTO_UDP;
|
||||
import static com.android.testutils.PacketReflector.IPV4_HEADER_LENGTH;
|
||||
import static com.android.testutils.PacketReflector.IPV6_HEADER_LENGTH;
|
||||
import static com.android.testutils.PacketReflector.IPV6_PROTO_OFFSET;
|
||||
import static com.android.testutils.PacketReflector.TCP_HEADER_LENGTH;
|
||||
import static com.android.testutils.PacketReflector.UDP_HEADER_LENGTH;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.net.TestNetworkInterface;
|
||||
import android.system.ErrnoException;
|
||||
import android.system.Os;
|
||||
import android.util.Log;
|
||||
|
||||
import java.io.FileDescriptor;
|
||||
import java.io.IOException;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* A class that forwards packets from a {@link TestNetworkInterface} to another
|
||||
* {@link TestNetworkInterface}.
|
||||
*
|
||||
* For testing purposes, a {@link TestNetworkInterface} provides a {@link FileDescriptor}
|
||||
* which allows content injection on the test network. However, this could be hard to use
|
||||
* because the callers need to compose IP packets in order to inject content to the
|
||||
* test network.
|
||||
*
|
||||
* In order to remove the need of composing the IP packets, this class forwards IP packets to
|
||||
* the {@link FileDescriptor} of another {@link TestNetworkInterface} instance. Thus,
|
||||
* the TCP/IP headers could be parsed/composed automatically by the protocol stack of this
|
||||
* additional {@link TestNetworkInterface}, while the payload is supplied by the
|
||||
* servers run on the interface.
|
||||
*
|
||||
* To make it work, an internal interface and an external interface are defined, where
|
||||
* the client might send packets from the internal interface which are originated from
|
||||
* multiple addresses to a server that listens on the different port.
|
||||
*
|
||||
* For the incoming packet received from external interface, for example a http response sent
|
||||
* from the http server, the same mechanism is applied but in a different direction,
|
||||
* where the source and destination will be swapped.
|
||||
*/
|
||||
public class PacketForwarder extends Thread {
|
||||
private static final String TAG = "PacketForwarder";
|
||||
|
||||
// The source fd to read packets from.
|
||||
@NonNull
|
||||
final FileDescriptor mSrcFd;
|
||||
// The buffer to temporarily hold the entire packet after receiving.
|
||||
@NonNull
|
||||
final byte[] mBuf;
|
||||
// The destination fd to write packets to.
|
||||
@NonNull
|
||||
final FileDescriptor mDstFd;
|
||||
|
||||
/**
|
||||
* Construct a {@link PacketForwarder}.
|
||||
*
|
||||
* This class reads packets from {@code srcFd} of a {@link TestNetworkInterface}, and
|
||||
* forwards them to the {@code dstFd} of another {@link TestNetworkInterface}.
|
||||
*
|
||||
* Note that this class is not useful if the instance is not managed by a
|
||||
* {@link PacketBridge} to set up a two-way communication.
|
||||
*
|
||||
* @param srcFd {@link FileDescriptor} to read packets from.
|
||||
* @param mtu MTU of the test network.
|
||||
* @param dstFd {@link FileDescriptor} to write packets to.
|
||||
*/
|
||||
public PacketForwarder(@NonNull FileDescriptor srcFd, int mtu,
|
||||
@NonNull FileDescriptor dstFd) {
|
||||
super(TAG);
|
||||
mSrcFd = Objects.requireNonNull(srcFd);
|
||||
mBuf = new byte[mtu];
|
||||
mDstFd = Objects.requireNonNull(dstFd);
|
||||
}
|
||||
|
||||
private void forwardPacket(@NonNull byte[] buf, int len) {
|
||||
try {
|
||||
Os.write(mDstFd, buf, 0, len);
|
||||
} catch (ErrnoException | IOException e) {
|
||||
Log.e(TAG, "Error writing packet: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
// Reads one packet from mSrcFd, and writes the packet to the mDstFd for supported protocols.
|
||||
private void processPacket() {
|
||||
final int len = PacketReflectorUtil.readPacket(mSrcFd, mBuf);
|
||||
if (len < 1) {
|
||||
// Usually happens when socket read is being interrupted, e.g. stopping PacketForwarder.
|
||||
return;
|
||||
}
|
||||
|
||||
final int version = mBuf[0] >>> 4;
|
||||
final int protoPos, ipHdrLen;
|
||||
switch (version) {
|
||||
case 4:
|
||||
ipHdrLen = IPV4_HEADER_LENGTH;
|
||||
protoPos = PacketReflector.IPV4_PROTO_OFFSET;
|
||||
break;
|
||||
case 6:
|
||||
ipHdrLen = IPV6_HEADER_LENGTH;
|
||||
protoPos = IPV6_PROTO_OFFSET;
|
||||
break;
|
||||
default:
|
||||
throw new IllegalStateException("Unexpected version: " + version);
|
||||
}
|
||||
if (len < ipHdrLen) {
|
||||
throw new IllegalStateException("Unexpected buffer length: " + len);
|
||||
}
|
||||
|
||||
final byte proto = mBuf[protoPos];
|
||||
final int transportHdrLen;
|
||||
switch (proto) {
|
||||
case IPPROTO_TCP:
|
||||
transportHdrLen = TCP_HEADER_LENGTH;
|
||||
break;
|
||||
case IPPROTO_UDP:
|
||||
transportHdrLen = UDP_HEADER_LENGTH;
|
||||
break;
|
||||
// TODO: Support ICMP.
|
||||
default:
|
||||
return; // Unknown protocol, ignored.
|
||||
}
|
||||
|
||||
if (len < ipHdrLen + transportHdrLen) {
|
||||
throw new IllegalStateException("Unexpected buffer length: " + len);
|
||||
}
|
||||
// Swap addresses.
|
||||
PacketReflectorUtil.swapAddresses(mBuf, version);
|
||||
|
||||
// Send the packet to the destination fd.
|
||||
forwardPacket(mBuf, len);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
Log.i(TAG, "starting fd=" + mSrcFd + " valid=" + mSrcFd.valid());
|
||||
while (!interrupted() && mSrcFd.valid()) {
|
||||
processPacket();
|
||||
}
|
||||
Log.i(TAG, "exiting fd=" + mSrcFd + " valid=" + mSrcFd.valid());
|
||||
}
|
||||
}
|
||||
|
|
@ -87,31 +87,6 @@ public class PacketReflector extends Thread {
|
|||
mBuf = new byte[mtu];
|
||||
}
|
||||
|
||||
private static void swapBytes(@NonNull byte[] buf, int pos1, int pos2, int len) {
|
||||
for (int i = 0; i < len; i++) {
|
||||
byte b = buf[pos1 + i];
|
||||
buf[pos1 + i] = buf[pos2 + i];
|
||||
buf[pos2 + i] = b;
|
||||
}
|
||||
}
|
||||
|
||||
private static void swapAddresses(@NonNull byte[] buf, int version) {
|
||||
int addrPos, addrLen;
|
||||
switch (version) {
|
||||
case 4:
|
||||
addrPos = IPV4_ADDR_OFFSET;
|
||||
addrLen = IPV4_ADDR_LENGTH;
|
||||
break;
|
||||
case 6:
|
||||
addrPos = IPV6_ADDR_OFFSET;
|
||||
addrLen = IPV6_ADDR_LENGTH;
|
||||
break;
|
||||
default:
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
swapBytes(buf, addrPos, addrPos + addrLen, addrLen);
|
||||
}
|
||||
|
||||
// Reflect TCP packets: swap the source and destination addresses, but don't change the ports.
|
||||
// This is used by the test to "connect to itself" through the VPN.
|
||||
private void processTcpPacket(@NonNull byte[] buf, int version, int len, int hdrLen) {
|
||||
|
|
@ -120,7 +95,7 @@ public class PacketReflector extends Thread {
|
|||
}
|
||||
|
||||
// Swap src and dst IP addresses.
|
||||
swapAddresses(buf, version);
|
||||
PacketReflectorUtil.swapAddresses(buf, version);
|
||||
|
||||
// Send the packet back.
|
||||
writePacket(buf, len);
|
||||
|
|
@ -134,11 +109,11 @@ public class PacketReflector extends Thread {
|
|||
}
|
||||
|
||||
// Swap src and dst IP addresses.
|
||||
swapAddresses(buf, version);
|
||||
PacketReflectorUtil.swapAddresses(buf, version);
|
||||
|
||||
// Swap dst and src ports.
|
||||
int portOffset = hdrLen;
|
||||
swapBytes(buf, portOffset, portOffset + 2, 2);
|
||||
PacketReflectorUtil.swapBytes(buf, portOffset, portOffset + 2, 2);
|
||||
|
||||
// Send the packet back.
|
||||
writePacket(buf, len);
|
||||
|
|
@ -160,7 +135,7 @@ public class PacketReflector extends Thread {
|
|||
|
||||
// Swap src and dst IP addresses, and send the packet back.
|
||||
// This effectively pings the device to see if it replies.
|
||||
swapAddresses(buf, version);
|
||||
PacketReflectorUtil.swapAddresses(buf, version);
|
||||
writePacket(buf, len);
|
||||
|
||||
// The device should have replied, and buf should now contain a ping response.
|
||||
|
|
@ -202,7 +177,7 @@ public class PacketReflector extends Thread {
|
|||
}
|
||||
|
||||
// Now swap the addresses again and reflect the packet. This sends a ping reply.
|
||||
swapAddresses(buf, version);
|
||||
PacketReflectorUtil.swapAddresses(buf, version);
|
||||
writePacket(buf, len);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -112,3 +112,28 @@ fun fixPacketChecksum(buf: ByteArray, len: Int, version: Int, protocol: Byte) {
|
|||
else -> throw IllegalArgumentException("Unsupported protocol: $protocol")
|
||||
}
|
||||
}
|
||||
|
||||
fun swapBytes(buf: ByteArray, pos1: Int, pos2: Int, len: Int) {
|
||||
for (i in 0 until len) {
|
||||
val b = buf[pos1 + i]
|
||||
buf[pos1 + i] = buf[pos2 + i]
|
||||
buf[pos2 + i] = b
|
||||
}
|
||||
}
|
||||
|
||||
fun swapAddresses(buf: ByteArray, version: Int) {
|
||||
val addrPos: Int
|
||||
val addrLen: Int
|
||||
when (version) {
|
||||
4 -> {
|
||||
addrPos = PacketReflector.IPV4_ADDR_OFFSET
|
||||
addrLen = PacketReflector.IPV4_ADDR_LENGTH
|
||||
}
|
||||
6 -> {
|
||||
addrPos = PacketReflector.IPV6_ADDR_OFFSET
|
||||
addrLen = PacketReflector.IPV6_ADDR_LENGTH
|
||||
}
|
||||
else -> throw java.lang.IllegalArgumentException()
|
||||
}
|
||||
swapBytes(buf, addrPos, addrPos + addrLen, addrLen)
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue