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.server
18
19 import android.net.InetAddresses
20 import android.net.LinkAddress
21 import android.net.LinkProperties
22 import android.net.NetworkCapabilities
23 import android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET
24 import android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED
25 import android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VPN
26 import android.net.NetworkCapabilities.TRANSPORT_VPN
27 import android.net.NetworkCapabilities.TRANSPORT_WIFI
28 import android.net.NetworkRequest
29 import android.net.VpnManager.TYPE_VPN_SERVICE
30 import android.net.VpnTransportInfo
31 import android.os.Build
32 import androidx.test.filters.SmallTest
33 import com.android.server.connectivity.ConnectivityFlags
34 import com.android.testutils.DevSdkIgnoreRule
35 import com.android.testutils.DevSdkIgnoreRunner
36 import com.android.testutils.RecorderCallback.CallbackEntry.LinkPropertiesChanged
37 import com.android.testutils.TestableNetworkCallback
38 import org.junit.Test
39 import org.junit.runner.RunWith
40 import org.mockito.InOrder
41 import org.mockito.Mockito.inOrder
42 import org.mockito.Mockito.never
43 import org.mockito.Mockito.timeout
44 import org.mockito.Mockito.verify
45
46 private const val VPN_IFNAME = "tun10041"
47 private const val VPN_IFNAME2 = "tun10042"
48 private const val WIFI_IFNAME = "wlan0"
49 private const val TIMEOUT_MS = 1_000L
50 private const val LONG_TIMEOUT_MS = 5_000
51
vpnNcnull52 private fun vpnNc() = NetworkCapabilities.Builder()
53 .addTransportType(TRANSPORT_VPN)
54 .removeCapability(NET_CAPABILITY_NOT_VPN)
55 .addCapability(NET_CAPABILITY_NOT_VCN_MANAGED)
56 .setTransportInfo(
57 VpnTransportInfo(
58 TYPE_VPN_SERVICE,
59 "MySession12345",
60 false /* bypassable */,
61 false /* longLivedTcpConnectionsExpensive */
62 )
63 )
64 .build()
65
66 private fun wifiNc() = NetworkCapabilities.Builder()
67 .addTransportType(TRANSPORT_WIFI)
68 .addCapability(NET_CAPABILITY_INTERNET)
69 .addCapability(NET_CAPABILITY_NOT_VCN_MANAGED)
70 .build()
71
72 private fun nr(transport: Int) = NetworkRequest.Builder()
73 .clearCapabilities()
74 .addTransportType(transport).apply {
75 if (transport != TRANSPORT_VPN) {
76 addCapability(NET_CAPABILITY_NOT_VPN)
77 }
78 }.build()
79
<lambda>null80 private fun lp(iface: String, vararg linkAddresses: LinkAddress) = LinkProperties().apply {
81 interfaceName = iface
82 for (linkAddress in linkAddresses) {
83 addLinkAddress(linkAddress)
84 }
85 }
86
87 @RunWith(DevSdkIgnoreRunner::class)
88 @SmallTest
89 @DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.S_V2)
90 class CSIngressDiscardRuleTests : CSTest() {
91 private val IPV6_ADDRESS = InetAddresses.parseNumericAddress("2001:db8:1::1")
92 private val IPV6_LINK_ADDRESS = LinkAddress(IPV6_ADDRESS, 64)
93 private val IPV6_ADDRESS2 = InetAddresses.parseNumericAddress("2001:db8:1::2")
94 private val IPV6_LINK_ADDRESS2 = LinkAddress(IPV6_ADDRESS2, 64)
95 private val IPV6_ADDRESS3 = InetAddresses.parseNumericAddress("2001:db8:1::3")
96 private val IPV6_LINK_ADDRESS3 = LinkAddress(IPV6_ADDRESS3, 64)
97 private val LOCAL_IPV6_ADDRRESS = InetAddresses.parseNumericAddress("fe80::1234")
98 private val LOCAL_IPV6_LINK_ADDRRESS = LinkAddress(LOCAL_IPV6_ADDRRESS, 64)
99
verifyNoMoreIngressDiscardRuleChangenull100 fun verifyNoMoreIngressDiscardRuleChange(inorder: InOrder) {
101 inorder.verify(bpfNetMaps, never()).setIngressDiscardRule(any(), any())
102 inorder.verify(bpfNetMaps, never()).removeIngressDiscardRule(any())
103 }
104
105 @Test
testVpnIngressDiscardRule_UpdateVpnAddressnull106 fun testVpnIngressDiscardRule_UpdateVpnAddress() {
107 // non-VPN network whose address will be not duplicated with VPN address
108 val wifiNc = wifiNc()
109 val wifiLp = lp(WIFI_IFNAME, IPV6_LINK_ADDRESS3)
110 val wifiAgent = Agent(nc = wifiNc, lp = wifiLp)
111 wifiAgent.connect()
112
113 val nr = nr(TRANSPORT_VPN)
114 val cb = TestableNetworkCallback()
115 cm.registerNetworkCallback(nr, cb)
116 val nc = vpnNc()
117 val lp = lp(VPN_IFNAME, IPV6_LINK_ADDRESS, LOCAL_IPV6_LINK_ADDRRESS)
118 val agent = Agent(nc = nc, lp = lp)
119 agent.connect()
120 cb.expectAvailableCallbacks(agent.network, validated = false)
121
122 // IngressDiscardRule is added to the VPN address
123 verify(bpfNetMaps).setIngressDiscardRule(IPV6_ADDRESS, VPN_IFNAME)
124 verify(bpfNetMaps, never()).setIngressDiscardRule(LOCAL_IPV6_ADDRRESS, VPN_IFNAME)
125
126 // The VPN address is changed
127 val newLp = lp(VPN_IFNAME, IPV6_LINK_ADDRESS2, LOCAL_IPV6_LINK_ADDRRESS)
128 agent.sendLinkProperties(newLp)
129 cb.expect<LinkPropertiesChanged>(agent.network)
130
131 // IngressDiscardRule is removed from the old VPN address and added to the new VPN address
132 verify(bpfNetMaps).removeIngressDiscardRule(IPV6_ADDRESS)
133 verify(bpfNetMaps).setIngressDiscardRule(IPV6_ADDRESS2, VPN_IFNAME)
134 verify(bpfNetMaps, never()).setIngressDiscardRule(LOCAL_IPV6_ADDRRESS, VPN_IFNAME)
135
136 agent.disconnect()
137 verify(bpfNetMaps, timeout(TIMEOUT_MS)).removeIngressDiscardRule(IPV6_ADDRESS2)
138
139 cm.unregisterNetworkCallback(cb)
140 }
141
142 @Test
testVpnIngressDiscardRule_UpdateInterfaceNamenull143 fun testVpnIngressDiscardRule_UpdateInterfaceName() {
144 val inorder = inOrder(bpfNetMaps)
145
146 val nr = nr(TRANSPORT_VPN)
147 val cb = TestableNetworkCallback()
148 cm.registerNetworkCallback(nr, cb)
149 val nc = vpnNc()
150 val lp = lp(VPN_IFNAME, IPV6_LINK_ADDRESS, LOCAL_IPV6_LINK_ADDRRESS)
151 val agent = Agent(nc = nc, lp = lp)
152 agent.connect()
153 cb.expectAvailableCallbacks(agent.network, validated = false)
154
155 // IngressDiscardRule is added to the VPN address
156 inorder.verify(bpfNetMaps).setIngressDiscardRule(IPV6_ADDRESS, VPN_IFNAME)
157 verifyNoMoreIngressDiscardRuleChange(inorder)
158
159 // The VPN interface name is changed
160 val newlp = lp(VPN_IFNAME2, IPV6_LINK_ADDRESS, LOCAL_IPV6_LINK_ADDRRESS)
161 agent.sendLinkProperties(newlp)
162 cb.expect<LinkPropertiesChanged>(agent.network)
163
164 // IngressDiscardRule is updated with the new interface name
165 inorder.verify(bpfNetMaps).setIngressDiscardRule(IPV6_ADDRESS, VPN_IFNAME2)
166 verifyNoMoreIngressDiscardRuleChange(inorder)
167
168 agent.disconnect()
169 inorder.verify(bpfNetMaps, timeout(TIMEOUT_MS)).removeIngressDiscardRule(IPV6_ADDRESS)
170
171 cm.unregisterNetworkCallback(cb)
172 }
173
174 @Test
testVpnIngressDiscardRule_DuplicatedIpAddress_UpdateVpnAddressnull175 fun testVpnIngressDiscardRule_DuplicatedIpAddress_UpdateVpnAddress() {
176 val inorder = inOrder(bpfNetMaps)
177
178 val wifiNc = wifiNc()
179 val wifiLp = lp(WIFI_IFNAME, IPV6_LINK_ADDRESS, LOCAL_IPV6_LINK_ADDRRESS)
180 val wifiAgent = Agent(nc = wifiNc, lp = wifiLp)
181 wifiAgent.connect()
182
183 // IngressDiscardRule is not added to non-VPN interfaces
184 inorder.verify(bpfNetMaps, never()).setIngressDiscardRule(any(), any())
185
186 val nr = nr(TRANSPORT_VPN)
187 val cb = TestableNetworkCallback()
188 cm.requestNetwork(nr, cb)
189 val vpnNc = vpnNc()
190 val vpnLp = lp(VPN_IFNAME, IPV6_LINK_ADDRESS, LOCAL_IPV6_LINK_ADDRRESS)
191 val vpnAgent = Agent(nc = vpnNc, lp = vpnLp)
192 vpnAgent.connect()
193 cb.expectAvailableCallbacks(vpnAgent.network, validated = false)
194
195 // IngressDiscardRule is not added since the VPN address is duplicated with the Wi-Fi
196 // address
197 inorder.verify(bpfNetMaps, never()).setIngressDiscardRule(any(), any())
198
199 // The VPN address is changed to a different address from the Wi-Fi interface
200 val newVpnlp = lp(VPN_IFNAME, IPV6_LINK_ADDRESS2, LOCAL_IPV6_LINK_ADDRRESS)
201 vpnAgent.sendLinkProperties(newVpnlp)
202
203 // IngressDiscardRule is added to the VPN address since the VPN address is not duplicated
204 // with the Wi-Fi address
205 cb.expect<LinkPropertiesChanged>(vpnAgent.network)
206 inorder.verify(bpfNetMaps).setIngressDiscardRule(IPV6_ADDRESS2, VPN_IFNAME)
207
208 // The VPN address is changed back to the same address as the Wi-Fi interface
209 vpnAgent.sendLinkProperties(vpnLp)
210 cb.expect<LinkPropertiesChanged>(vpnAgent.network)
211
212 // IngressDiscardRule for IPV6_ADDRESS2 is removed but IngressDiscardRule for
213 // IPV6_LINK_ADDRESS is not added since Wi-Fi also uses IPV6_LINK_ADDRESS
214 inorder.verify(bpfNetMaps).removeIngressDiscardRule(IPV6_ADDRESS2)
215 verifyNoMoreIngressDiscardRuleChange(inorder)
216
217 vpnAgent.disconnect()
218 verifyNoMoreIngressDiscardRuleChange(inorder)
219
220 cm.unregisterNetworkCallback(cb)
221 }
222
223 @Test
testVpnIngressDiscardRule_DuplicatedIpAddress_UpdateNonVpnAddressnull224 fun testVpnIngressDiscardRule_DuplicatedIpAddress_UpdateNonVpnAddress() {
225 val inorder = inOrder(bpfNetMaps)
226
227 val vpnNc = vpnNc()
228 val vpnLp = lp(VPN_IFNAME, IPV6_LINK_ADDRESS, LOCAL_IPV6_LINK_ADDRRESS)
229 val vpnAgent = Agent(nc = vpnNc, lp = vpnLp)
230 vpnAgent.connect()
231
232 // IngressDiscardRule is added to the VPN address
233 inorder.verify(bpfNetMaps).setIngressDiscardRule(IPV6_ADDRESS, VPN_IFNAME)
234 verifyNoMoreIngressDiscardRuleChange(inorder)
235
236 val nr = nr(TRANSPORT_WIFI)
237 val cb = TestableNetworkCallback()
238 cm.requestNetwork(nr, cb)
239 val wifiNc = wifiNc()
240 val wifiLp = lp(WIFI_IFNAME, IPV6_LINK_ADDRESS, LOCAL_IPV6_LINK_ADDRRESS)
241 val wifiAgent = Agent(nc = wifiNc, lp = wifiLp)
242 wifiAgent.connect()
243 cb.expectAvailableCallbacks(wifiAgent.network, validated = false)
244
245 // IngressDiscardRule is removed since the VPN address is duplicated with the Wi-Fi address
246 inorder.verify(bpfNetMaps).removeIngressDiscardRule(IPV6_ADDRESS)
247
248 // The Wi-Fi address is changed to a different address from the VPN interface
249 val newWifilp = lp(WIFI_IFNAME, IPV6_LINK_ADDRESS2, LOCAL_IPV6_LINK_ADDRRESS)
250 wifiAgent.sendLinkProperties(newWifilp)
251 cb.expect<LinkPropertiesChanged>(wifiAgent.network)
252
253 // IngressDiscardRule is added to the VPN address since the VPN address is not duplicated
254 // with the Wi-Fi address
255 inorder.verify(bpfNetMaps).setIngressDiscardRule(IPV6_ADDRESS, VPN_IFNAME)
256 verifyNoMoreIngressDiscardRuleChange(inorder)
257
258 // The Wi-Fi address is changed back to the same address as the VPN interface
259 wifiAgent.sendLinkProperties(wifiLp)
260 cb.expect<LinkPropertiesChanged>(wifiAgent.network)
261
262 // IngressDiscardRule is removed since the VPN address is duplicated with the Wi-Fi address
263 inorder.verify(bpfNetMaps).removeIngressDiscardRule(IPV6_ADDRESS)
264
265 // IngressDiscardRule is added to the VPN address since Wi-Fi is disconnected
266 wifiAgent.disconnect()
267 inorder.verify(bpfNetMaps, timeout(TIMEOUT_MS))
268 .setIngressDiscardRule(IPV6_ADDRESS, VPN_IFNAME)
269
270 vpnAgent.disconnect()
271 inorder.verify(bpfNetMaps, timeout(TIMEOUT_MS)).removeIngressDiscardRule(IPV6_ADDRESS)
272
273 cm.unregisterNetworkCallback(cb)
274 }
275
276 @Test
testVpnIngressDiscardRule_UnregisterAfterReplacementnull277 fun testVpnIngressDiscardRule_UnregisterAfterReplacement() {
278 val wifiNc = wifiNc()
279 val wifiLp = lp(WIFI_IFNAME, IPV6_LINK_ADDRESS, LOCAL_IPV6_LINK_ADDRRESS)
280 val wifiAgent = Agent(nc = wifiNc, lp = wifiLp)
281 wifiAgent.connect()
282 wifiAgent.unregisterAfterReplacement(LONG_TIMEOUT_MS)
283 waitForIdle()
284
285 val vpnNc = vpnNc()
286 val vpnLp = lp(VPN_IFNAME, IPV6_LINK_ADDRESS, LOCAL_IPV6_LINK_ADDRRESS)
287 val vpnAgent = Agent(nc = vpnNc, lp = vpnLp)
288 vpnAgent.connect()
289
290 // IngressDiscardRule is added since the Wi-Fi network is destroyed
291 verify(bpfNetMaps).setIngressDiscardRule(IPV6_ADDRESS, VPN_IFNAME)
292
293 // IngressDiscardRule is removed since the VPN network is destroyed
294 vpnAgent.unregisterAfterReplacement(LONG_TIMEOUT_MS)
295 waitForIdle()
296 verify(bpfNetMaps).removeIngressDiscardRule(IPV6_ADDRESS)
297 }
298
299 @Test @FeatureFlags([Flag(ConnectivityFlags.INGRESS_TO_VPN_ADDRESS_FILTERING, false)])
testVpnIngressDiscardRule_FeatureDisablednull300 fun testVpnIngressDiscardRule_FeatureDisabled() {
301 val nr = nr(TRANSPORT_VPN)
302 val cb = TestableNetworkCallback()
303 cm.registerNetworkCallback(nr, cb)
304 val nc = vpnNc()
305 val lp = lp(VPN_IFNAME, IPV6_LINK_ADDRESS, LOCAL_IPV6_LINK_ADDRRESS)
306 val agent = Agent(nc = nc, lp = lp)
307 agent.connect()
308 cb.expectAvailableCallbacks(agent.network, validated = false)
309
310 // IngressDiscardRule should not be added since feature is disabled
311 verify(bpfNetMaps, never()).setIngressDiscardRule(any(), any())
312 }
313 }
314