• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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