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