[BR02] Implement isUidNetworkingBlocked

This is needed for data stall detection mechanism in NetworkStack
to get the information about whether the network is blocked for
a given uid and conditions. Because the API will be called
frequently from NetworkStack to resolve all status for all uids
on the device, the API cannot call into the service which
creates IPC. Instead, the API need to directly access bpf maps
in the user process to retrieve the status. In this case the
user process is the network stack, the access control is provided
by linux file permission and selinux.

Test: atest FrameworksNetTests:android.net.connectivity.android.net.BpfNetMapsReaderTest
Test: atest FrameworksNetTests:android.net.connectivity.android.net.ConnectivityManagerTest
NO_IFTTT=Refactor only change for firewall chains definitions
Bug: 297836825

Change-Id: Iaf983b71ec98cbfe5152dcfade8a3120f938f135
This commit is contained in:
Junyu Lai
2023-08-29 18:32:57 +08:00
parent 792a7cd1a1
commit e003152e2c
9 changed files with 232 additions and 60 deletions

View File

@@ -16,23 +16,31 @@
package android.net
import android.net.BpfNetMapsConstants.DOZABLE_MATCH
import android.net.BpfNetMapsConstants.STANDBY_MATCH
import android.net.BpfNetMapsConstants.UID_RULES_CONFIGURATION_KEY
import android.net.BpfNetMapsUtils.getMatchByFirewallChain
import android.os.Build
import android.os.Build.VERSION_CODES
import com.android.net.module.util.IBpfMap
import com.android.net.module.util.Struct.S32
import com.android.net.module.util.Struct.U32
import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo
import com.android.testutils.DevSdkIgnoreRunner
import com.android.testutils.TestBpfMap
import java.lang.reflect.Modifier
import kotlin.test.assertEquals
import kotlin.test.assertFalse
import kotlin.test.assertTrue
import org.junit.Test
import org.junit.runner.RunWith
private const val TEST_UID1 = 1234
private const val TEST_UID2 = TEST_UID1 + 1
private const val NO_IIF = 0
// pre-T devices does not support Bpf.
@RunWith(DevSdkIgnoreRunner::class)
@IgnoreUpTo(Build.VERSION_CODES.S_V2)
@IgnoreUpTo(VERSION_CODES.S_V2)
class BpfNetMapsReaderTest {
private val testConfigurationMap: IBpfMap<S32, U32> = TestBpfMap()
private val testUidOwnerMap: IBpfMap<S32, UidOwnerValue> = TestBpfMap()
@@ -66,4 +74,75 @@ class BpfNetMapsReaderTest {
doTestIsChainEnabled(ConnectivityManager.FIREWALL_CHAIN_RESTRICTED)
doTestIsChainEnabled(ConnectivityManager.FIREWALL_CHAIN_LOW_POWER_STANDBY)
}
@Test
fun testFirewallChainList() {
// Verify that when a firewall chain constant is added, it should also be included in
// firewall chain list.
val declaredChains = ConnectivityManager::class.java.declaredFields.filter {
Modifier.isStatic(it.modifiers) && it.name.startsWith("FIREWALL_CHAIN_")
}
// Verify the size matches, this also verifies no common item in allow and deny chains.
assertEquals(BpfNetMapsConstants.ALLOW_CHAINS.size +
BpfNetMapsConstants.DENY_CHAINS.size, declaredChains.size)
declaredChains.forEach {
assertTrue(BpfNetMapsConstants.ALLOW_CHAINS.contains(it.get(null)) ||
BpfNetMapsConstants.DENY_CHAINS.contains(it.get(null)))
}
}
private fun mockChainEnabled(chain: Int, enabled: Boolean) {
val config = testConfigurationMap.getValue(UID_RULES_CONFIGURATION_KEY).`val`
val newConfig = if (enabled) {
config or getMatchByFirewallChain(chain)
} else {
config and getMatchByFirewallChain(chain).inv()
}
testConfigurationMap.updateEntry(UID_RULES_CONFIGURATION_KEY, U32(newConfig))
}
@Test
fun testIsUidNetworkingBlockedByFirewallChains_allowChain() {
// With everything disabled by default, verify the return value is false.
testConfigurationMap.updateEntry(UID_RULES_CONFIGURATION_KEY, U32(0))
assertFalse(bpfNetMapsReader.isUidBlockedByFirewallChains(TEST_UID1))
// Enable dozable chain but does not provide allowed list. Verify the network is blocked
// for all uids.
mockChainEnabled(ConnectivityManager.FIREWALL_CHAIN_DOZABLE, true)
assertTrue(bpfNetMapsReader.isUidBlockedByFirewallChains(TEST_UID1))
assertTrue(bpfNetMapsReader.isUidBlockedByFirewallChains(TEST_UID2))
// Add uid1 to dozable allowed list. Verify the network is not blocked for uid1, while
// uid2 is blocked.
testUidOwnerMap.updateEntry(S32(TEST_UID1), UidOwnerValue(NO_IIF, DOZABLE_MATCH))
assertFalse(bpfNetMapsReader.isUidBlockedByFirewallChains(TEST_UID1))
assertTrue(bpfNetMapsReader.isUidBlockedByFirewallChains(TEST_UID2))
}
@Test
fun testIsUidNetworkingBlockedByFirewallChains_denyChain() {
// Enable standby chain but does not provide denied list. Verify the network is allowed
// for all uids.
testConfigurationMap.updateEntry(UID_RULES_CONFIGURATION_KEY, U32(0))
mockChainEnabled(ConnectivityManager.FIREWALL_CHAIN_STANDBY, true)
assertFalse(bpfNetMapsReader.isUidBlockedByFirewallChains(TEST_UID1))
assertFalse(bpfNetMapsReader.isUidBlockedByFirewallChains(TEST_UID2))
// Add uid1 to standby allowed list. Verify the network is blocked for uid1, while
// uid2 is not blocked.
testUidOwnerMap.updateEntry(S32(TEST_UID1), UidOwnerValue(NO_IIF, STANDBY_MATCH))
assertTrue(bpfNetMapsReader.isUidBlockedByFirewallChains(TEST_UID1))
assertFalse(bpfNetMapsReader.isUidBlockedByFirewallChains(TEST_UID2))
}
@Test
fun testIsUidNetworkingBlockedByFirewallChains_blockedWithAllowed() {
// Uids blocked by powersave chain but allowed by standby chain, verify the blocking
// takes higher priority.
testConfigurationMap.updateEntry(UID_RULES_CONFIGURATION_KEY, U32(0))
mockChainEnabled(ConnectivityManager.FIREWALL_CHAIN_POWERSAVE, true)
mockChainEnabled(ConnectivityManager.FIREWALL_CHAIN_STANDBY, true)
assertTrue(bpfNetMapsReader.isUidBlockedByFirewallChains(TEST_UID1))
}
}