Tell netd about local networks

This patch does the following :
• When a network is local but the system doesn't support it,
  throw in registerNetworkAgent.
• When a network is local, tell netd about it.
• Add tests for the above, as well as a test for callbacks
  being sent correctly when the caps are set and not when
  they aren't and that the new keep connected flag is
  respected.

Telling netd about the network being local has it add two
routes that do not exist if the network is not local :
• One at PRIORITY_LOCAL_NETWORK matching unmarked sockets
  and looking up the table for the interface.
20000: from all fwmark 0x0/0x10000 lookup xxxx
• One at PRIORITY_EXPLICIT_NETWORK matching explicitly on
  network 99, so that dnsmasq traffic and any OEM traffic
  marked for network 99 flowing there.
16000: from all fwmark 0x10063/0x1ffff iif lo lookup xxxx
IMPLICIT_NETWORK and EXPLICIT_NETWORK rules are installed
by connectivity service as a matter of course whether the
network is local or not.

See commit If8729fc6f3716a580c936584b851bc38000b5de5 for
implementation details of this mechanic.

There is no need to implement anything in particular for the
new connected reason, as the current implementation will
already keep it connected. A new test makes sure of that.

Test: FrameworksNetTests
      CtsNetTestCases
      FrameworksNetIntegrationTests
      NetworkStackTests
      NetworkStaticLibTests
      TetheringTests
      MtsTetheringTestLatestSdk
      TetheringIntegrationTests
Change-Id: I6fb7dfe4c232eea8cac9ac268897ddb36bb794d1
This commit is contained in:
Chalard Jean
2023-08-10 17:01:11 +09:00
parent 10353d5351
commit 774dc3cd51
3 changed files with 148 additions and 7 deletions

View File

