diff --git a/service/src/com/android/server/ConnectivityService.java b/service/src/com/android/server/ConnectivityService.java index 005d6177e5..b1ae019bbf 100755 --- a/service/src/com/android/server/ConnectivityService.java +++ b/service/src/com/android/server/ConnectivityService.java @@ -478,6 +478,7 @@ public class ConnectivityService extends IConnectivityManager.Stub private volatile boolean mLockdownEnabled; private final boolean mRequestRestrictedWifiEnabled; + private final boolean mBackgroundFirewallChainEnabled; /** * Stale copy of uid blocked reasons provided by NPMS. As long as they are accessed only in @@ -1798,6 +1799,8 @@ public class ConnectivityService extends IConnectivityManager.Stub mLocationPermissionChecker = mDeps.makeLocationPermissionChecker(mContext); mRequestRestrictedWifiEnabled = mDeps.isAtLeastU() && mDeps.isFeatureEnabled(context, REQUEST_RESTRICTED_WIFI); + mBackgroundFirewallChainEnabled = mDeps.isAtLeastV() && mDeps.isFeatureNotChickenedOut( + context, ConnectivityFlags.BACKGROUND_FIREWALL_CHAIN); mCarrierPrivilegeAuthenticator = mDeps.makeCarrierPrivilegeAuthenticator( mContext, mTelephonyManager, mRequestRestrictedWifiEnabled, this::handleUidCarrierPrivilegesLost, mHandler); @@ -4152,6 +4155,9 @@ public class ConnectivityService extends IConnectivityManager.Stub pw.println(); pw.println("Multicast routing supported: " + (mMulticastRoutingCoordinatorService != null)); + + pw.println(); + pw.println("Background firewall chain enabled: " + mBackgroundFirewallChainEnabled); } private void dumpNetworks(IndentingPrintWriter pw) { @@ -13521,6 +13527,12 @@ public class ConnectivityService extends IConnectivityManager.Stub public void setUidFirewallRule(final int chain, final int uid, final int rule) { enforceNetworkStackOrSettingsPermission(); + if (chain == FIREWALL_CHAIN_BACKGROUND && !mBackgroundFirewallChainEnabled) { + Log.i(TAG, "Ignoring operation setUidFirewallRule on the background chain because the" + + " feature is disabled."); + return; + } + // There are only two type of firewall rule: FIREWALL_RULE_ALLOW or FIREWALL_RULE_DENY int firewallRule = getFirewallRuleType(chain, rule); @@ -13593,6 +13605,12 @@ public class ConnectivityService extends IConnectivityManager.Stub public void setFirewallChainEnabled(final int chain, final boolean enable) { enforceNetworkStackOrSettingsPermission(); + if (chain == FIREWALL_CHAIN_BACKGROUND && !mBackgroundFirewallChainEnabled) { + Log.i(TAG, "Ignoring operation setFirewallChainEnabled on the background chain because" + + " the feature is disabled."); + return; + } + try { mBpfNetMaps.setChildChain(chain, enable); } catch (ServiceSpecificException e) { @@ -13619,6 +13637,12 @@ public class ConnectivityService extends IConnectivityManager.Stub public void replaceFirewallChain(final int chain, final int[] uids) { enforceNetworkStackOrSettingsPermission(); + if (chain == FIREWALL_CHAIN_BACKGROUND && !mBackgroundFirewallChainEnabled) { + Log.i(TAG, "Ignoring operation replaceFirewallChain on the background chain because" + + " the feature is disabled."); + return; + } + mBpfNetMaps.replaceUidChain(chain, uids); } diff --git a/service/src/com/android/server/connectivity/ConnectivityFlags.java b/service/src/com/android/server/connectivity/ConnectivityFlags.java index a55c683bc0..176307d729 100644 --- a/service/src/com/android/server/connectivity/ConnectivityFlags.java +++ b/service/src/com/android/server/connectivity/ConnectivityFlags.java @@ -42,6 +42,8 @@ public final class ConnectivityFlags { public static final String INGRESS_TO_VPN_ADDRESS_FILTERING = "ingress_to_vpn_address_filtering"; + public static final String BACKGROUND_FIREWALL_CHAIN = "background_firewall_chain"; + private boolean mNoRematchAllRequestsOnRegister; /** diff --git a/tests/unit/java/com/android/server/ConnectivityServiceTest.java b/tests/unit/java/com/android/server/ConnectivityServiceTest.java index 17c59017ce..7822fe0a46 100755 --- a/tests/unit/java/com/android/server/ConnectivityServiceTest.java +++ b/tests/unit/java/com/android/server/ConnectivityServiceTest.java @@ -172,6 +172,7 @@ import static com.android.server.ConnectivityService.makeNflogPrefix; import static com.android.server.ConnectivityServiceTestUtils.transportToLegacyType; import static com.android.server.NetworkAgentWrapper.CallbackType.OnQosCallbackRegister; import static com.android.server.NetworkAgentWrapper.CallbackType.OnQosCallbackUnregister; +import static com.android.server.connectivity.ConnectivityFlags.BACKGROUND_FIREWALL_CHAIN; import static com.android.server.connectivity.ConnectivityFlags.INGRESS_TO_VPN_ADDRESS_FILTERING; import static com.android.testutils.Cleanup.testAndCleanup; import static com.android.testutils.ConcurrentUtils.await; @@ -391,6 +392,7 @@ import com.android.internal.net.VpnConfig; import com.android.internal.util.WakeupMessage; import com.android.internal.util.test.BroadcastInterceptingContext; import com.android.internal.util.test.FakeSettingsProvider; +import com.android.modules.utils.build.SdkLevel; import com.android.net.module.util.ArrayTrackRecord; import com.android.net.module.util.BaseNetdUnsolicitedEventListener; import com.android.net.module.util.CollectionUtils; @@ -2171,6 +2173,8 @@ public class ConnectivityServiceTest { return true; case INGRESS_TO_VPN_ADDRESS_FILTERING: return true; + case BACKGROUND_FIREWALL_CHAIN: + return true; default: return super.isFeatureNotChickenedOut(context, name); } @@ -10488,7 +10492,10 @@ public class ConnectivityServiceTest { doTestSetUidFirewallRule(FIREWALL_CHAIN_POWERSAVE, FIREWALL_RULE_DENY); doTestSetUidFirewallRule(FIREWALL_CHAIN_RESTRICTED, FIREWALL_RULE_DENY); doTestSetUidFirewallRule(FIREWALL_CHAIN_LOW_POWER_STANDBY, FIREWALL_RULE_DENY); - doTestSetUidFirewallRule(FIREWALL_CHAIN_BACKGROUND, FIREWALL_RULE_DENY); + if (SdkLevel.isAtLeastV()) { + // FIREWALL_CHAIN_BACKGROUND is only available on V+. + doTestSetUidFirewallRule(FIREWALL_CHAIN_BACKGROUND, FIREWALL_RULE_DENY); + } doTestSetUidFirewallRule(FIREWALL_CHAIN_OEM_DENY_1, FIREWALL_RULE_ALLOW); doTestSetUidFirewallRule(FIREWALL_CHAIN_OEM_DENY_2, FIREWALL_RULE_ALLOW); doTestSetUidFirewallRule(FIREWALL_CHAIN_OEM_DENY_3, FIREWALL_RULE_ALLOW); @@ -10496,16 +10503,19 @@ public class ConnectivityServiceTest { @Test @IgnoreUpTo(SC_V2) public void testSetFirewallChainEnabled() throws Exception { - final List firewallChains = Arrays.asList( + final List firewallChains = new ArrayList<>(Arrays.asList( FIREWALL_CHAIN_DOZABLE, FIREWALL_CHAIN_STANDBY, FIREWALL_CHAIN_POWERSAVE, FIREWALL_CHAIN_RESTRICTED, FIREWALL_CHAIN_LOW_POWER_STANDBY, - FIREWALL_CHAIN_BACKGROUND, FIREWALL_CHAIN_OEM_DENY_1, FIREWALL_CHAIN_OEM_DENY_2, - FIREWALL_CHAIN_OEM_DENY_3); + FIREWALL_CHAIN_OEM_DENY_3)); + if (SdkLevel.isAtLeastV()) { + // FIREWALL_CHAIN_BACKGROUND is only available on V+. + firewallChains.add(FIREWALL_CHAIN_BACKGROUND); + } for (final int chain: firewallChains) { mCm.setFirewallChainEnabled(chain, true /* enabled */); verify(mBpfNetMaps).setChildChain(chain, true /* enable */); @@ -10552,7 +10562,10 @@ public class ConnectivityServiceTest { doTestSetFirewallChainEnabledCloseSocket(FIREWALL_CHAIN_POWERSAVE, allowlist); doTestSetFirewallChainEnabledCloseSocket(FIREWALL_CHAIN_RESTRICTED, allowlist); doTestSetFirewallChainEnabledCloseSocket(FIREWALL_CHAIN_LOW_POWER_STANDBY, allowlist); - doTestSetFirewallChainEnabledCloseSocket(FIREWALL_CHAIN_BACKGROUND, allowlist); + if (SdkLevel.isAtLeastV()) { + // FIREWALL_CHAIN_BACKGROUND is only available on V+. + doTestSetFirewallChainEnabledCloseSocket(FIREWALL_CHAIN_BACKGROUND, allowlist); + } doTestSetFirewallChainEnabledCloseSocket(FIREWALL_CHAIN_STANDBY, denylist); doTestSetFirewallChainEnabledCloseSocket(FIREWALL_CHAIN_OEM_DENY_1, denylist); @@ -10574,7 +10587,10 @@ public class ConnectivityServiceTest { doTestReplaceFirewallChain(FIREWALL_CHAIN_POWERSAVE); doTestReplaceFirewallChain(FIREWALL_CHAIN_RESTRICTED); doTestReplaceFirewallChain(FIREWALL_CHAIN_LOW_POWER_STANDBY); - doTestReplaceFirewallChain(FIREWALL_CHAIN_BACKGROUND); + if (SdkLevel.isAtLeastV()) { + // FIREWALL_CHAIN_BACKGROUND is only available on V+. + doTestReplaceFirewallChain(FIREWALL_CHAIN_BACKGROUND); + } doTestReplaceFirewallChain(FIREWALL_CHAIN_OEM_DENY_1); doTestReplaceFirewallChain(FIREWALL_CHAIN_OEM_DENY_2); doTestReplaceFirewallChain(FIREWALL_CHAIN_OEM_DENY_3); diff --git a/tests/unit/java/com/android/server/connectivityservice/CSFirewallChainTest.kt b/tests/unit/java/com/android/server/connectivityservice/CSFirewallChainTest.kt new file mode 100644 index 0000000000..16de4dadea --- /dev/null +++ b/tests/unit/java/com/android/server/connectivityservice/CSFirewallChainTest.kt @@ -0,0 +1,157 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server + +import android.net.ConnectivityManager +import android.os.Build +import androidx.test.filters.SmallTest +import com.android.server.connectivity.ConnectivityFlags.BACKGROUND_FIREWALL_CHAIN +import com.android.testutils.DevSdkIgnoreRule +import com.android.testutils.DevSdkIgnoreRule.IgnoreAfter +import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo +import com.android.testutils.DevSdkIgnoreRunner +import org.junit.Rule +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.ArgumentMatchers.anyBoolean +import org.mockito.ArgumentMatchers.anyInt +import org.mockito.Mockito.any +import org.mockito.Mockito.clearInvocations +import org.mockito.Mockito.never +import org.mockito.Mockito.verify + +@RunWith(DevSdkIgnoreRunner::class) +@SmallTest +@IgnoreUpTo(Build.VERSION_CODES.S_V2) +class CSFirewallChainTest : CSTest() { + @get:Rule + val ignoreRule = DevSdkIgnoreRule() + + // Tests for setFirewallChainEnabled on FIREWALL_CHAIN_BACKGROUND + @Test + @FeatureFlags(flags = [Flag(BACKGROUND_FIREWALL_CHAIN, false)]) + fun setFirewallChainEnabled_backgroundChainDisabled() { + verifySetFirewallChainEnabledOnBackgroundDoesNothing() + } + + @Test + @FeatureFlags(flags = [Flag(BACKGROUND_FIREWALL_CHAIN, true)]) + @IgnoreUpTo(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) + fun setFirewallChainEnabled_backgroundChainEnabled_afterU() { + cm.setFirewallChainEnabled(ConnectivityManager.FIREWALL_CHAIN_BACKGROUND, true) + verify(bpfNetMaps).setChildChain(ConnectivityManager.FIREWALL_CHAIN_BACKGROUND, true) + + clearInvocations(bpfNetMaps) + + cm.setFirewallChainEnabled(ConnectivityManager.FIREWALL_CHAIN_BACKGROUND, false) + verify(bpfNetMaps).setChildChain(ConnectivityManager.FIREWALL_CHAIN_BACKGROUND, false) + } + + @Test + @FeatureFlags(flags = [Flag(BACKGROUND_FIREWALL_CHAIN, true)]) + @IgnoreAfter(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) + fun setFirewallChainEnabled_backgroundChainEnabled_uptoU() { + verifySetFirewallChainEnabledOnBackgroundDoesNothing() + } + + private fun verifySetFirewallChainEnabledOnBackgroundDoesNothing() { + cm.setFirewallChainEnabled(ConnectivityManager.FIREWALL_CHAIN_BACKGROUND, true) + verify(bpfNetMaps, never()).setChildChain(anyInt(), anyBoolean()) + + cm.setFirewallChainEnabled(ConnectivityManager.FIREWALL_CHAIN_BACKGROUND, false) + verify(bpfNetMaps, never()).setChildChain(anyInt(), anyBoolean()) + } + + // Tests for replaceFirewallChain on FIREWALL_CHAIN_BACKGROUND + @Test + @FeatureFlags(flags = [Flag(BACKGROUND_FIREWALL_CHAIN, false)]) + fun replaceFirewallChain_backgroundChainDisabled() { + verifyReplaceFirewallChainOnBackgroundDoesNothing() + } + + @Test + @FeatureFlags(flags = [Flag(BACKGROUND_FIREWALL_CHAIN, true)]) + @IgnoreUpTo(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) + fun replaceFirewallChain_backgroundChainEnabled_afterU() { + val uids = intArrayOf(53, 42, 79) + cm.replaceFirewallChain(ConnectivityManager.FIREWALL_CHAIN_BACKGROUND, uids) + verify(bpfNetMaps).replaceUidChain(ConnectivityManager.FIREWALL_CHAIN_BACKGROUND, uids) + } + + @Test + @FeatureFlags(flags = [Flag(BACKGROUND_FIREWALL_CHAIN, true)]) + @IgnoreAfter(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) + fun replaceFirewallChain_backgroundChainEnabled_uptoU() { + verifyReplaceFirewallChainOnBackgroundDoesNothing() + } + + private fun verifyReplaceFirewallChainOnBackgroundDoesNothing() { + val uids = intArrayOf(53, 42, 79) + cm.replaceFirewallChain(ConnectivityManager.FIREWALL_CHAIN_BACKGROUND, uids) + verify(bpfNetMaps, never()).replaceUidChain(anyInt(), any(IntArray::class.java)) + } + + // Tests for setUidFirewallRule on FIREWALL_CHAIN_BACKGROUND + @Test + @FeatureFlags(flags = [Flag(BACKGROUND_FIREWALL_CHAIN, false)]) + fun setUidFirewallRule_backgroundChainDisabled() { + verifySetUidFirewallRuleOnBackgroundDoesNothing() + } + + @Test + @FeatureFlags(flags = [Flag(BACKGROUND_FIREWALL_CHAIN, true)]) + @IgnoreUpTo(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) + fun setUidFirewallRule_backgroundChainEnabled_afterU() { + val uid = 2345 + + cm.setUidFirewallRule(ConnectivityManager.FIREWALL_CHAIN_BACKGROUND, uid, + ConnectivityManager.FIREWALL_RULE_DEFAULT) + verify(bpfNetMaps).setUidRule(ConnectivityManager.FIREWALL_CHAIN_BACKGROUND, uid, + ConnectivityManager.FIREWALL_RULE_DENY) + + clearInvocations(bpfNetMaps) + + cm.setUidFirewallRule(ConnectivityManager.FIREWALL_CHAIN_BACKGROUND, uid, + ConnectivityManager.FIREWALL_RULE_DENY) + verify(bpfNetMaps).setUidRule(ConnectivityManager.FIREWALL_CHAIN_BACKGROUND, uid, + ConnectivityManager.FIREWALL_RULE_DENY) + + clearInvocations(bpfNetMaps) + + cm.setUidFirewallRule(ConnectivityManager.FIREWALL_CHAIN_BACKGROUND, uid, + ConnectivityManager.FIREWALL_RULE_ALLOW) + verify(bpfNetMaps).setUidRule(ConnectivityManager.FIREWALL_CHAIN_BACKGROUND, uid, + ConnectivityManager.FIREWALL_RULE_ALLOW) + } + + @Test + @FeatureFlags(flags = [Flag(BACKGROUND_FIREWALL_CHAIN, true)]) + @IgnoreAfter(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) + fun setUidFirewallRule_backgroundChainEnabled_uptoU() { + verifySetUidFirewallRuleOnBackgroundDoesNothing() + } + + private fun verifySetUidFirewallRuleOnBackgroundDoesNothing() { + val uid = 2345 + + listOf(ConnectivityManager.FIREWALL_RULE_DEFAULT, ConnectivityManager.FIREWALL_RULE_ALLOW, + ConnectivityManager.FIREWALL_RULE_DENY).forEach { rule -> + cm.setUidFirewallRule(ConnectivityManager.FIREWALL_CHAIN_BACKGROUND, uid, rule) + verify(bpfNetMaps, never()).setUidRule(anyInt(), anyInt(), anyInt()) + } + } +} diff --git a/tests/unit/java/com/android/server/connectivityservice/base/CSTest.kt b/tests/unit/java/com/android/server/connectivityservice/base/CSTest.kt index bd26c630a6..3b06ad0038 100644 --- a/tests/unit/java/com/android/server/connectivityservice/base/CSTest.kt +++ b/tests/unit/java/com/android/server/connectivityservice/base/CSTest.kt @@ -161,6 +161,7 @@ open class CSTest { it[ConnectivityService.ALLOW_SYSUI_CONNECTIVITY_REPORTS] = true it[ConnectivityService.ALLOW_SATALLITE_NETWORK_FALLBACK] = true it[ConnectivityFlags.INGRESS_TO_VPN_ADDRESS_FILTERING] = true + it[ConnectivityFlags.BACKGROUND_FIREWALL_CHAIN] = true } fun setFeatureEnabled(flag: String, enabled: Boolean) = enabledFeatures.set(flag, enabled)