1 /* 2 * Copyright (C) 2023 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.testutils; 18 19 import static com.android.testutils.PacketReflector.IPPROTO_TCP; 20 import static com.android.testutils.PacketReflector.IPPROTO_UDP; 21 import static com.android.testutils.PacketReflector.IPV4_HEADER_LENGTH; 22 import static com.android.testutils.PacketReflector.IPV6_HEADER_LENGTH; 23 import static com.android.testutils.PacketReflector.IPV6_PROTO_OFFSET; 24 import static com.android.testutils.PacketReflector.TCP_HEADER_LENGTH; 25 import static com.android.testutils.PacketReflector.UDP_HEADER_LENGTH; 26 27 import android.annotation.NonNull; 28 import android.net.TestNetworkInterface; 29 import android.system.ErrnoException; 30 import android.system.Os; 31 import android.util.Log; 32 33 import androidx.annotation.GuardedBy; 34 35 import java.io.FileDescriptor; 36 import java.io.IOException; 37 import java.net.InetAddress; 38 import java.util.Objects; 39 40 /** 41 * A class that forwards packets from a {@link TestNetworkInterface} to another 42 * {@link TestNetworkInterface} with NAT. 43 * 44 * For testing purposes, a {@link TestNetworkInterface} provides a {@link FileDescriptor} 45 * which allows content injection on the test network. However, this could be hard to use 46 * because the callers need to compose IP packets in order to inject content to the 47 * test network. 48 * 49 * In order to remove the need of composing the IP packets, this class forwards IP packets to 50 * the {@link FileDescriptor} of another {@link TestNetworkInterface} instance. Thus, 51 * the TCP/IP headers could be parsed/composed automatically by the protocol stack of this 52 * additional {@link TestNetworkInterface}, while the payload is supplied by the 53 * servers run on the interface. 54 * 55 * To make it work, an internal interface and an external interface are defined, where 56 * the client might send packets from the internal interface which are originated from 57 * multiple addresses to a server that listens on the external address. 58 * 59 * When forwarding the outgoing packet on the internal interface, a simple NAT mechanism 60 * is implemented during forwarding, which will swap the source and destination, 61 * but replacing the source address with the external address, 62 * e.g. 192.168.1.1:1234 -> 8.8.8.8:80 will be translated into 8.8.8.8:1234 -> 1.2.3.4:80. 63 * 64 * For the above example, a client who sends http request will have a hallucination that 65 * it is talking to a remote server at 8.8.8.8. Also, the server listens on 1.2.3.4 will 66 * have a different hallucination that the request is sent from a remote client at 8.8.8.8, 67 * to a local address 1.2.3.4. 68 * 69 * And a NAT mapping is created at the time when the outgoing packet is forwarded. 70 * With a different internal source port, the instance learned that when a response with the 71 * destination port 1234, it should forward the packet to the internal address 192.168.1.1. 72 * 73 * For the incoming packet received from external interface, for example a http response sent 74 * from the http server, the same mechanism is applied but in a different direction, 75 * where the source and destination will be swapped, and the source address will be replaced 76 * with the internal address, which is obtained from the NAT mapping described above. 77 */ 78 public abstract class NatPacketForwarderBase extends Thread { 79 private static final String TAG = "NatPacketForwarder"; 80 static final int DESTINATION_PORT_OFFSET = 2; 81 82 // The source fd to read packets from. 83 @NonNull 84 final FileDescriptor mSrcFd; 85 // The buffer to temporarily hold the entire packet after receiving. 86 @NonNull 87 final byte[] mBuf; 88 // The destination fd to write packets to. 89 @NonNull 90 final FileDescriptor mDstFd; 91 // The NAT mapping table shared between two NatPacketForwarder instances to map from 92 // the source port to the associated internal address. The map can be read/write from two 93 // different threads on any given time whenever receiving packets on the 94 // {@link TestNetworkInterface}. Thus, synchronize on the object when reading/writing is needed. 95 @GuardedBy("mNatMap") 96 @NonNull 97 final PacketBridge.NatMap mNatMap; 98 // The address of the external interface. See {@link NatPacketForwarder}. 99 @NonNull 100 final InetAddress mExtAddr; 101 102 /** 103 * Construct a {@link NatPacketForwarderBase}. 104 * 105 * This class reads packets from {@code srcFd} of a {@link TestNetworkInterface}, and 106 * forwards them to the {@code dstFd} of another {@link TestNetworkInterface} with 107 * NAT applied. See {@link NatPacketForwarderBase}. 108 * 109 * To apply NAT, the address of the external interface needs to be supplied through 110 * {@code extAddr} to identify the external interface. And a shared NAT mapping table, 111 * {@code natMap} is needed to be shared between these two instances. 112 * 113 * Note that this class is not useful if the instance is not managed by a 114 * {@link PacketBridge} to set up a two-way communication. 115 * 116 * @param srcFd {@link FileDescriptor} to read packets from. 117 * @param mtu MTU of the test network. 118 * @param dstFd {@link FileDescriptor} to write packets to. 119 * @param extAddr the external address, which is the address of the external interface. 120 * See {@link NatPacketForwarderBase}. 121 * @param natMap the NAT mapping table shared between two {@link NatPacketForwarderBase} 122 * instance. 123 */ NatPacketForwarderBase(@onNull FileDescriptor srcFd, int mtu, @NonNull FileDescriptor dstFd, @NonNull InetAddress extAddr, @NonNull PacketBridge.NatMap natMap)124 public NatPacketForwarderBase(@NonNull FileDescriptor srcFd, int mtu, 125 @NonNull FileDescriptor dstFd, @NonNull InetAddress extAddr, 126 @NonNull PacketBridge.NatMap natMap) { 127 super(TAG); 128 mSrcFd = Objects.requireNonNull(srcFd); 129 mBuf = new byte[mtu]; 130 mDstFd = Objects.requireNonNull(dstFd); 131 mExtAddr = Objects.requireNonNull(extAddr); 132 mNatMap = Objects.requireNonNull(natMap); 133 } 134 135 /** 136 * A method to prepare forwarding packets between two instances of {@link TestNetworkInterface}, 137 * which includes re-write addresses, ports and fix up checksums. 138 * Subclasses should override this method to implement a simple NAT. 139 */ preparePacketForForwarding(@onNull byte[] buf, int len, int version, int proto)140 abstract void preparePacketForForwarding(@NonNull byte[] buf, int len, int version, int proto); 141 forwardPacket(@onNull byte[] buf, int len)142 private void forwardPacket(@NonNull byte[] buf, int len) { 143 try { 144 Os.write(mDstFd, buf, 0, len); 145 } catch (ErrnoException | IOException e) { 146 Log.e(TAG, "Error writing packet: " + e.getMessage()); 147 } 148 } 149 150 // Reads one packet from mSrcFd, and writes the packet to the mDstFd for supported protocols. processPacket()151 private void processPacket() { 152 final int len = PacketReflectorUtil.readPacket(mSrcFd, mBuf); 153 if (len < 1) { 154 throw new IllegalStateException("Unexpected buffer length: " + len); 155 } 156 157 final int version = mBuf[0] >>> 4; 158 final int protoPos, ipHdrLen; 159 switch (version) { 160 case 4: 161 ipHdrLen = IPV4_HEADER_LENGTH; 162 protoPos = PacketReflector.IPV4_PROTO_OFFSET; 163 break; 164 case 6: 165 ipHdrLen = IPV6_HEADER_LENGTH; 166 protoPos = IPV6_PROTO_OFFSET; 167 break; 168 default: 169 throw new IllegalStateException("Unexpected version: " + version); 170 } 171 if (len < ipHdrLen) { 172 throw new IllegalStateException("Unexpected buffer length: " + len); 173 } 174 175 final byte proto = mBuf[protoPos]; 176 final int transportHdrLen; 177 switch (proto) { 178 case IPPROTO_TCP: 179 transportHdrLen = TCP_HEADER_LENGTH; 180 break; 181 case IPPROTO_UDP: 182 transportHdrLen = UDP_HEADER_LENGTH; 183 break; 184 // TODO: Support ICMP. 185 default: 186 return; // Unknown protocol, ignored. 187 } 188 189 if (len < ipHdrLen + transportHdrLen) { 190 throw new IllegalStateException("Unexpected buffer length: " + len); 191 } 192 // Re-write addresses, ports and fix up checksums. 193 preparePacketForForwarding(mBuf, len, version, proto); 194 // Send the packet to the destination fd. 195 forwardPacket(mBuf, len); 196 } 197 198 @Override run()199 public void run() { 200 Log.i(TAG, "starting fd=" + mSrcFd + " valid=" + mSrcFd.valid()); 201 while (!interrupted() && mSrcFd.valid()) { 202 processPacket(); 203 } 204 Log.i(TAG, "exiting fd=" + mSrcFd + " valid=" + mSrcFd.valid()); 205 } 206 } 207