@@ -19,6 +19,7 @@ package com.android.server;
import static android.Manifest.permission.RECEIVE_DATA_ACTIVITY_CHANGE; import static android.Manifest.permission.RECEIVE_DATA_ACTIVITY_CHANGE;
import static android.app.ActivityManager.UidFrozenStateChangedCallback.UID_FROZEN_STATE_FROZEN; import static android.app.ActivityManager.UidFrozenStateChangedCallback.UID_FROZEN_STATE_FROZEN;
import static android.content.pm.PackageManager.FEATURE_BLUETOOTH; import static android.content.pm.PackageManager.FEATURE_BLUETOOTH;
import static android.content.pm.PackageManager.FEATURE_LEANBACK;
import static android.content.pm.PackageManager.FEATURE_WATCH; import static android.content.pm.PackageManager.FEATURE_WATCH;
import static android.content.pm.PackageManager.FEATURE_WIFI; import static android.content.pm.PackageManager.FEATURE_WIFI;
import static android.content.pm.PackageManager.FEATURE_WIFI_DIRECT; import static android.content.pm.PackageManager.FEATURE_WIFI_DIRECT;
@@ -67,6 +68,7 @@ import static android.net.NetworkCapabilities.NET_CAPABILITY_CAPTIVE_PORTAL;
import static android.net.NetworkCapabilities.NET_CAPABILITY_ENTERPRISE; import static android.net.NetworkCapabilities.NET_CAPABILITY_ENTERPRISE;
import static android.net.NetworkCapabilities.NET_CAPABILITY_FOREGROUND; import static android.net.NetworkCapabilities.NET_CAPABILITY_FOREGROUND;
import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET; import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET;
import static android.net.NetworkCapabilities.NET_CAPABILITY_LOCAL_NETWORK;
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_CONGESTED; import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_CONGESTED;
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED; import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED;
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED; import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED;
@@ -5006,7 +5008,10 @@ public class ConnectivityService extends IConnectivityManager.Stub
!nai.networkAgentConfig.allowBypass /* secure */, !nai.networkAgentConfig.allowBypass /* secure */,
getVpnType(nai), nai.networkAgentConfig.excludeLocalRouteVpn); getVpnType(nai), nai.networkAgentConfig.excludeLocalRouteVpn);
} else { } else {
config = new NativeNetworkConfig(nai.network.getNetId(), NativeNetworkType.PHYSICAL, final boolean hasLocalCap =
nai.networkCapabilities.hasCapability(NET_CAPABILITY_LOCAL_NETWORK);
config = new NativeNetworkConfig(nai.network.getNetId(),
hasLocalCap ? NativeNetworkType.PHYSICAL_LOCAL : NativeNetworkType.PHYSICAL,
getNetworkPermission(nai.networkCapabilities), getNetworkPermission(nai.networkCapabilities),
false /* secure */, false /* secure */,
VpnManager.TYPE_VPN_NONE, VpnManager.TYPE_VPN_NONE,
@@ -8055,6 +8060,18 @@ public class ConnectivityService extends IConnectivityManager.Stub
return nai == getDefaultNetwork(); return nai == getDefaultNetwork();
} }
/**
* Returns whether local agents are supported on this device.
*
* Local agents are supported from U on TVs, and from V on all devices.
*/
@VisibleForTesting
public boolean areLocalAgentsSupported() {
final PackageManager pm = mContext.getPackageManager();
// Local agents are supported starting on U on TVs and on V on everything else.
return mDeps.isAtLeastV() || (mDeps.isAtLeastU() && pm.hasSystemFeature(FEATURE_LEANBACK));
}
/** /**
* Register a new agent with ConnectivityService to handle a network. * Register a new agent with ConnectivityService to handle a network.
* *
@@ -8084,6 +8101,12 @@ public class ConnectivityService extends IConnectivityManager.Stub
} else { } else {
enforceNetworkFactoryPermission(); enforceNetworkFactoryPermission();
} }
final boolean hasLocalCap =
networkCapabilities.hasCapability(NET_CAPABILITY_LOCAL_NETWORK);
if (hasLocalCap && !areLocalAgentsSupported()) {
// Before U, netd doesn't support PHYSICAL_LOCAL networks so this can't work.
throw new IllegalArgumentException("Local agents are not supported in this version");
}
final int uid = mDeps.getCallingUid(); final int uid = mDeps.getCallingUid();
final long token = Binder.clearCallingIdentity(); final long token = Binder.clearCallingIdentity();
@@ -9189,7 +9212,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
// are Type.LISTEN, but should not have NetworkCallbacks invoked. // are Type.LISTEN, but should not have NetworkCallbacks invoked.
return; return;
} }
Bundle bundle = new Bundle(); final Bundle bundle = new Bundle();
// TODO b/177608132: make sure callbacks are indexed by NRIs and not NetworkRequest objects. // TODO b/177608132: make sure callbacks are indexed by NRIs and not NetworkRequest objects.
// TODO: check if defensive copies of data is needed. // TODO: check if defensive copies of data is needed.
final NetworkRequest nrForCallback = nri.getNetworkRequestForCallback(); final NetworkRequest nrForCallback = nri.getNetworkRequestForCallback();

View File

@@ -64,7 +64,6 @@ import android.util.SparseArray;
import com.android.internal.annotations.VisibleForTesting; import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.IndentingPrintWriter; import com.android.internal.util.IndentingPrintWriter;
import com.android.internal.util.WakeupMessage; import com.android.internal.util.WakeupMessage;
import com.android.modules.utils.build.SdkLevel;
import com.android.server.ConnectivityService; import com.android.server.ConnectivityService;
import java.io.PrintWriter; import java.io.PrintWriter;
@@ -470,8 +469,8 @@ public class NetworkAgentInfo implements NetworkRanker.Scoreable {
+ networkCapabilities.getOwnerUid() + " to " + nc.getOwnerUid()); + networkCapabilities.getOwnerUid() + " to " + nc.getOwnerUid());
nc.setOwnerUid(networkCapabilities.getOwnerUid()); nc.setOwnerUid(networkCapabilities.getOwnerUid());
} }
restrictCapabilitiesFromNetworkAgent( restrictCapabilitiesFromNetworkAgent(nc, creatorUid, mHasAutomotiveFeature,
nc, creatorUid, mHasAutomotiveFeature, carrierPrivilegeAuthenticator); mConnServiceDeps, carrierPrivilegeAuthenticator);
return nc; return nc;
} }
@@ -601,6 +600,7 @@ public class NetworkAgentInfo implements NetworkRanker.Scoreable {
private static final String TAG = ConnectivityService.class.getSimpleName(); private static final String TAG = ConnectivityService.class.getSimpleName();
private static final boolean VDBG = false; private static final boolean VDBG = false;
private final ConnectivityService mConnService; private final ConnectivityService mConnService;
private final ConnectivityService.Dependencies mConnServiceDeps;
private final Context mContext; private final Context mContext;
private final Handler mHandler; private final Handler mHandler;
private final QosCallbackTracker mQosCallbackTracker; private final QosCallbackTracker mQosCallbackTracker;
@@ -628,6 +628,7 @@ public class NetworkAgentInfo implements NetworkRanker.Scoreable {
networkCapabilities = nc; networkCapabilities = nc;
networkAgentConfig = config; networkAgentConfig = config;
mConnService = connService; mConnService = connService;
mConnServiceDeps = deps;
setScore(score); // uses members connService, networkCapabilities and networkAgentConfig setScore(score); // uses members connService, networkCapabilities and networkAgentConfig
clatd = new Nat464Xlat(this, netd, dnsResolver, deps); clatd = new Nat464Xlat(this, netd, dnsResolver, deps);
mContext = context; mContext = context;
@@ -1518,23 +1519,26 @@ public class NetworkAgentInfo implements NetworkRanker.Scoreable {
*/ */
public static void restrictCapabilitiesFromNetworkAgent(@NonNull final NetworkCapabilities nc, public static void restrictCapabilitiesFromNetworkAgent(@NonNull final NetworkCapabilities nc,
final int creatorUid, final boolean hasAutomotiveFeature, final int creatorUid, final boolean hasAutomotiveFeature,
@NonNull final ConnectivityService.Dependencies deps,
@Nullable final CarrierPrivilegeAuthenticator authenticator) { @Nullable final CarrierPrivilegeAuthenticator authenticator) {
if (nc.hasTransport(TRANSPORT_TEST)) { if (nc.hasTransport(TRANSPORT_TEST)) {
nc.restrictCapabilitiesForTestNetwork(creatorUid); nc.restrictCapabilitiesForTestNetwork(creatorUid);
} }
if (!areAllowedUidsAcceptableFromNetworkAgent(nc, hasAutomotiveFeature, authenticator)) { if (!areAllowedUidsAcceptableFromNetworkAgent(
nc, hasAutomotiveFeature, deps, authenticator)) {
nc.setAllowedUids(new ArraySet<>()); nc.setAllowedUids(new ArraySet<>());
} }
} }
private static boolean areAllowedUidsAcceptableFromNetworkAgent( private static boolean areAllowedUidsAcceptableFromNetworkAgent(
@NonNull final NetworkCapabilities nc, final boolean hasAutomotiveFeature, @NonNull final NetworkCapabilities nc, final boolean hasAutomotiveFeature,
@NonNull final ConnectivityService.Dependencies deps,
@Nullable final CarrierPrivilegeAuthenticator carrierPrivilegeAuthenticator) { @Nullable final CarrierPrivilegeAuthenticator carrierPrivilegeAuthenticator) {
// NCs without access UIDs are fine. // NCs without access UIDs are fine.
if (!nc.hasAllowedUids()) return true; if (!nc.hasAllowedUids()) return true;
// S and below must never accept access UIDs, even if an agent sends them, because netd // S and below must never accept access UIDs, even if an agent sends them, because netd
// didn't support the required feature in S. // didn't support the required feature in S.
if (!SdkLevel.isAtLeastT()) return false; if (!deps.isAtLeastT()) return false;
// On a non-restricted network, access UIDs make no sense // On a non-restricted network, access UIDs make no sense
if (nc.hasCapability(NET_CAPABILITY_NOT_RESTRICTED)) return false; if (nc.hasCapability(NET_CAPABILITY_NOT_RESTRICTED)) return false;

View File

@@ -0,0 +1,114 @@
/*
* Copyright (C) 2023 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.content.pm.PackageManager.FEATURE_LEANBACK
import android.net.INetd
import android.net.NativeNetworkConfig
import android.net.NativeNetworkType
import android.net.NetworkCapabilities
import android.net.NetworkCapabilities.NET_CAPABILITY_LOCAL_NETWORK
import android.net.NetworkRequest
import android.net.NetworkScore
import android.net.NetworkScore.KEEP_CONNECTED_FOR_TEST
import android.net.VpnManager
import android.os.Build
import androidx.test.filters.SmallTest
import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo
import com.android.testutils.DevSdkIgnoreRunner
import com.android.testutils.RecorderCallback.CallbackEntry.Available
import com.android.testutils.TestableNetworkCallback
import org.junit.Assert.assertEquals
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.Parameterized
import org.mockito.Mockito.doReturn
import org.mockito.Mockito.inOrder
import org.mockito.Mockito.never
import org.mockito.Mockito.timeout
import kotlin.test.assertFailsWith
private const val TIMEOUT_MS = 2_000L
private const val NO_CALLBACK_TIMEOUT_MS = 200L
private fun keepConnectedScore() =
FromS(NetworkScore.Builder().setKeepConnectedReason(KEEP_CONNECTED_FOR_TEST).build())
@RunWith(DevSdkIgnoreRunner::class)
@SmallTest
@IgnoreUpTo(Build.VERSION_CODES.R)
class CSLocalAgentCreationTests(
private val sdkLevel: Int,
private val isTv: Boolean,
private val addLocalNetCapToRequest: Boolean
) : CSTest() {
companion object {
@JvmStatic
@Parameterized.Parameters
fun arguments() = listOf(
arrayOf(VERSION_V, false /* isTv */, true /* addLocalNetCapToRequest */),
arrayOf(VERSION_V, false /* isTv */, false /* addLocalNetCapToRequest */),
arrayOf(VERSION_V, true /* isTv */, true /* addLocalNetCapToRequest */),
arrayOf(VERSION_V, true /* isTv */, false /* addLocalNetCapToRequest */),
arrayOf(VERSION_U, false /* isTv */, true /* addLocalNetCapToRequest */),
arrayOf(VERSION_U, false /* isTv */, false /* addLocalNetCapToRequest */),
arrayOf(VERSION_U, true /* isTv */, true /* addLocalNetCapToRequest */),
arrayOf(VERSION_U, true /* isTv */, false /* addLocalNetCapToRequest */),
arrayOf(VERSION_T, false /* isTv */, false /* addLocalNetCapToRequest */),
arrayOf(VERSION_T, true /* isTv */, false /* addLocalNetCapToRequest */),
)
}
private fun makeNativeNetworkConfigLocal(netId: Int, permission: Int) =
NativeNetworkConfig(netId, NativeNetworkType.PHYSICAL_LOCAL, permission,
false /* secure */, VpnManager.TYPE_VPN_NONE, false /* excludeLocalRoutes */)
@Test
fun testLocalAgents() {
val netdInOrder = inOrder(netd)
deps.setBuildSdk(sdkLevel)
doReturn(isTv).`when`(packageManager).hasSystemFeature(FEATURE_LEANBACK)
val allNetworksCb = TestableNetworkCallback()
val request = NetworkRequest.Builder()
if (addLocalNetCapToRequest) {
request.addCapability(NET_CAPABILITY_LOCAL_NETWORK)
}
cm.registerNetworkCallback(request.build(), allNetworksCb)
val ncTemplate = NetworkCapabilities.Builder().run {
addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
addCapability(NET_CAPABILITY_LOCAL_NETWORK)
}.build()
val localAgent = if (sdkLevel >= VERSION_V || sdkLevel == VERSION_U && isTv) {
Agent(nc = ncTemplate, score = keepConnectedScore())
} else {
assertFailsWith<IllegalArgumentException> { Agent(nc = ncTemplate) }
netdInOrder.verify(netd, never()).networkCreate(any())
return
}
localAgent.connect()
netdInOrder.verify(netd).networkCreate(
makeNativeNetworkConfigLocal(localAgent.network.netId, INetd.PERMISSION_NONE))
if (addLocalNetCapToRequest) {
assertEquals(localAgent.network, allNetworksCb.expect<Available>().network)
} else {
allNetworksCb.assertNoCallback(NO_CALLBACK_TIMEOUT_MS)
}
cm.unregisterNetworkCallback(allNetworksCb)
localAgent.disconnect()
netdInOrder.verify(netd, timeout(TIMEOUT_MS)).networkDestroy(localAgent.network.netId)
}
}