• 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 android.content.Context
20 import android.net.ConnectivityManager
21 import android.net.LinkAddress
22 import android.net.LinkProperties
23 import android.net.Network
24 import android.net.NetworkCapabilities
25 import android.net.NetworkRequest
26 import android.net.TestNetworkInterface
27 import android.net.TestNetworkManager
28 import android.net.TestNetworkSpecifier
29 import android.os.Binder
30 import com.android.testutils.RecorderCallback.CallbackEntry.Available
31 import java.net.InetAddress
32 import libcore.io.IoUtils
33 
34 private const val MIN_PORT_NUMBER = 1025
35 private const val MAX_PORT_NUMBER = 65535
36 
37 /**
38  * A class that set up two {@link TestNetworkInterface} with NAT, and forward packets between them.
39  *
40  * See {@link NatPacketForwarder} for more detailed information.
41  */
42 class PacketBridge(
43     context: Context,
44     internalAddr: LinkAddress,
45     externalAddr: LinkAddress,
46     dnsAddr: InetAddress
47 ) {
48     private val natMap = NatMap()
49     private val binder = Binder()
50 
51     private val cm = context.getSystemService(ConnectivityManager::class.java)
52     private val tnm = context.getSystemService(TestNetworkManager::class.java)
53 
54     // Create test networks.
55     private val internalIface = tnm.createTunInterface(listOf(internalAddr))
56     private val externalIface = tnm.createTunInterface(listOf(externalAddr))
57 
58     // Register test networks to ConnectivityService.
59     private val internalNetworkCallback: TestableNetworkCallback
60     private val externalNetworkCallback: TestableNetworkCallback
61     val internalNetwork: Network
62     val externalNetwork: Network
63     init {
64         val (inCb, inNet) = createTestNetwork(internalIface, internalAddr, dnsAddr)
65         val (exCb, exNet) = createTestNetwork(externalIface, externalAddr, dnsAddr)
66         internalNetworkCallback = inCb
67         externalNetworkCallback = exCb
68         internalNetwork = inNet
69         externalNetwork = exNet
70     }
71 
72     // Setup the packet bridge.
73     private val internalFd = internalIface.fileDescriptor.fileDescriptor
74     private val externalFd = externalIface.fileDescriptor.fileDescriptor
75 
76     private val pr1 = NatInternalPacketForwarder(
77         internalFd,
78         1500,
79         externalFd,
80         externalAddr.address,
81         natMap
82     )
83     private val pr2 = NatExternalPacketForwarder(
84         externalFd,
85         1500,
86         internalFd,
87         externalAddr.address,
88         natMap
89     )
90 
startnull91     fun start() {
92         IoUtils.setBlocking(internalFd, true /* blocking */)
93         IoUtils.setBlocking(externalFd, true /* blocking */)
94         pr1.start()
95         pr2.start()
96     }
97 
stopnull98     fun stop() {
99         pr1.interrupt()
100         pr2.interrupt()
101         cm.unregisterNetworkCallback(internalNetworkCallback)
102         cm.unregisterNetworkCallback(externalNetworkCallback)
103     }
104 
105     /**
106      * Creates a test network with given test TUN interface and addresses.
107      */
createTestNetworknull108     private fun createTestNetwork(
109         testIface: TestNetworkInterface,
110         addr: LinkAddress,
111         dnsAddr: InetAddress
112     ): Pair<TestableNetworkCallback, Network> {
113         // Make a network request to hold the test network
114         val nr = NetworkRequest.Builder()
115             .clearCapabilities()
116             .addTransportType(NetworkCapabilities.TRANSPORT_TEST)
117             .setNetworkSpecifier(TestNetworkSpecifier(testIface.interfaceName))
118             .build()
119         val testCb = TestableNetworkCallback()
120         cm.requestNetwork(nr, testCb)
121 
122         val lp = LinkProperties().apply {
123             addLinkAddress(addr)
124             interfaceName = testIface.interfaceName
125             addDnsServer(dnsAddr)
126         }
127         tnm.setupTestNetwork(lp, true /* isMetered */, binder)
128 
129         // Wait for available before return.
130         val network = testCb.expect<Available>().network
131         return testCb to network
132     }
133 
134     /**
135      * A helper class to maintain the mappings between internal addresses/ports and external
136      * ports.
137      *
138      * This class assigns an unused external port number if the mapping between
139      * srcaddress:srcport:protocol and the external port does not exist yet.
140      *
141      * Note that this class is not thread-safe. The instance of the class needs to be
142      * synchronized in the callers when being used in multiple threads.
143      */
144     class NatMap {
145         data class AddressInfo(val address: InetAddress, val port: Int, val protocol: Int)
146 
147         private val mToExternalPort = HashMap<AddressInfo, Int>()
148         private val mFromExternalPort = HashMap<Int, AddressInfo>()
149 
150         // Skip well-known port 0~1024.
151         private var nextExternalPort = MIN_PORT_NUMBER
152 
toExternalPortnull153         fun toExternalPort(addr: InetAddress, port: Int, protocol: Int): Int {
154             val info = AddressInfo(addr, port, protocol)
155             val extPort: Int
156             if (!mToExternalPort.containsKey(info)) {
157                 extPort = nextExternalPort++
158                 if (nextExternalPort > MAX_PORT_NUMBER) {
159                     throw IllegalStateException("Available ports are exhausted")
160                 }
161                 mToExternalPort[info] = extPort
162                 mFromExternalPort[extPort] = info
163             } else {
164                 extPort = mToExternalPort[info]!!
165             }
166             return extPort
167         }
168 
fromExternalPortnull169         fun fromExternalPort(port: Int): AddressInfo? {
170             return mFromExternalPort[port]
171         }
172     }
173 }
174