Merge changes from topics "remove-napt", "support-multipleaddr" into main
* changes: Support multiple addresses in PacketBridge Remove NAPT from PacketForwarder
This commit is contained in:
commit
24b54c2285
6 changed files with 58 additions and 318 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,26 @@ 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.
|
||||
*/
|
||||
class PacketBridge(
|
||||
context: Context,
|
||||
internalAddr: LinkAddress,
|
||||
externalAddr: LinkAddress,
|
||||
addresses: List<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(addresses)
|
||||
@SuppressLint("MissingPermission")
|
||||
private val externalIface = tnm.createTunInterface(addresses)
|
||||
|
||||
// Register test networks to ConnectivityService.
|
||||
private val internalNetworkCallback: TestableNetworkCallback
|
||||
|
|
@ -61,32 +59,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, addresses, dnsAddr)
|
||||
val (exCb, exNet) = createTestNetwork(externalIface, addresses, 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 */)
|
||||
|
|
@ -107,7 +93,7 @@ class PacketBridge(
|
|||
*/
|
||||
private fun createTestNetwork(
|
||||
testIface: TestNetworkInterface,
|
||||
addr: LinkAddress,
|
||||
addresses: List<LinkAddress>,
|
||||
dnsAddr: InetAddress
|
||||
): Pair<TestableNetworkCallback, Network> {
|
||||
// Make a network request to hold the test network
|
||||
|
|
@ -120,7 +106,7 @@ class PacketBridge(
|
|||
cm.requestNetwork(nr, testCb)
|
||||
|
||||
val lp = LinkProperties().apply {
|
||||
addLinkAddress(addr)
|
||||
setLinkAddresses(addresses)
|
||||
interfaceName = testIface.interfaceName
|
||||
addDnsServer(dnsAddr)
|
||||
}
|
||||
|
|
@ -130,44 +116,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