blob: 891966655148b5665ccdb4f86b0dd78042acbcd4 [file] [log] [blame]
Junyu Lai626045a2023-08-28 18:49:44 +08001/*
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
17package android.net
18
Junyu Lai9e880522023-11-14 15:44:37 +080019import android.net.BpfNetMapsConstants.DATA_SAVER_DISABLED
20import android.net.BpfNetMapsConstants.DATA_SAVER_ENABLED
21import android.net.BpfNetMapsConstants.DATA_SAVER_ENABLED_KEY
Junyu Laie0031522023-08-29 18:32:57 +080022import android.net.BpfNetMapsConstants.DOZABLE_MATCH
Junyu Laic3dc5b62023-09-06 19:10:02 +080023import android.net.BpfNetMapsConstants.HAPPY_BOX_MATCH
24import android.net.BpfNetMapsConstants.PENALTY_BOX_MATCH
Junyu Laie0031522023-08-29 18:32:57 +080025import android.net.BpfNetMapsConstants.STANDBY_MATCH
Junyu Lai626045a2023-08-28 18:49:44 +080026import android.net.BpfNetMapsConstants.UID_RULES_CONFIGURATION_KEY
27import android.net.BpfNetMapsUtils.getMatchByFirewallChain
Junyu Laie0031522023-08-29 18:32:57 +080028import android.os.Build.VERSION_CODES
Junyu Lai626045a2023-08-28 18:49:44 +080029import com.android.net.module.util.IBpfMap
30import com.android.net.module.util.Struct.S32
31import com.android.net.module.util.Struct.U32
Junyu Lai9e880522023-11-14 15:44:37 +080032import com.android.net.module.util.Struct.U8
33import com.android.testutils.DevSdkIgnoreRule
Junyu Lai626045a2023-08-28 18:49:44 +080034import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo
35import com.android.testutils.DevSdkIgnoreRunner
36import com.android.testutils.TestBpfMap
Junyu Laie0031522023-08-29 18:32:57 +080037import java.lang.reflect.Modifier
38import kotlin.test.assertEquals
Junyu Lai626045a2023-08-28 18:49:44 +080039import kotlin.test.assertFalse
40import kotlin.test.assertTrue
Junyu Lai9e880522023-11-14 15:44:37 +080041import org.junit.Rule
Junyu Lai626045a2023-08-28 18:49:44 +080042import org.junit.Test
43import org.junit.runner.RunWith
44
Junyu Laie0031522023-08-29 18:32:57 +080045private const val TEST_UID1 = 1234
46private const val TEST_UID2 = TEST_UID1 + 1
Junyu Laic3dc5b62023-09-06 19:10:02 +080047private const val TEST_UID3 = TEST_UID2 + 1
Junyu Laie0031522023-08-29 18:32:57 +080048private const val NO_IIF = 0
49
Junyu Lai626045a2023-08-28 18:49:44 +080050// pre-T devices does not support Bpf.
51@RunWith(DevSdkIgnoreRunner::class)
Junyu Laie0031522023-08-29 18:32:57 +080052@IgnoreUpTo(VERSION_CODES.S_V2)
Junyu Lai626045a2023-08-28 18:49:44 +080053class BpfNetMapsReaderTest {
Junyu Lai9e880522023-11-14 15:44:37 +080054 @Rule
55 @JvmField
56 val ignoreRule = DevSdkIgnoreRule()
57
Junyu Lai626045a2023-08-28 18:49:44 +080058 private val testConfigurationMap: IBpfMap<S32, U32> = TestBpfMap()
59 private val testUidOwnerMap: IBpfMap<S32, UidOwnerValue> = TestBpfMap()
Junyu Lai9e880522023-11-14 15:44:37 +080060 private val testDataSaverEnabledMap: IBpfMap<S32, U8> = TestBpfMap()
Junyu Lai626045a2023-08-28 18:49:44 +080061 private val bpfNetMapsReader = BpfNetMapsReader(
Junyu Lai9e880522023-11-14 15:44:37 +080062 TestDependencies(testConfigurationMap, testUidOwnerMap, testDataSaverEnabledMap))
Junyu Lai626045a2023-08-28 18:49:44 +080063
64 class TestDependencies(
65 private val configMap: IBpfMap<S32, U32>,
Junyu Lai9e880522023-11-14 15:44:37 +080066 private val uidOwnerMap: IBpfMap<S32, UidOwnerValue>,
67 private val dataSaverEnabledMap: IBpfMap<S32, U8>
Junyu Lai626045a2023-08-28 18:49:44 +080068 ) : BpfNetMapsReader.Dependencies() {
69 override fun getConfigurationMap() = configMap
70 override fun getUidOwnerMap() = uidOwnerMap
Junyu Lai9e880522023-11-14 15:44:37 +080071 override fun getDataSaverEnabledMap() = dataSaverEnabledMap
Junyu Lai626045a2023-08-28 18:49:44 +080072 }
73
74 private fun doTestIsChainEnabled(chain: Int) {
75 testConfigurationMap.updateEntry(
76 UID_RULES_CONFIGURATION_KEY,
77 U32(getMatchByFirewallChain(chain))
78 )
79 assertTrue(bpfNetMapsReader.isChainEnabled(chain))
80 testConfigurationMap.updateEntry(UID_RULES_CONFIGURATION_KEY, U32(0))
81 assertFalse(bpfNetMapsReader.isChainEnabled(chain))
82 }
83
84 @Test
85 @Throws(Exception::class)
86 fun testIsChainEnabled() {
87 doTestIsChainEnabled(ConnectivityManager.FIREWALL_CHAIN_DOZABLE)
88 doTestIsChainEnabled(ConnectivityManager.FIREWALL_CHAIN_STANDBY)
89 doTestIsChainEnabled(ConnectivityManager.FIREWALL_CHAIN_POWERSAVE)
90 doTestIsChainEnabled(ConnectivityManager.FIREWALL_CHAIN_RESTRICTED)
91 doTestIsChainEnabled(ConnectivityManager.FIREWALL_CHAIN_LOW_POWER_STANDBY)
92 }
Junyu Laie0031522023-08-29 18:32:57 +080093
94 @Test
95 fun testFirewallChainList() {
96 // Verify that when a firewall chain constant is added, it should also be included in
97 // firewall chain list.
98 val declaredChains = ConnectivityManager::class.java.declaredFields.filter {
99 Modifier.isStatic(it.modifiers) && it.name.startsWith("FIREWALL_CHAIN_")
100 }
101 // Verify the size matches, this also verifies no common item in allow and deny chains.
102 assertEquals(BpfNetMapsConstants.ALLOW_CHAINS.size +
103 BpfNetMapsConstants.DENY_CHAINS.size, declaredChains.size)
104 declaredChains.forEach {
105 assertTrue(BpfNetMapsConstants.ALLOW_CHAINS.contains(it.get(null)) ||
106 BpfNetMapsConstants.DENY_CHAINS.contains(it.get(null)))
107 }
108 }
109
110 private fun mockChainEnabled(chain: Int, enabled: Boolean) {
111 val config = testConfigurationMap.getValue(UID_RULES_CONFIGURATION_KEY).`val`
112 val newConfig = if (enabled) {
113 config or getMatchByFirewallChain(chain)
114 } else {
115 config and getMatchByFirewallChain(chain).inv()
116 }
117 testConfigurationMap.updateEntry(UID_RULES_CONFIGURATION_KEY, U32(newConfig))
118 }
119
Junyu Laic3dc5b62023-09-06 19:10:02 +0800120 fun isUidNetworkingBlocked(uid: Int, metered: Boolean = false, dataSaver: Boolean = false) =
121 bpfNetMapsReader.isUidNetworkingBlocked(uid, metered, dataSaver)
122
Junyu Laie0031522023-08-29 18:32:57 +0800123 @Test
124 fun testIsUidNetworkingBlockedByFirewallChains_allowChain() {
125 // With everything disabled by default, verify the return value is false.
126 testConfigurationMap.updateEntry(UID_RULES_CONFIGURATION_KEY, U32(0))
Junyu Laic3dc5b62023-09-06 19:10:02 +0800127 assertFalse(isUidNetworkingBlocked(TEST_UID1))
Junyu Laie0031522023-08-29 18:32:57 +0800128
129 // Enable dozable chain but does not provide allowed list. Verify the network is blocked
130 // for all uids.
131 mockChainEnabled(ConnectivityManager.FIREWALL_CHAIN_DOZABLE, true)
Junyu Laic3dc5b62023-09-06 19:10:02 +0800132 assertTrue(isUidNetworkingBlocked(TEST_UID1))
133 assertTrue(isUidNetworkingBlocked(TEST_UID2))
Junyu Laie0031522023-08-29 18:32:57 +0800134
135 // Add uid1 to dozable allowed list. Verify the network is not blocked for uid1, while
136 // uid2 is blocked.
137 testUidOwnerMap.updateEntry(S32(TEST_UID1), UidOwnerValue(NO_IIF, DOZABLE_MATCH))
Junyu Laic3dc5b62023-09-06 19:10:02 +0800138 assertFalse(isUidNetworkingBlocked(TEST_UID1))
139 assertTrue(isUidNetworkingBlocked(TEST_UID2))
Junyu Laie0031522023-08-29 18:32:57 +0800140 }
141
142 @Test
143 fun testIsUidNetworkingBlockedByFirewallChains_denyChain() {
144 // Enable standby chain but does not provide denied list. Verify the network is allowed
145 // for all uids.
146 testConfigurationMap.updateEntry(UID_RULES_CONFIGURATION_KEY, U32(0))
147 mockChainEnabled(ConnectivityManager.FIREWALL_CHAIN_STANDBY, true)
Junyu Laic3dc5b62023-09-06 19:10:02 +0800148 assertFalse(isUidNetworkingBlocked(TEST_UID1))
149 assertFalse(isUidNetworkingBlocked(TEST_UID2))
Junyu Laie0031522023-08-29 18:32:57 +0800150
151 // Add uid1 to standby allowed list. Verify the network is blocked for uid1, while
152 // uid2 is not blocked.
153 testUidOwnerMap.updateEntry(S32(TEST_UID1), UidOwnerValue(NO_IIF, STANDBY_MATCH))
Junyu Laic3dc5b62023-09-06 19:10:02 +0800154 assertTrue(isUidNetworkingBlocked(TEST_UID1))
155 assertFalse(isUidNetworkingBlocked(TEST_UID2))
Junyu Laie0031522023-08-29 18:32:57 +0800156 }
157
158 @Test
159 fun testIsUidNetworkingBlockedByFirewallChains_blockedWithAllowed() {
160 // Uids blocked by powersave chain but allowed by standby chain, verify the blocking
161 // takes higher priority.
162 testConfigurationMap.updateEntry(UID_RULES_CONFIGURATION_KEY, U32(0))
163 mockChainEnabled(ConnectivityManager.FIREWALL_CHAIN_POWERSAVE, true)
164 mockChainEnabled(ConnectivityManager.FIREWALL_CHAIN_STANDBY, true)
Junyu Laic3dc5b62023-09-06 19:10:02 +0800165 assertTrue(isUidNetworkingBlocked(TEST_UID1))
166 }
167
168 @IgnoreUpTo(VERSION_CODES.S_V2)
169 @Test
170 fun testIsUidNetworkingBlockedByDataSaver() {
171 // With everything disabled by default, verify the return value is false.
172 testConfigurationMap.updateEntry(UID_RULES_CONFIGURATION_KEY, U32(0))
173 assertFalse(isUidNetworkingBlocked(TEST_UID1, metered = true))
174
175 // Add uid1 to penalty box, verify the network is blocked for uid1, while uid2 is not
176 // affected.
177 testUidOwnerMap.updateEntry(S32(TEST_UID1), UidOwnerValue(NO_IIF, PENALTY_BOX_MATCH))
178 assertTrue(isUidNetworkingBlocked(TEST_UID1, metered = true))
179 assertFalse(isUidNetworkingBlocked(TEST_UID2, metered = true))
180
181 // Enable data saver, verify the network is blocked for uid1, uid2, but uid3 in happy box
182 // is not affected.
183 testUidOwnerMap.updateEntry(S32(TEST_UID3), UidOwnerValue(NO_IIF, HAPPY_BOX_MATCH))
184 assertTrue(isUidNetworkingBlocked(TEST_UID1, metered = true, dataSaver = true))
185 assertTrue(isUidNetworkingBlocked(TEST_UID2, metered = true, dataSaver = true))
186 assertFalse(isUidNetworkingBlocked(TEST_UID3, metered = true, dataSaver = true))
187
188 // Add uid1 to happy box as well, verify nothing is changed because penalty box has higher
189 // priority.
190 testUidOwnerMap.updateEntry(
191 S32(TEST_UID1),
192 UidOwnerValue(NO_IIF, PENALTY_BOX_MATCH or HAPPY_BOX_MATCH)
193 )
194 assertTrue(isUidNetworkingBlocked(TEST_UID1, metered = true, dataSaver = true))
195 assertTrue(isUidNetworkingBlocked(TEST_UID2, metered = true, dataSaver = true))
196 assertFalse(isUidNetworkingBlocked(TEST_UID3, metered = true, dataSaver = true))
197
198 // Enable doze mode, verify uid3 is blocked even if it is in happy box.
199 mockChainEnabled(ConnectivityManager.FIREWALL_CHAIN_DOZABLE, true)
200 assertTrue(isUidNetworkingBlocked(TEST_UID1, metered = true, dataSaver = true))
201 assertTrue(isUidNetworkingBlocked(TEST_UID2, metered = true, dataSaver = true))
202 assertTrue(isUidNetworkingBlocked(TEST_UID3, metered = true, dataSaver = true))
203
204 // Disable doze mode and data saver, only uid1 which is in penalty box is blocked.
205 mockChainEnabled(ConnectivityManager.FIREWALL_CHAIN_DOZABLE, false)
206 assertTrue(isUidNetworkingBlocked(TEST_UID1, metered = true))
207 assertFalse(isUidNetworkingBlocked(TEST_UID2, metered = true))
208 assertFalse(isUidNetworkingBlocked(TEST_UID3, metered = true))
209
210 // Make the network non-metered, nothing is blocked.
211 assertFalse(isUidNetworkingBlocked(TEST_UID1))
212 assertFalse(isUidNetworkingBlocked(TEST_UID2))
213 assertFalse(isUidNetworkingBlocked(TEST_UID3))
Junyu Laie0031522023-08-29 18:32:57 +0800214 }
Junyu Lai9e880522023-11-14 15:44:37 +0800215
Junyu Lai9e880522023-11-14 15:44:37 +0800216 @Test
217 fun testGetDataSaverEnabled() {
218 testDataSaverEnabledMap.updateEntry(DATA_SAVER_ENABLED_KEY, U8(DATA_SAVER_DISABLED))
219 assertFalse(bpfNetMapsReader.dataSaverEnabled)
220 testDataSaverEnabledMap.updateEntry(DATA_SAVER_ENABLED_KEY, U8(DATA_SAVER_ENABLED))
221 assertTrue(bpfNetMapsReader.dataSaverEnabled)
222 }
Junyu Lai626045a2023-08-28 18:49:44 +0800223}