Snap for 14099086 from a22d4e1061 to 25Q4-release
Change-Id: I40687ca1e6a1ea2951a6d377e9225e2cf1d1a690
This commit is contained in:
commit
98c16d1361
34 changed files with 945 additions and 281 deletions
|
|
@ -22,7 +22,10 @@ package {
|
|||
java_defaults {
|
||||
name: "TetheringApiLevel",
|
||||
sdk_version: "module_current",
|
||||
min_sdk_version: "30",
|
||||
min_sdk_version: select(release_flag("RELEASE_DEPRECATE_MAINLINE_R_SUPPORT"), {
|
||||
true: "31",
|
||||
default: "30",
|
||||
}),
|
||||
}
|
||||
|
||||
java_defaults {
|
||||
|
|
|
|||
|
|
@ -53,5 +53,8 @@ android_test {
|
|||
"TetheringApiCurrentLib",
|
||||
],
|
||||
compile_multilib: "both",
|
||||
min_sdk_version: "30",
|
||||
min_sdk_version: select(release_flag("RELEASE_DEPRECATE_MAINLINE_R_SUPPORT"), {
|
||||
true: "31",
|
||||
default: "30",
|
||||
}),
|
||||
}
|
||||
|
|
|
|||
|
|
@ -45,7 +45,10 @@ java_library {
|
|||
|
||||
java_defaults {
|
||||
name: "TetheringTestsDefaults",
|
||||
min_sdk_version: "30",
|
||||
min_sdk_version: select(release_flag("RELEASE_DEPRECATE_MAINLINE_R_SUPPORT"), {
|
||||
true: "31",
|
||||
default: "30",
|
||||
}),
|
||||
srcs: [
|
||||
"src/**/*.java",
|
||||
"src/**/*.kt",
|
||||
|
|
|
|||
|
|
@ -834,6 +834,7 @@ public class VcnGatewayConnection extends StateMachine {
|
|||
mDeps.newUnderlyingNetworkController(
|
||||
mVcnContext,
|
||||
mConnectionConfig,
|
||||
mId,
|
||||
subscriptionGroup,
|
||||
mLastSnapshot,
|
||||
mUnderlyingNetworkControllerCallback);
|
||||
|
|
@ -2815,11 +2816,17 @@ public class VcnGatewayConnection extends StateMachine {
|
|||
public UnderlyingNetworkController newUnderlyingNetworkController(
|
||||
VcnContext vcnContext,
|
||||
VcnGatewayConnectionConfig connectionConfig,
|
||||
int gatewayConnectionId,
|
||||
ParcelUuid subscriptionGroup,
|
||||
TelephonySubscriptionSnapshot snapshot,
|
||||
UnderlyingNetworkControllerCallback callback) {
|
||||
return new UnderlyingNetworkController(
|
||||
vcnContext, connectionConfig, subscriptionGroup, snapshot, callback);
|
||||
vcnContext,
|
||||
connectionConfig,
|
||||
gatewayConnectionId,
|
||||
subscriptionGroup,
|
||||
snapshot,
|
||||
callback);
|
||||
}
|
||||
|
||||
/** Builds a new IkeSession. */
|
||||
|
|
|
|||
|
|
@ -16,7 +16,6 @@
|
|||
|
||||
package com.android.server.vcn.metrics;
|
||||
|
||||
import static com.android.server.vcn.metrics.VcnStatsLog.VCN_GATEWAY_CONNECTION_STATE_CHANGED;
|
||||
import static com.android.server.vcn.metrics.VcnStatsLog.VCN_GATEWAY_CONNECTION_STATE_CHANGED__GW_TEARDOWN_REASON__GATEWAY_TEARDOWN_REASON_INTERNAL_ERROR;
|
||||
import static com.android.server.vcn.metrics.VcnStatsLog.VCN_GATEWAY_CONNECTION_STATE_CHANGED__GW_TEARDOWN_REASON__GATEWAY_TEARDOWN_REASON_NETWORK_AGENT_UNWANTED;
|
||||
import static com.android.server.vcn.metrics.VcnStatsLog.VCN_GATEWAY_CONNECTION_STATE_CHANGED__GW_TEARDOWN_REASON__GATEWAY_TEARDOWN_REASON_NONE;
|
||||
|
|
@ -56,7 +55,7 @@ public class VcnMetrics {
|
|||
/** Log an atom when a VcnGatewayConnection has entered safe mode. */
|
||||
public void logEnterSafeMode(int gatewayConnectionId) {
|
||||
VcnStatsLog.write(
|
||||
VCN_GATEWAY_CONNECTION_STATE_CHANGED,
|
||||
VcnStatsLog.VCN_GATEWAY_CONNECTION_STATE_CHANGED,
|
||||
gatewayConnectionId,
|
||||
GATEWAY_TEARDOWN_REASON_NONE,
|
||||
true /* isInSafeMode */);
|
||||
|
|
@ -125,4 +124,12 @@ public class VcnMetrics {
|
|||
true /* isConnected */,
|
||||
false /* isValidated */);
|
||||
}
|
||||
|
||||
/**
|
||||
* Log an atom about number of validated underlying network available for VCN network selection.
|
||||
*/
|
||||
public void logValidatedUnderlyingNetworkCount(int gatewayConnectionId, int count) {
|
||||
VcnStatsLog.write(
|
||||
VcnStatsLog.VCN_UNDERLYING_NETWORKS_STATE_CHANGED, gatewayConnectionId, count);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -56,6 +56,7 @@ import com.android.internal.annotations.VisibleForTesting.Visibility;
|
|||
import com.android.modules.utils.HandlerExecutor;
|
||||
import com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscriptionSnapshot;
|
||||
import com.android.server.vcn.VcnContext;
|
||||
import com.android.server.vcn.metrics.VcnMetrics;
|
||||
import com.android.server.vcn.routeselection.UnderlyingNetworkEvaluator.NetworkEvaluatorCallback;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
|
@ -105,19 +106,31 @@ public class UnderlyingNetworkController {
|
|||
@Nullable private UnderlyingNetworkRecord mCurrentRecord;
|
||||
@Nullable private UnderlyingNetworkRecord.Builder mRecordInProgress;
|
||||
|
||||
private final int mGatewayConnectionId;
|
||||
private final VcnMetrics mVcnMetrics;
|
||||
|
||||
public UnderlyingNetworkController(
|
||||
@NonNull VcnContext vcnContext,
|
||||
@NonNull VcnGatewayConnectionConfig connectionConfig,
|
||||
int gatewayConnectionId,
|
||||
@NonNull ParcelUuid subscriptionGroup,
|
||||
@NonNull TelephonySubscriptionSnapshot snapshot,
|
||||
@NonNull UnderlyingNetworkControllerCallback cb) {
|
||||
this(vcnContext, connectionConfig, subscriptionGroup, snapshot, cb, new Dependencies());
|
||||
this(
|
||||
vcnContext,
|
||||
connectionConfig,
|
||||
gatewayConnectionId,
|
||||
subscriptionGroup,
|
||||
snapshot,
|
||||
cb,
|
||||
new Dependencies());
|
||||
}
|
||||
|
||||
@VisibleForTesting(visibility = Visibility.PRIVATE)
|
||||
UnderlyingNetworkController(
|
||||
@NonNull VcnContext vcnContext,
|
||||
@NonNull VcnGatewayConnectionConfig connectionConfig,
|
||||
int gatewayConnectionId,
|
||||
@NonNull ParcelUuid subscriptionGroup,
|
||||
@NonNull TelephonySubscriptionSnapshot snapshot,
|
||||
@NonNull UnderlyingNetworkControllerCallback cb,
|
||||
|
|
@ -128,6 +141,8 @@ public class UnderlyingNetworkController {
|
|||
mLastSnapshot = Objects.requireNonNull(snapshot, "Missing snapshot");
|
||||
mCb = Objects.requireNonNull(cb, "Missing cb");
|
||||
mDeps = Objects.requireNonNull(deps, "Missing deps");
|
||||
mGatewayConnectionId = gatewayConnectionId;
|
||||
mVcnMetrics = mDeps.newVcnMetrics();
|
||||
|
||||
mHandler = new Handler(mVcnContext.getLooper());
|
||||
|
||||
|
|
@ -468,6 +483,9 @@ public class UnderlyingNetworkController {
|
|||
.getContext()
|
||||
.getSystemService(TelephonyManager.class)
|
||||
.unregisterTelephonyCallback(mActiveDataSubIdListener);
|
||||
|
||||
// Log empty validated network count left, as a clean up state.
|
||||
mVcnMetrics.logValidatedUnderlyingNetworkCount(mGatewayConnectionId, 0);
|
||||
}
|
||||
|
||||
private TreeSet<UnderlyingNetworkEvaluator> getSortedUnderlyingNetworks() {
|
||||
|
|
@ -489,6 +507,7 @@ public class UnderlyingNetworkController {
|
|||
}
|
||||
|
||||
TreeSet<UnderlyingNetworkEvaluator> sorted = getSortedUnderlyingNetworks();
|
||||
mVcnMetrics.logValidatedUnderlyingNetworkCount(mGatewayConnectionId, sorted.size());
|
||||
|
||||
UnderlyingNetworkEvaluator candidateEvaluator = sorted.isEmpty() ? null : sorted.first();
|
||||
UnderlyingNetworkRecord candidate =
|
||||
|
|
@ -734,7 +753,9 @@ public class UnderlyingNetworkController {
|
|||
}
|
||||
|
||||
@VisibleForTesting(visibility = Visibility.PRIVATE)
|
||||
/* External dependencies, for injection in tests */
|
||||
public static class Dependencies {
|
||||
/** Builds a new UnderlyingNetworkEvaluator. */
|
||||
public UnderlyingNetworkEvaluator newUnderlyingNetworkEvaluator(
|
||||
@NonNull VcnContext vcnContext,
|
||||
@NonNull Network network,
|
||||
|
|
@ -752,5 +773,10 @@ public class UnderlyingNetworkController {
|
|||
carrierConfig,
|
||||
evaluatorCallback);
|
||||
}
|
||||
|
||||
/** Builds a new VcnMetrics. */
|
||||
public VcnMetrics newVcnMetrics() {
|
||||
return new VcnMetrics();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@ import static com.android.server.vcn.VcnTestUtils.setupIpSecManager;
|
|||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyInt;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.CALLS_REAL_METHODS;
|
||||
import static org.mockito.Mockito.atLeastOnce;
|
||||
|
|
@ -228,7 +229,7 @@ public class VcnGatewayConnectionTestBase {
|
|||
|
||||
doReturn(mUnderlyingNetworkController)
|
||||
.when(mDeps)
|
||||
.newUnderlyingNetworkController(any(), any(), any(), any(), any());
|
||||
.newUnderlyingNetworkController(any(), any(), anyInt(), any(), any(), any());
|
||||
doReturn(mVcnMetrics).when(mDeps).newVcnMetrics();
|
||||
doReturn(mWakeLock)
|
||||
.when(mDeps)
|
||||
|
|
|
|||
|
|
@ -71,6 +71,7 @@ import androidx.test.runner.AndroidJUnit4;
|
|||
import com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscriptionSnapshot;
|
||||
import com.android.server.vcn.VcnContext;
|
||||
import com.android.server.vcn.VcnNetworkProvider;
|
||||
import com.android.server.vcn.metrics.VcnMetrics;
|
||||
import com.android.server.vcn.routeselection.UnderlyingNetworkController.Dependencies;
|
||||
import com.android.server.vcn.routeselection.UnderlyingNetworkController.NetworkBringupCallback;
|
||||
import com.android.server.vcn.routeselection.UnderlyingNetworkController.UnderlyingNetworkControllerCallback;
|
||||
|
|
@ -100,6 +101,7 @@ public class UnderlyingNetworkControllerTest {
|
|||
private static final int INITIAL_SUB_ID_1 = 1;
|
||||
private static final int INITIAL_SUB_ID_2 = 2;
|
||||
private static final int UPDATED_SUB_ID = 3;
|
||||
private static final int GATEWAY_CONNECTION_ID = 1234;
|
||||
|
||||
private static final Set<Integer> INITIAL_SUB_IDS =
|
||||
new ArraySet<>(Arrays.asList(INITIAL_SUB_ID_1, INITIAL_SUB_ID_2));
|
||||
|
|
@ -164,6 +166,7 @@ public class UnderlyingNetworkControllerTest {
|
|||
@Mock private UnderlyingNetworkControllerCallback mNetworkControllerCb;
|
||||
@Mock private NetworkEvaluatorCallback mEvaluatorCallback;
|
||||
@Mock private Network mNetwork;
|
||||
@Mock private VcnMetrics mVcnMetrics;
|
||||
|
||||
@Spy private Dependencies mDependencies = new Dependencies();
|
||||
|
||||
|
|
@ -218,11 +221,13 @@ public class UnderlyingNetworkControllerTest {
|
|||
doReturn(mNetworkEvaluator)
|
||||
.when(mDependencies)
|
||||
.newUnderlyingNetworkEvaluator(any(), any(), any(), any(), any(), any(), any());
|
||||
doReturn(mVcnMetrics).when(mDependencies).newVcnMetrics();
|
||||
|
||||
mUnderlyingNetworkController =
|
||||
new UnderlyingNetworkController(
|
||||
mVcnContext,
|
||||
VcnGatewayConnectionConfigTest.buildTestConfig(),
|
||||
GATEWAY_CONNECTION_ID,
|
||||
SUB_GROUP,
|
||||
mSubscriptionSnapshot,
|
||||
mNetworkControllerCb,
|
||||
|
|
@ -268,6 +273,7 @@ public class UnderlyingNetworkControllerTest {
|
|||
new UnderlyingNetworkController(
|
||||
vcnContext,
|
||||
VcnGatewayConnectionConfigTest.buildTestConfig(),
|
||||
GATEWAY_CONNECTION_ID,
|
||||
SUB_GROUP,
|
||||
mSubscriptionSnapshot,
|
||||
mNetworkControllerCb);
|
||||
|
|
@ -320,6 +326,7 @@ public class UnderlyingNetworkControllerTest {
|
|||
new UnderlyingNetworkController(
|
||||
mVcnContext,
|
||||
VcnGatewayConnectionConfigTest.buildTestConfig(networkTemplates),
|
||||
GATEWAY_CONNECTION_ID,
|
||||
SUB_GROUP,
|
||||
mSubscriptionSnapshot,
|
||||
mNetworkControllerCb);
|
||||
|
|
@ -361,6 +368,7 @@ public class UnderlyingNetworkControllerTest {
|
|||
new UnderlyingNetworkController(
|
||||
mVcnContext,
|
||||
VcnGatewayConnectionConfigTest.buildTestConfig(networkTemplates),
|
||||
GATEWAY_CONNECTION_ID,
|
||||
SUB_GROUP,
|
||||
mSubscriptionSnapshot,
|
||||
mNetworkControllerCb);
|
||||
|
|
@ -515,6 +523,7 @@ public class UnderlyingNetworkControllerTest {
|
|||
.unregisterNetworkCallback(any(NetworkBringupCallback.class));
|
||||
verify(mConnectivityManager)
|
||||
.unregisterNetworkCallback(any(UnderlyingNetworkListener.class));
|
||||
verify(mVcnMetrics).logValidatedUnderlyingNetworkCount(eq(GATEWAY_CONNECTION_ID), eq(0));
|
||||
}
|
||||
|
||||
private static UnderlyingNetworkRecord getTestNetworkRecord(
|
||||
|
|
@ -780,6 +789,7 @@ public class UnderlyingNetworkControllerTest {
|
|||
new UnderlyingNetworkController(
|
||||
mVcnContext,
|
||||
VcnGatewayConnectionConfigTest.buildTestConfig(networkTemplates),
|
||||
GATEWAY_CONNECTION_ID,
|
||||
SUB_GROUP,
|
||||
mSubscriptionSnapshot,
|
||||
mNetworkControllerCb,
|
||||
|
|
@ -834,6 +844,24 @@ public class UnderlyingNetworkControllerTest {
|
|||
verify(expectedEvaluator, never()).setIsSelected(eq(true), any(), any(), any(), any());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testValidatedUnderlyingNetworkCountsLogged() {
|
||||
final List<VcnUnderlyingNetworkTemplate> networkTemplates = new ArrayList();
|
||||
networkTemplates.add(CELL_TEMPLATE_DUN);
|
||||
networkTemplates.add(CELL_TEMPLATE_CBS);
|
||||
|
||||
// Bring up CBS network
|
||||
UnderlyingNetworkListener cb = setupControllerAndGetNetworkListener(networkTemplates);
|
||||
bringupNetworkAndGetEvaluator(cb, CBS_NETWORK_CAPABILITIES, networkTemplates);
|
||||
|
||||
verify(mVcnMetrics).logValidatedUnderlyingNetworkCount(eq(GATEWAY_CONNECTION_ID), eq(1));
|
||||
|
||||
// Bring up DUN network
|
||||
bringupNetworkAndGetEvaluator(cb, DUN_NETWORK_CAPABILITIES, networkTemplates);
|
||||
|
||||
verify(mVcnMetrics).logValidatedUnderlyingNetworkCount(eq(GATEWAY_CONNECTION_ID), eq(2));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSelectMorePreferredNetwork() {
|
||||
final List<VcnUnderlyingNetworkTemplate> networkTemplates = new ArrayList();
|
||||
|
|
|
|||
|
|
@ -46,7 +46,7 @@ class BpfRingbufTest : public ::testing::Test {
|
|||
mRingbufPath("/sys/fs/bpf/map_bpfRingbufProg_test_ringbuf") {}
|
||||
|
||||
void SetUp() {
|
||||
if (!android::bpf::isAtLeastKernelVersion(5, 8, 0)) {
|
||||
if (!android::bpf::isAtLeastKernelVersion(5, 8)) {
|
||||
GTEST_SKIP() << "BPF ring buffers not supported below 5.8";
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -107,7 +107,7 @@ class BpfMapRO {
|
|||
protected:
|
||||
void abortOnMismatch(bool writable) const {
|
||||
if (!mMapFd.ok()) Abort(errno, "mMapFd %d is not valid", mMapFd.get());
|
||||
if (isAtLeastKernelVersion(4, 14, 0)) {
|
||||
if (isAtLeastKernelVersion(4, 14)) {
|
||||
int flags = bpfGetFdMapFlags(mMapFd);
|
||||
if (flags < 0) Abort(errno, "bpfGetFdMapFlags fail: flags=%d", flags);
|
||||
if (flags & BPF_F_WRONLY) Abort(0, "map is write-only (flags=0x%X)", flags);
|
||||
|
|
@ -228,7 +228,7 @@ class BpfMapRO {
|
|||
// Does not allow early termination (via f erroring out) - may be implemented with bulk api
|
||||
Result<void> forAll(const function<void(const Key &)> &f) const {
|
||||
// No kernel bpfmap bulk lookup api which doesn't return both keys & values.
|
||||
if (isAtLeastKernelVersion(5, 10, 0)) return doBulkLookupAndMaybeDelete(/*delete*/ false,
|
||||
if (isAtLeastKernelVersion(5, 10)) return doBulkLookupAndMaybeDelete(/*delete*/ false,
|
||||
[&f](const Key &key, const Value &) {
|
||||
f(key);
|
||||
}
|
||||
|
|
@ -258,7 +258,7 @@ class BpfMapRO {
|
|||
|
||||
// Does not allow early termination (via f erroring out) - maybe implemented with bulk api
|
||||
Result<void> forAll(const function<void(const Key &, const Value &)> &f) const {
|
||||
if (isAtLeastKernelVersion(5, 10, 0)) return doBulkLookupAndMaybeDelete(/*delete*/ false, f);
|
||||
if (isAtLeastKernelVersion(5, 10)) return doBulkLookupAndMaybeDelete(/*delete*/ false, f);
|
||||
return iterate(
|
||||
[&f](const Key &key, const Value &value) -> Result<void> {
|
||||
f(key, value);
|
||||
|
|
@ -379,7 +379,7 @@ class BpfMap : public BpfMapRW<Key, Value> {
|
|||
}
|
||||
|
||||
Result<Value> readAndDeleteValue(const Key& key) {
|
||||
if (isAtLeastKernelVersion(5, 4, 0)) {
|
||||
if (isAtLeastKernelVersion(5, 4)) {
|
||||
Value value;
|
||||
if (!findAndDeleteMapEntry(mMapFd, &key, &value)) return value;
|
||||
if (errno == ENOENT) return ERROR_FROM_ERRNO("read&DeleteVal");
|
||||
|
|
@ -423,7 +423,7 @@ class BpfMap : public BpfMapRW<Key, Value> {
|
|||
|
||||
// Does not allow early termination (via f erroring out) - maybe implemented with bulk api
|
||||
Result<void> consume(const std::function<void(const Key&, const Value&)>& f) {
|
||||
if (isAtLeastKernelVersion(5, 10, 0)) return doBulkLookupAndMaybeDelete(/*delete*/true, f);
|
||||
if (isAtLeastKernelVersion(5, 10)) return doBulkLookupAndMaybeDelete(/*delete*/true, f);
|
||||
Result<Key> curKey = getFirstKey();
|
||||
while (curKey.ok()) {
|
||||
const Result<Key> &nextKey = getNextKey(curKey.value());
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@
|
|||
#include <string.h>
|
||||
#include <sys/resource.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/system_properties.h>
|
||||
#include <sys/utsname.h>
|
||||
|
||||
#include <android-base/properties.h>
|
||||
|
|
@ -34,19 +35,44 @@
|
|||
namespace android {
|
||||
namespace bpf {
|
||||
|
||||
const bool unreleased = (base::GetProperty("ro.build.version.codename", "REL") != "REL");
|
||||
const int api_level = unreleased ? 10000 : android_get_device_api_level();
|
||||
const bool isAtLeastR = (api_level >= 30);
|
||||
const bool isAtLeastS = (api_level >= 31);
|
||||
// Sv2 is 32
|
||||
const bool isAtLeastT = (api_level >= 33);
|
||||
const bool isAtLeastU = (api_level >= 34);
|
||||
const bool isAtLeastV = (api_level >= 35);
|
||||
const bool isAtLeast25Q2 = (api_level >= 36); // 36.0
|
||||
const bool isAtLeast25Q3 = (api_level > 36); // 36.0+ (TODO: fix this!)
|
||||
const bool isAtLeast25Q4 = false; // 36.1
|
||||
const bool isAtLeast26Q1 = false; // 36.1+
|
||||
const bool isAtLeast26Q2 = false; // 37.0
|
||||
static inline int get_api_level_full() {
|
||||
// This fetches/parses 'ro.build.version.sdk' system property.
|
||||
const int api_level = android_get_device_api_level();
|
||||
// Before Baklava/25Q2 there is no 'sdk_full'
|
||||
if (api_level < 36) return api_level * 100; // 3x -> 3x00
|
||||
|
||||
// Fetch and parse the 'sdk_full' system property.
|
||||
char value[92] = {};
|
||||
if (__system_property_get("ro.build.version.sdk_full", value) < 1) abort();
|
||||
int major, minor;
|
||||
if (sscanf(value, "%d.%d", &major, &minor) != 2) abort();
|
||||
if (major < 36 || minor < 0 || minor > 9) abort();
|
||||
const int api_level_full = major * 100 + minor * 10; // 3x.y -> 3xy0
|
||||
|
||||
// Fetch and parse our platform provided .rc file - this provides quarterly info as well
|
||||
FILE * f = fopen("/system/etc/init/netbpfload.rc", "re");
|
||||
if (!f) abort();
|
||||
int y, q, a, b, c;
|
||||
if (fscanf(f, "# %d %d %d %d %d #", &y, &q, &a, &b, &c) != 5) abort();
|
||||
if (a < 36 || b < 0 || b > 9 || c < 0 || c > 4) abort();
|
||||
fclose(f);
|
||||
const int api_level_fuller = a * 100 + b * 10 + c * 2; // 3x.y.z -> 3xy[2z]
|
||||
|
||||
const bool unreleased = (base::GetProperty("ro.build.version.codename", "REL") != "REL");
|
||||
return std::max(api_level_fuller, api_level_full) + unreleased;
|
||||
}
|
||||
|
||||
const int api_level_full = get_api_level_full();
|
||||
|
||||
const bool isAtLeastS = (api_level_full >= 3100); // 31
|
||||
// 32 is Sv2
|
||||
const bool isAtLeastT = (api_level_full >= 3300); // 33
|
||||
const bool isAtLeastU = (api_level_full >= 3400); // 34
|
||||
const bool isAtLeastV = (api_level_full >= 3500); // 35
|
||||
const bool isAtLeast25Q2 = (api_level_full >= 3600); // 36.0
|
||||
const bool isAtLeast25Q4 = (api_level_full >= 3610); // 36.1
|
||||
const bool isAtLeast26Q1 = (api_level_full >= 3612); // 36.1+
|
||||
const bool isAtLeast26Q2 = (api_level_full >= 3700); // 37.0
|
||||
|
||||
// See kernel's net/core/sock_diag.c __sock_gen_cookie()
|
||||
// the implementation of which guarantees 0 will never be returned,
|
||||
|
|
|
|||
|
|
@ -81,14 +81,14 @@ static constexpr unsigned minSupportedKernelVer =
|
|||
#error "__ANDROID_API__ is pre-R"
|
||||
#endif
|
||||
|
||||
static inline bool isAtLeastKernelVersion(unsigned major, unsigned minor, unsigned sub) {
|
||||
static inline bool isAtLeastKernelVersion(unsigned major, unsigned minor, unsigned sub = 0) {
|
||||
unsigned k = KVER(major, minor, sub);
|
||||
if (k <= minSupportedKernelVer) return true;
|
||||
return kernelVer >= k;
|
||||
}
|
||||
|
||||
static inline bool isKernelVersion(unsigned major, unsigned minor) {
|
||||
return isAtLeastKernelVersion(major, minor, 0) && !isAtLeastKernelVersion(major, minor + 1, 0);
|
||||
return isAtLeastKernelVersion(major, minor) && !isAtLeastKernelVersion(major, minor + 1);
|
||||
}
|
||||
|
||||
static inline bool __unused isLtsKernel() {
|
||||
|
|
|
|||
|
|
@ -66,10 +66,6 @@
|
|||
#include "bpf/BpfUtils.h"
|
||||
#include "bpf_map_def.h"
|
||||
|
||||
// The following matches bpf_helpers.h, which is only for inclusion in bpf code
|
||||
#define BPFLOADER_MAINLINE_S_VERSION 42u
|
||||
#define BPFLOADER_MAINLINE_25Q2_VERSION 47u
|
||||
|
||||
using android::base::borrowed_fd;
|
||||
using android::base::EndsWith;
|
||||
using android::base::GetIntProperty;
|
||||
|
|
@ -452,7 +448,7 @@ int readCodeSections(ElfObject& elfObj, vector<codeSection>& cs) {
|
|||
}
|
||||
|
||||
static unsigned int sanitizeMapFlags(unsigned int map_flags) {
|
||||
if (!isAtLeastKernelVersion(5, 10, 0)) map_flags &= ~BPF_F_MMAPABLE;
|
||||
if (!isAtLeastKernelVersion(5, 10)) map_flags &= ~BPF_F_MMAPABLE;
|
||||
return map_flags;
|
||||
}
|
||||
|
||||
|
|
@ -464,7 +460,7 @@ static bool mapMatchesExpectations(const unique_fd& fd,
|
|||
// This is because the primary failure mode we're trying to detect here
|
||||
// is either a source code misconfiguration (which is likely kernel independent)
|
||||
// or a newly introduced kernel feature/bug (which is unlikely to get backported to 4.9).
|
||||
if (!isAtLeastKernelVersion(4, 14, 0)) return true;
|
||||
if (!isAtLeastKernelVersion(4, 14)) return true;
|
||||
|
||||
// Assuming fd is a valid Bpf Map file descriptor then
|
||||
// all the following should always succeed on a 4.14+ kernel.
|
||||
|
|
@ -642,7 +638,7 @@ static int loadBtf(ElfObject &elfObj, struct btf *btf) {
|
|||
if (ret) return ret;
|
||||
}
|
||||
|
||||
if (!isAtLeastKernelVersion(5, 10, 0)) {
|
||||
if (!isAtLeastKernelVersion(5, 10)) {
|
||||
// Likely unnecessary on kernel 5.4 but untested.
|
||||
sanitizeBtf(btf);
|
||||
}
|
||||
|
|
@ -778,7 +774,7 @@ static int pinMap(const borrowed_fd& fd, const struct bpf_map_def& mapDef) {
|
|||
return -err;
|
||||
}
|
||||
|
||||
if (isAtLeastKernelVersion(4, 14, 0)) {
|
||||
if (isAtLeastKernelVersion(4, 14)) {
|
||||
int mapId = bpfGetFdMapId(fd);
|
||||
if (mapId == -1) {
|
||||
const int err = errno;
|
||||
|
|
@ -791,7 +787,7 @@ static int pinMap(const borrowed_fd& fd, const struct bpf_map_def& mapDef) {
|
|||
}
|
||||
|
||||
static bool isMapTypeSupported(enum bpf_map_type type) {
|
||||
if (type == BPF_MAP_TYPE_LPM_TRIE && !isAtLeastKernelVersion(4, 14, 0)) {
|
||||
if (type == BPF_MAP_TYPE_LPM_TRIE && !isAtLeastKernelVersion(4, 14)) {
|
||||
// On Linux Kernels older than 4.14 this map type doesn't exist - autoskip.
|
||||
return false;
|
||||
}
|
||||
|
|
@ -799,7 +795,7 @@ static bool isMapTypeSupported(enum bpf_map_type type) {
|
|||
}
|
||||
|
||||
static enum bpf_map_type sanitizeMapType(enum bpf_map_type type) {
|
||||
if (type == BPF_MAP_TYPE_DEVMAP && !isAtLeastKernelVersion(4, 14, 0)) {
|
||||
if (type == BPF_MAP_TYPE_DEVMAP && !isAtLeastKernelVersion(4, 14)) {
|
||||
// On Linux Kernels older than 4.14 this map type doesn't exist, but it can kind
|
||||
// of be approximated: ARRAY has the same userspace api, though it is not usable
|
||||
// by the same ebpf programs. However, that's okay because the bpf_redirect_map()
|
||||
|
|
@ -809,7 +805,7 @@ static enum bpf_map_type sanitizeMapType(enum bpf_map_type type) {
|
|||
// Hence using an ARRAY instead of a DEVMAP simply makes life easier for userspace.
|
||||
return BPF_MAP_TYPE_ARRAY;
|
||||
}
|
||||
if (type == BPF_MAP_TYPE_DEVMAP_HASH && !isAtLeastKernelVersion(5, 4, 0)) {
|
||||
if (type == BPF_MAP_TYPE_DEVMAP_HASH && !isAtLeastKernelVersion(5, 4)) {
|
||||
// On Linux Kernels older than 5.4 this map type doesn't exist, but it can kind
|
||||
// of be approximated: HASH has the same userspace visible api.
|
||||
// However it cannot be used by ebpf programs in the same way.
|
||||
|
|
@ -823,13 +819,12 @@ static enum bpf_map_type sanitizeMapType(enum bpf_map_type type) {
|
|||
return type;
|
||||
}
|
||||
|
||||
static int createMaps(ElfObject& elfObj, vector<struct bpf_map_def>& md, vector<unique_fd>& mapFds,
|
||||
const unsigned int bpfloader_ver) {
|
||||
static int createMaps(ElfObject& elfObj, vector<struct bpf_map_def>& md, vector<unique_fd>& mapFds) {
|
||||
int ret = 0;
|
||||
vector<char> btfData;
|
||||
struct btf *btf = NULL;
|
||||
auto btfGuard = base::make_scope_guard([&btf] { if (btf) btf__free(btf); });
|
||||
if (isAtLeastKernelVersion(4, 19, 0)) {
|
||||
if (isAtLeastKernelVersion(4, 19)) {
|
||||
// On Linux Kernels older than 4.18 BPF_BTF_LOAD command doesn't exist.
|
||||
ret = elfObj.readSectionByName(".BTF", btfData);
|
||||
if (ret) {
|
||||
|
|
@ -847,14 +842,14 @@ static int createMaps(ElfObject& elfObj, vector<struct bpf_map_def>& md, vector<
|
|||
}
|
||||
|
||||
for (unsigned i = 0; i < md.size(); i++) {
|
||||
if (bpfloader_ver < md[i].bpfloader_min_ver) {
|
||||
if (api_level_full < md[i].bpfloader_min_ver) {
|
||||
ALOGD("skipping map %s which requires bpfloader min ver 0x%05x", md[i].name(),
|
||||
md[i].bpfloader_min_ver);
|
||||
mapFds.push_back(unique_fd());
|
||||
continue;
|
||||
}
|
||||
|
||||
if (bpfloader_ver >= md[i].bpfloader_max_ver) {
|
||||
if (api_level_full >= md[i].bpfloader_max_ver) {
|
||||
ALOGD("skipping map %s which requires bpfloader max ver 0x%05x", md[i].name(),
|
||||
md[i].bpfloader_max_ver);
|
||||
mapFds.push_back(unique_fd());
|
||||
|
|
@ -906,7 +901,7 @@ static int createMaps(ElfObject& elfObj, vector<struct bpf_map_def>& md, vector<
|
|||
.max_entries = max_entries,
|
||||
.map_flags = sanitizeMapFlags(md[i].map_flags),
|
||||
};
|
||||
if (isAtLeastKernelVersion(4, 15, 0))
|
||||
if (isAtLeastKernelVersion(4, 15))
|
||||
strlcpy(req.map_name, md[i].name(), sizeof(req.map_name));
|
||||
|
||||
bool haveBtf = btf && isBtfSupported(type);
|
||||
|
|
@ -1036,9 +1031,8 @@ static int pinProg(const borrowed_fd& fd, const struct bpf_prog_def& progDef) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int validateProg(const borrowed_fd& fd, const char* const progPinLoc,
|
||||
const unsigned int bpfloader_ver) {
|
||||
if (!isAtLeastKernelVersion(4, 14, 0)) {
|
||||
static int validateProg(const borrowed_fd& fd, const char* const progPinLoc) {
|
||||
if (!isAtLeastKernelVersion(4, 14)) {
|
||||
return 0;
|
||||
}
|
||||
int progId = bpfGetFdProgId(fd);
|
||||
|
|
@ -1063,15 +1057,14 @@ static int validateProg(const borrowed_fd& fd, const char* const progPinLoc,
|
|||
}
|
||||
ALOGI("prog %s id %d len jit:%d xlat:%d", progPinLoc, progId, jitLen, xlatLen);
|
||||
|
||||
if (!jitLen && bpfloader_ver >= BPFLOADER_MAINLINE_25Q2_VERSION) {
|
||||
if (!jitLen && api_level_full >= BPFLOADER_MAINLINE_25Q2_VERSION) {
|
||||
ALOGE("Kernel eBPF JIT failure for %s", progPinLoc);
|
||||
return -ENOTSUP;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int loadCodeSections(ElfObject& elfObj, vector<codeSection>& cs, const string& license,
|
||||
const unsigned int bpfloader_ver) {
|
||||
static int loadCodeSections(ElfObject& elfObj, vector<codeSection>& cs, const string& license) {
|
||||
for (int i = 0; i < (int)cs.size(); i++) {
|
||||
unique_fd& fd = cs[i].prog_fd;
|
||||
int ret;
|
||||
|
|
@ -1088,8 +1081,8 @@ static int loadCodeSections(ElfObject& elfObj, vector<codeSection>& cs, const st
|
|||
|
||||
if (kernelVer < cs[i].prog_def->min_kver) continue;
|
||||
if (kernelVer >= cs[i].prog_def->max_kver) continue;
|
||||
if (bpfloader_ver < cs[i].prog_def->bpfloader_min_ver) continue;
|
||||
if (bpfloader_ver >= cs[i].prog_def->bpfloader_max_ver) continue;
|
||||
if (api_level_full < cs[i].prog_def->bpfloader_min_ver) continue;
|
||||
if (api_level_full >= cs[i].prog_def->bpfloader_max_ver) continue;
|
||||
|
||||
bool reuse = false;
|
||||
if (access(cs[i].prog_def->pin_location, F_OK) == 0) {
|
||||
|
|
@ -1110,7 +1103,7 @@ static int loadCodeSections(ElfObject& elfObj, vector<codeSection>& cs, const st
|
|||
.log_buf = ptr_to_u64(log_buf),
|
||||
.expected_attach_type = cs[i].prog_def->attach_type,
|
||||
};
|
||||
if (isAtLeastKernelVersion(4, 15, 0))
|
||||
if (isAtLeastKernelVersion(4, 15))
|
||||
strlcpy(req.prog_name, cs[i].prog_def->name(), sizeof(req.prog_name));
|
||||
fd.reset(bpf(BPF_PROG_LOAD, req));
|
||||
|
||||
|
|
@ -1152,15 +1145,14 @@ static int loadCodeSections(ElfObject& elfObj, vector<codeSection>& cs, const st
|
|||
ret = pinProg(fd, cs[i].prog_def.value());
|
||||
if (ret) return ret;
|
||||
}
|
||||
ret = validateProg(fd, cs[i].prog_def->pin_location, bpfloader_ver);
|
||||
ret = validateProg(fd, cs[i].prog_def->pin_location);
|
||||
if (ret) return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int prepareLoadMaps(const struct bpf_object* obj, const vector<struct bpf_map_def>& md,
|
||||
const unsigned int bpfloader_ver) {
|
||||
static int prepareLoadMaps(const struct bpf_object* obj, const vector<struct bpf_map_def>& md) {
|
||||
for (unsigned i = 0; i < md.size(); i++) {
|
||||
struct bpf_map* m = bpf_object__find_map_by_name(obj, md[i].name());
|
||||
if (!m) {
|
||||
|
|
@ -1168,9 +1160,9 @@ static int prepareLoadMaps(const struct bpf_object* obj, const vector<struct bpf
|
|||
return -1;
|
||||
}
|
||||
|
||||
if (bpfloader_ver < md[i].bpfloader_min_ver || bpfloader_ver >= md[i].bpfloader_max_ver) {
|
||||
if (api_level_full < md[i].bpfloader_min_ver || api_level_full >= md[i].bpfloader_max_ver) {
|
||||
ALOGD("skipping map %s: bpfloader 0x%05x is outside required range [0x%05x, 0x%05x)",
|
||||
md[i].name(), bpfloader_ver,
|
||||
md[i].name(), api_level_full,
|
||||
md[i].bpfloader_min_ver, md[i].bpfloader_max_ver);
|
||||
bpf_map__set_autocreate(m, false);
|
||||
continue;
|
||||
|
|
@ -1195,8 +1187,7 @@ static int prepareLoadMaps(const struct bpf_object* obj, const vector<struct bpf
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int prepareLoadProgs(const struct bpf_object* obj, const vector<codeSection>& cs,
|
||||
const unsigned int bpfloader_ver) {
|
||||
static int prepareLoadProgs(const struct bpf_object* obj, const vector<codeSection>& cs) {
|
||||
for (int i = 0; i < (int)cs.size(); i++) {
|
||||
if (!cs[i].prog_def.has_value()) {
|
||||
ALOGE("[%d] missing program definition! bad bpf.o build?", i);
|
||||
|
|
@ -1218,11 +1209,11 @@ static int prepareLoadProgs(const struct bpf_object* obj, const vector<codeSecti
|
|||
continue;
|
||||
}
|
||||
|
||||
unsigned bpfMinVer = cs[i].prog_def->bpfloader_min_ver;
|
||||
unsigned bpfMaxVer = cs[i].prog_def->bpfloader_max_ver;
|
||||
if (bpfloader_ver < bpfMinVer || bpfloader_ver >= bpfMaxVer) {
|
||||
int bpfMinVer = cs[i].prog_def->bpfloader_min_ver;
|
||||
int bpfMaxVer = cs[i].prog_def->bpfloader_max_ver;
|
||||
if (api_level_full < bpfMinVer || api_level_full >= bpfMaxVer) {
|
||||
ALOGD("skipping prog %s: bpfloader 0x%05x is outside required range [0x%05x, 0x%05x)",
|
||||
cs[i].prog_def->name(), bpfloader_ver, bpfMinVer, bpfMaxVer);
|
||||
cs[i].prog_def->name(), api_level_full, bpfMinVer, bpfMaxVer);
|
||||
bpf_program__set_autoload(prog, false);
|
||||
continue;
|
||||
}
|
||||
|
|
@ -1261,7 +1252,7 @@ static int pinMaps(const struct bpf_object* obj, const vector<struct bpf_map_def
|
|||
}
|
||||
|
||||
static int pinProgs(const struct bpf_object * obj,
|
||||
const vector<codeSection>& cs, const unsigned int bpfloader_ver) {
|
||||
const vector<codeSection>& cs) {
|
||||
int ret;
|
||||
|
||||
for (int i = 0; i < (int)cs.size(); i++) {
|
||||
|
|
@ -1283,13 +1274,13 @@ static int pinProgs(const struct bpf_object * obj,
|
|||
int fd = bpf_program__fd(prog);
|
||||
ret = pinProg(fd, cs[i].prog_def.value());
|
||||
if (ret) return ret;
|
||||
ret = validateProg(fd, cs[i].prog_def->pin_location, bpfloader_ver);
|
||||
ret = validateProg(fd, cs[i].prog_def->pin_location);
|
||||
if (ret) return ret;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int loadProgByLibbpf(const char* const elfPath, const unsigned int bpfloader_ver) {
|
||||
static int loadProgByLibbpf(const char* const elfPath) {
|
||||
ElfObject elfObj(elfPath);
|
||||
vector<struct bpf_map_def> md;
|
||||
vector<codeSection> cs;
|
||||
|
|
@ -1305,30 +1296,30 @@ static int loadProgByLibbpf(const char* const elfPath, const unsigned int bpfloa
|
|||
ret = elfObj.readSectionByName(".android_maps", md);
|
||||
if (ret) return ret;
|
||||
|
||||
ret = prepareLoadMaps(obj, md, bpfloader_ver);
|
||||
ret = prepareLoadMaps(obj, md);
|
||||
if (ret) return ret;
|
||||
|
||||
ret = readCodeSections(elfObj, cs);
|
||||
if (ret && ret != -ENOENT) return ret;
|
||||
|
||||
ret = prepareLoadProgs(obj, cs, bpfloader_ver);
|
||||
ret = prepareLoadProgs(obj, cs);
|
||||
if (ret) return ret;
|
||||
|
||||
ret = bpf_object__load(obj);
|
||||
if (ret) return ret;
|
||||
// On Linux Kernels older than 4.18 BPF_BTF_LOAD command doesn't exist.
|
||||
if (isAtLeastKernelVersion(4, 19, 0) && bpf_object__btf_fd(obj) < 0) return -1;
|
||||
if (isAtLeastKernelVersion(4, 19) && bpf_object__btf_fd(obj) < 0) return -1;
|
||||
|
||||
ret = pinMaps(obj, md);
|
||||
if (ret) return ret;
|
||||
|
||||
ret = pinProgs(obj, cs, bpfloader_ver);
|
||||
ret = pinProgs(obj, cs);
|
||||
if (ret) return ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int loadProg(const char* const elfPath, const unsigned int bpfloader_ver) {
|
||||
int loadProg(const char* const elfPath) {
|
||||
ElfObject elfObj(elfPath);
|
||||
vector<char> license;
|
||||
vector<codeSection> cs;
|
||||
|
|
@ -1345,13 +1336,13 @@ int loadProg(const char* const elfPath, const unsigned int bpfloader_ver) {
|
|||
elfPath, (char*)license.data());
|
||||
}
|
||||
|
||||
ALOGD("BpfLoader ver 0x%05x processing ELF object %s", bpfloader_ver, elfPath);
|
||||
ALOGD("Processing ELF object %s", elfPath);
|
||||
|
||||
ret = elfObj.readSectionByName(".android_maps", md);
|
||||
if (ret == -2) ret = 0; // -2 means there were no maps to read
|
||||
if (ret) return ret;
|
||||
|
||||
ret = createMaps(elfObj, md, mapFds, bpfloader_ver);
|
||||
ret = createMaps(elfObj, md, mapFds);
|
||||
if (ret) {
|
||||
ALOGE("Failed to create maps: (ret=%d) in %s", ret, elfPath);
|
||||
return ret;
|
||||
|
|
@ -1369,7 +1360,7 @@ int loadProg(const char* const elfPath, const unsigned int bpfloader_ver) {
|
|||
|
||||
applyMapRelo(elfObj, md, mapFds, cs);
|
||||
|
||||
ret = loadCodeSections(elfObj, cs, string(license.data()), bpfloader_ver);
|
||||
ret = loadCodeSections(elfObj, cs, string(license.data()));
|
||||
if (ret) ALOGE("Failed to load programs, loadCodeSections ret=%d", ret);
|
||||
|
||||
return ret;
|
||||
|
|
@ -1383,10 +1374,8 @@ static bool exists(const char* const path) {
|
|||
abort(); // can only hit this if permissions (likely selinux) are screwed up
|
||||
}
|
||||
|
||||
static bool loadObject(const unsigned int bpfloader_ver,
|
||||
const char* const progPath, const bool useLibbpf = false) {
|
||||
if (useLibbpf ? loadProgByLibbpf(progPath, bpfloader_ver) :
|
||||
loadProg(progPath, bpfloader_ver)) {
|
||||
static bool loadObject(const char* const progPath, const bool useLibbpf = false) {
|
||||
if (useLibbpf ? loadProgByLibbpf(progPath) : loadProg(progPath)) {
|
||||
ALOGE("Failed to load object: %s, libbpf: %d", progPath, useLibbpf);
|
||||
return false;
|
||||
}
|
||||
|
|
@ -1397,14 +1386,14 @@ static bool loadObject(const unsigned int bpfloader_ver,
|
|||
#define APEXROOT "/apex/com.android.tethering"
|
||||
#define BPFROOT APEXROOT "/etc/bpf/mainline/"
|
||||
|
||||
static bool loadAllObjects(const unsigned int bpfloader_ver) {
|
||||
bool libbpf = isAtLeast25Q3 || useLibBpf;
|
||||
if (!loadObject(bpfloader_ver, BPFROOT "offload.o")) return false;
|
||||
if (!loadObject(bpfloader_ver, BPFROOT "test.o", libbpf)) return false;
|
||||
static bool loadAllObjects() {
|
||||
bool libbpf = isAtLeast26Q1 || useLibBpf;
|
||||
if (!loadObject(BPFROOT "offload.o")) return false;
|
||||
if (!loadObject(BPFROOT "test.o", libbpf)) return false;
|
||||
if (isAtLeastT) {
|
||||
if (!loadObject(bpfloader_ver, BPFROOT "clatd.o", libbpf)) return false;
|
||||
if (!loadObject(bpfloader_ver, BPFROOT "dscpPolicy.o", libbpf)) return false;
|
||||
if (!loadObject(bpfloader_ver, BPFROOT "netd.o", libbpf)) return false;
|
||||
if (!loadObject(BPFROOT "clatd.o", libbpf)) return false;
|
||||
if (!loadObject(BPFROOT "dscpPolicy.o", libbpf)) return false;
|
||||
if (!loadObject(BPFROOT "netd.o", libbpf)) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
|
@ -1590,28 +1579,15 @@ static int doLoad(char** argv, char * const envp[]) {
|
|||
|
||||
const bool runningAsRoot = !getuid(); // true iff U QPR3 or V+
|
||||
|
||||
const int first_api_level = GetIntProperty("ro.board.first_api_level", api_level);
|
||||
const int first_api_level = GetIntProperty("ro.board.first_api_level", api_level_full / 100);
|
||||
|
||||
// last in U QPR2 beta1
|
||||
const bool has_platform_bpfloader_rc = exists("/system/etc/init/bpfloader.rc");
|
||||
// first in U QPR2 beta~2
|
||||
const bool has_platform_netbpfload_rc = exists("/system/etc/init/netbpfload.rc");
|
||||
|
||||
// Version of Network BpfLoader depends on the Android OS version
|
||||
unsigned int bpfloader_ver = BPFLOADER_MAINLINE_S_VERSION; // [42u]
|
||||
if (isAtLeastT) ++bpfloader_ver; // [43] BPFLOADER_MAINLINE_T_VERSION
|
||||
if (isAtLeastU) ++bpfloader_ver; // [44] BPFLOADER_MAINLINE_U_VERSION
|
||||
if (runningAsRoot) ++bpfloader_ver; // [45] BPFLOADER_MAINLINE_U_QPR3_VERSION
|
||||
if (isAtLeastV) ++bpfloader_ver; // [46] BPFLOADER_MAINLINE_V_VERSION
|
||||
if (isAtLeast25Q2) ++bpfloader_ver; // [47] BPFLOADER_MAINLINE_25Q2_VERSION
|
||||
if (isAtLeast25Q3) ++bpfloader_ver; // [48] BPFLOADER_MAINLINE_25Q3_VERSION
|
||||
if (isAtLeast25Q4) ++bpfloader_ver; // [49] BPFLOADER_MAINLINE_25Q4_VERSION
|
||||
if (isAtLeast26Q1) ++bpfloader_ver; // [50] BPFLOADER_MAINLINE_26Q1_VERSION
|
||||
if (isAtLeast26Q2) ++bpfloader_ver; // [51] BPFLOADER_MAINLINE_26Q2_VERSION
|
||||
|
||||
ALOGI("NetBpfLoad v0.%u (%s) api:%d/%d kver:%07x (%s) libbpf: v%u.%u "
|
||||
"uid:%d rc:%d%d",
|
||||
bpfloader_ver, argv[0], android_get_device_api_level(), api_level,
|
||||
ALOGI("NetBpfLoad (%s) api:%d/%d kver:%07x (%s) libbpf: v%u.%u uid:%d rc:%d%d",
|
||||
argv[0], android_get_device_api_level(), api_level_full,
|
||||
kernelVer, describeArch(), libbpf_major_version(),
|
||||
libbpf_minor_version(), getuid(), has_platform_bpfloader_rc,
|
||||
has_platform_netbpfload_rc);
|
||||
|
|
@ -1630,41 +1606,41 @@ static int doLoad(char** argv, char * const envp[]) {
|
|||
|
||||
// both S and T require kernel 4.9 (and eBpf support)
|
||||
// (this also guarantees 'kernelVer' isn't an invalid uninitialized 0)
|
||||
if (!isAtLeastKernelVersion(4, 9, 0)) {
|
||||
if (!isAtLeastKernelVersion(4, 9)) {
|
||||
ALOGE("Android S & T require kernel 4.9.");
|
||||
return 3;
|
||||
}
|
||||
|
||||
// U bumps the kernel requirement up to 4.14
|
||||
if (isAtLeastU && !isAtLeastKernelVersion(4, 14, 0)) {
|
||||
if (isAtLeastU && !isAtLeastKernelVersion(4, 14)) {
|
||||
ALOGE("Android U requires kernel 4.14.");
|
||||
return 4;
|
||||
}
|
||||
|
||||
// V bumps the kernel requirement up to 4.19
|
||||
// see also: //system/netd/tests/kernel_test.cpp TestKernel419
|
||||
if (isAtLeastV && !isAtLeastKernelVersion(4, 19, 0)) {
|
||||
if (isAtLeastV && !isAtLeastKernelVersion(4, 19)) {
|
||||
ALOGE("Android V requires kernel 4.19.");
|
||||
return 5;
|
||||
}
|
||||
|
||||
// 25Q2 bumps the kernel requirement up to 5.4
|
||||
// see also: //system/netd/tests/kernel_test.cpp TestKernel54
|
||||
if (isAtLeast25Q2 && !isAtLeastKernelVersion(5, 4, 0)) {
|
||||
if (isAtLeast25Q2 && !isAtLeastKernelVersion(5, 4)) {
|
||||
ALOGE("Android 25Q2 requires kernel 5.4.");
|
||||
return 6;
|
||||
}
|
||||
|
||||
// 25Q4 bumps the kernel requirement up to 5.10
|
||||
// see also: //system/netd/tests/kernel_test.cpp TestKernel510
|
||||
if (isAtLeast25Q4 && !isAtLeastKernelVersion(5, 10, 0)) {
|
||||
if (isAtLeast25Q4 && !isAtLeastKernelVersion(5, 10)) {
|
||||
ALOGE("Android 25Q4 requires kernel 5.10.");
|
||||
return 7;
|
||||
}
|
||||
|
||||
// Technically already required by U, but only enforce on V+
|
||||
// see also: //system/netd/tests/kernel_test.cpp TestKernel64Bit
|
||||
if (isAtLeastV && isKernel32Bit() && isAtLeastKernelVersion(5, 16, 0)) {
|
||||
if (isAtLeastV && isKernel32Bit() && isAtLeastKernelVersion(5, 16)) {
|
||||
ALOGE("Android V+ platform with 32 bit kernel version >= 5.16.0 is unsupported");
|
||||
if (!isTV()) return 8;
|
||||
}
|
||||
|
|
@ -1675,7 +1651,7 @@ static int doLoad(char** argv, char * const envp[]) {
|
|||
}
|
||||
|
||||
// 6.6 is highest version supported by Android V, so this is effectively W+ (sdk=36+)
|
||||
if (isKernel32Bit() && isAtLeastKernelVersion(6, 7, 0)) {
|
||||
if (isKernel32Bit() && isAtLeastKernelVersion(6, 7)) {
|
||||
ALOGE("Android platform with 32 bit kernel version >= 6.7.0 is unsupported");
|
||||
return 10;
|
||||
}
|
||||
|
|
@ -1740,7 +1716,7 @@ static int doLoad(char** argv, char * const envp[]) {
|
|||
* Note, however, that TV and Wear devices will continue to support 32-bit userspace
|
||||
* on ARM64.
|
||||
*/
|
||||
if (isUserspace32bit() && isAtLeastKernelVersion(6, 2, 0)) {
|
||||
if (isUserspace32bit() && isAtLeastKernelVersion(6, 2)) {
|
||||
// Stuff won't work reliably, but...
|
||||
if (isArm() && (isTV() || isWear())) {
|
||||
// exempt Arm TV or Wear devices (arm32 ABI is far less problematic than x86-32)
|
||||
|
|
@ -1765,7 +1741,7 @@ static int doLoad(char** argv, char * const envp[]) {
|
|||
// Since officially Android only supports LTS, 6.13+ really means 6.18+,
|
||||
// and won't be supported before 2026, most likely Android 17 / 26Q2.
|
||||
// 6.13+ (implying 26Q2+) requires 64-bit userspace.
|
||||
if (isUserspace32bit() && isAtLeastKernelVersion(6, 13, 0)) {
|
||||
if (isUserspace32bit() && isAtLeastKernelVersion(6, 13)) {
|
||||
// due to previous check only reachable on Arm && (<=T kernel uprev || TV || Wear)
|
||||
ALOGE("64-bit userspace required on 6.13+ kernels.");
|
||||
return 14;
|
||||
|
|
@ -1804,7 +1780,7 @@ static int doLoad(char** argv, char * const envp[]) {
|
|||
// (this writeFile is known to fail on at least 4.19, but always defaults to 0 on
|
||||
// pre-5.13, on 5.13+ it depends on CONFIG_BPF_UNPRIV_DEFAULT_OFF)
|
||||
if (!writeFile("/proc/sys/kernel/unprivileged_bpf_disabled", "0\n") &&
|
||||
isAtLeastKernelVersion(5, 13, 0)) return 23;
|
||||
isAtLeastKernelVersion(5, 13)) return 23;
|
||||
}
|
||||
|
||||
if (isAtLeastU) {
|
||||
|
|
@ -1858,7 +1834,7 @@ static int doLoad(char** argv, char * const envp[]) {
|
|||
ALOGE("bpfGetNextMapId(zero) returned %u (errno %d)", mapId, errno);
|
||||
return 33;
|
||||
}
|
||||
} else if (isAtLeastKernelVersion(4, 14, 0)) { // implies S through U QPR2
|
||||
} else if (isAtLeastKernelVersion(4, 14)) { // implies S through U QPR2
|
||||
// bpfGetNext{Prog,Map}Id require 4.14+
|
||||
// furthermore since we're not running as root, we're not the initial
|
||||
// platform bpfloader, so there may already be some maps & programs.
|
||||
|
|
@ -1883,11 +1859,11 @@ static int doLoad(char** argv, char * const envp[]) {
|
|||
// and thus it should return 0 with errno == ENOENT.
|
||||
ALOGE("bpfGetNextMapId(final %d) returned %d errno %d", mapId, next, errno);
|
||||
if (next || errno != ENOENT) return 35;
|
||||
if (isAtLeastT || isAtLeastKernelVersion(4, 20, 0)) return 36;
|
||||
if (isAtLeastT || isAtLeastKernelVersion(4, 20)) return 36;
|
||||
// implies Android S with 4.14 or 4.19 kernel
|
||||
ALOGW("Detected kernel with invalid BPF UAPI - disabling mainline use of eBPF.");
|
||||
// leave a flag that we're 'done'
|
||||
if (!createDir("/sys/fs/bpf/netd_shared/mainline_done")) return 43; // same as below
|
||||
if (!createDir("/sys/fs/bpf/netd_shared/mainline_done")) return 37;
|
||||
return 0;
|
||||
}
|
||||
} else { // implies S/T with 4.9 kernel
|
||||
|
|
@ -1895,27 +1871,20 @@ static int doLoad(char** argv, char * const envp[]) {
|
|||
}
|
||||
|
||||
// Load all ELF objects, create programs and maps, and pin them
|
||||
if (!loadAllObjects(bpfloader_ver)) {
|
||||
if (!loadAllObjects()) {
|
||||
ALOGE("=== CRITICAL FAILURE LOADING BPF PROGRAMS ===");
|
||||
ALOGE("If this triggers reliably, you're probably missing kernel options or patches.");
|
||||
ALOGE("If this triggers randomly, you might be hitting some memory allocation "
|
||||
"problems or startup script race.");
|
||||
ALOGE("--- DO NOT EXPECT SYSTEM TO BOOT SUCCESSFULLY ---");
|
||||
sleep(20);
|
||||
return 37;
|
||||
return 38;
|
||||
}
|
||||
|
||||
{
|
||||
// Create a trivial bpf map: a two element array [int->int]
|
||||
unique_fd map(createMap(BPF_MAP_TYPE_ARRAY, sizeof(int), sizeof(int), 2, 0));
|
||||
|
||||
int zero = 0;
|
||||
int kernel_bugs = 0;
|
||||
if (writeToMapEntry(map, &zero, &kernel_bugs, BPF_ANY)) {
|
||||
ALOGE("Failure to write into index 0 of kernel bugs array.");
|
||||
return 38;
|
||||
}
|
||||
|
||||
int one = 1;
|
||||
int value = 123;
|
||||
if (writeToMapEntry(map, &one, &value, BPF_ANY)) {
|
||||
|
|
|
|||
|
|
@ -82,12 +82,12 @@ static Status initPrograms(const char* cg2_path) {
|
|||
if (!isAtLeastT) return Status("S- platform is unsupported");
|
||||
|
||||
// S requires eBPF support which was only added in 4.9, so this should be satisfied.
|
||||
if (!isAtLeastKernelVersion(4, 9, 0)) {
|
||||
if (!isAtLeastKernelVersion(4, 9)) {
|
||||
return Status("kernel version < 4.9.0 is unsupported");
|
||||
}
|
||||
|
||||
// U bumps the kernel requirement up to 4.14
|
||||
if (isAtLeastU && !isAtLeastKernelVersion(4, 14, 0)) {
|
||||
if (isAtLeastU && !isAtLeastKernelVersion(4, 14)) {
|
||||
return Status("U+ platform with kernel version < 4.14.0 is unsupported");
|
||||
}
|
||||
|
||||
|
|
@ -97,12 +97,12 @@ static Status initPrograms(const char* cg2_path) {
|
|||
}
|
||||
|
||||
// V bumps the kernel requirement up to 4.19
|
||||
if (isAtLeastV && !isAtLeastKernelVersion(4, 19, 0)) {
|
||||
if (isAtLeastV && !isAtLeastKernelVersion(4, 19)) {
|
||||
return Status("V+ platform with kernel version < 4.19.0 is unsupported");
|
||||
}
|
||||
|
||||
// 25Q2 bumps the kernel requirement up to 5.4
|
||||
if (isAtLeast25Q2 && !isAtLeastKernelVersion(5, 4, 0)) {
|
||||
if (isAtLeast25Q2 && !isAtLeastKernelVersion(5, 4)) {
|
||||
return Status("25Q2+ platform with kernel version < 5.4.0 is unsupported");
|
||||
}
|
||||
|
||||
|
|
@ -121,12 +121,12 @@ static Status initPrograms(const char* cg2_path) {
|
|||
// cgroup if the program is pinned properly.
|
||||
// TODO: delete the if statement once all devices should support cgroup
|
||||
// socket filter (ie. the minimum kernel version required is 4.14).
|
||||
if (isAtLeastKernelVersion(4, 14, 0)) {
|
||||
if (isAtLeastKernelVersion(4, 14)) {
|
||||
RETURN_IF_NOT_OK(attachProgramToCgroup(CGROUP_INET_CREATE_PROG_PATH,
|
||||
cg_fd, BPF_CGROUP_INET_SOCK_CREATE));
|
||||
}
|
||||
|
||||
if (isAtLeastKernelVersion(5, 10, 0)) {
|
||||
if (isAtLeastKernelVersion(5, 10)) {
|
||||
RETURN_IF_NOT_OK(attachProgramToCgroup(CGROUP_INET_RELEASE_PROG_PATH,
|
||||
cg_fd, BPF_CGROUP_INET_SOCK_RELEASE));
|
||||
}
|
||||
|
|
@ -134,7 +134,7 @@ static Status initPrograms(const char* cg2_path) {
|
|||
if (isAtLeastV) {
|
||||
// V requires 4.19+, so technically this 2nd 'if' is not required, but it
|
||||
// doesn't hurt us to try to support AOSP forks that try to support older kernels.
|
||||
if (isAtLeastKernelVersion(4, 19, 0)) {
|
||||
if (isAtLeastKernelVersion(4, 19)) {
|
||||
RETURN_IF_NOT_OK(attachProgramToCgroup(CGROUP_CONNECT4_PROG_PATH,
|
||||
cg_fd, BPF_CGROUP_INET4_CONNECT));
|
||||
RETURN_IF_NOT_OK(attachProgramToCgroup(CGROUP_CONNECT6_PROG_PATH,
|
||||
|
|
@ -149,7 +149,7 @@ static Status initPrograms(const char* cg2_path) {
|
|||
cg_fd, BPF_CGROUP_UDP6_SENDMSG));
|
||||
}
|
||||
|
||||
if (isAtLeastKernelVersion(5, 4, 0)) {
|
||||
if (isAtLeastKernelVersion(5, 4)) {
|
||||
RETURN_IF_NOT_OK(attachProgramToCgroup(CGROUP_GETSOCKOPT_PROG_PATH,
|
||||
cg_fd, BPF_CGROUP_GETSOCKOPT));
|
||||
RETURN_IF_NOT_OK(attachProgramToCgroup(CGROUP_SETSOCKOPT_PROG_PATH,
|
||||
|
|
@ -157,7 +157,7 @@ static Status initPrograms(const char* cg2_path) {
|
|||
}
|
||||
}
|
||||
|
||||
if (isAtLeastKernelVersion(4, 19, 0)) {
|
||||
if (isAtLeastKernelVersion(4, 19)) {
|
||||
RETURN_IF_NOT_OK(attachProgramToCgroup(CGROUP_BIND4_PROG_PATH,
|
||||
cg_fd, BPF_CGROUP_INET4_BIND));
|
||||
RETURN_IF_NOT_OK(attachProgramToCgroup(CGROUP_BIND6_PROG_PATH,
|
||||
|
|
@ -172,14 +172,14 @@ static Status initPrograms(const char* cg2_path) {
|
|||
if (queryProgram(cg_fd, BPF_CGROUP_INET6_BIND) <= 0) abort();
|
||||
}
|
||||
|
||||
if (isAtLeastKernelVersion(5, 10, 0)) {
|
||||
if (isAtLeastKernelVersion(5, 10)) {
|
||||
if (queryProgram(cg_fd, BPF_CGROUP_INET_SOCK_RELEASE) <= 0) abort();
|
||||
}
|
||||
|
||||
if (isAtLeastV) {
|
||||
// V requires 4.19+, so technically this 2nd 'if' is not required, but it
|
||||
// doesn't hurt us to try to support AOSP forks that try to support older kernels.
|
||||
if (isAtLeastKernelVersion(4, 19, 0)) {
|
||||
if (isAtLeastKernelVersion(4, 19)) {
|
||||
if (queryProgram(cg_fd, BPF_CGROUP_INET4_CONNECT) <= 0) abort();
|
||||
if (queryProgram(cg_fd, BPF_CGROUP_INET6_CONNECT) <= 0) abort();
|
||||
if (queryProgram(cg_fd, BPF_CGROUP_UDP4_RECVMSG) <= 0) abort();
|
||||
|
|
@ -188,7 +188,7 @@ static Status initPrograms(const char* cg2_path) {
|
|||
if (queryProgram(cg_fd, BPF_CGROUP_UDP6_SENDMSG) <= 0) abort();
|
||||
}
|
||||
|
||||
if (isAtLeastKernelVersion(5, 4, 0)) {
|
||||
if (isAtLeastKernelVersion(5, 4)) {
|
||||
if (queryProgram(cg_fd, BPF_CGROUP_GETSOCKOPT) <= 0) abort();
|
||||
if (queryProgram(cg_fd, BPF_CGROUP_SETSOCKOPT) <= 0) abort();
|
||||
}
|
||||
|
|
@ -319,7 +319,7 @@ static void mapLockTest(void) {
|
|||
|
||||
Status BpfHandler::initMaps() {
|
||||
// bpfLock() requires bpfGetFdMapId which is only available on 4.14+ kernels.
|
||||
if (isAtLeastKernelVersion(4, 14, 0)) {
|
||||
if (isAtLeastKernelVersion(4, 14)) {
|
||||
mapLockTest();
|
||||
}
|
||||
|
||||
|
|
@ -378,7 +378,7 @@ int BpfHandler::tagSocket(int sockFd, uint32_t tag, uid_t chargeUid, uid_t realU
|
|||
|
||||
// On 5.10+ the BPF_CGROUP_INET_SOCK_RELEASE hook takes care of cookie tag map cleanup
|
||||
// during socket destruction. As such the socket destroy listener is superfluous.
|
||||
if (!isAtLeastKernelVersion(5, 10, 0)) {
|
||||
if (!isAtLeastKernelVersion(5, 10)) {
|
||||
int socketProto;
|
||||
socklen_t protoLen = sizeof(socketProto);
|
||||
if (getsockopt(sockFd, SOL_SOCKET, SO_PROTOCOL, &socketProto, &protoLen)) {
|
||||
|
|
|
|||
|
|
@ -191,7 +191,7 @@ TEST_F(BpfHandlerTest, TestTagSocketWithUnsupportedProtocol) {
|
|||
int rawSocket = socket(AF_INET, SOCK_RAW | SOCK_CLOEXEC, IPPROTO_RAW);
|
||||
EXPECT_LE(0, rawSocket);
|
||||
EXPECT_NE(NONEXISTENT_COOKIE, getSocketCookie(rawSocket));
|
||||
if (isAtLeastKernelVersion(5, 10, 0)) {
|
||||
if (isAtLeastKernelVersion(5, 10)) {
|
||||
EXPECT_EQ(0, mBh.tagSocket(rawSocket, TEST_TAG, TEST_UID, TEST_UID));
|
||||
} else {
|
||||
EXPECT_EQ(-EPROTONOSUPPORT, mBh.tagSocket(rawSocket, TEST_TAG, TEST_UID, TEST_UID));
|
||||
|
|
|
|||
|
|
@ -6,37 +6,6 @@
|
|||
|
||||
#include "bpf_map_def.h"
|
||||
|
||||
// Android Mainline BpfLoader when running on Android S (sdk=31)
|
||||
// Note: this value (and the following +1u's) are hardcoded in NetBpfLoad.cpp
|
||||
#define BPFLOADER_MAINLINE_S_VERSION 42u
|
||||
|
||||
// Android Mainline BpfLoader when running on Android T (sdk=33)
|
||||
#define BPFLOADER_MAINLINE_T_VERSION (BPFLOADER_MAINLINE_S_VERSION + 1u)
|
||||
|
||||
// Android Mainline BpfLoader when running on Android U (sdk=34)
|
||||
#define BPFLOADER_MAINLINE_U_VERSION (BPFLOADER_MAINLINE_T_VERSION + 1u)
|
||||
|
||||
// Android Mainline BpfLoader when running on Android U QPR3
|
||||
#define BPFLOADER_MAINLINE_U_QPR3_VERSION (BPFLOADER_MAINLINE_U_VERSION + 1u)
|
||||
|
||||
// Android Mainline BpfLoader when running on Android V (sdk=35)
|
||||
#define BPFLOADER_MAINLINE_V_VERSION (BPFLOADER_MAINLINE_U_QPR3_VERSION + 1u)
|
||||
|
||||
// Android Mainline BpfLoader when running on Android 25Q2 (sdk=36 aka 36.0)
|
||||
#define BPFLOADER_MAINLINE_25Q2_VERSION (BPFLOADER_MAINLINE_V_VERSION + 1u)
|
||||
|
||||
// Android Mainline BpfLoader when running on Android 25Q3 (sdk 36.0+)
|
||||
#define BPFLOADER_MAINLINE_25Q3_VERSION (BPFLOADER_MAINLINE_25Q2_VERSION + 1u)
|
||||
|
||||
// Android Mainline BpfLoader when running on Android 25Q4 (sdk 36.1)
|
||||
#define BPFLOADER_MAINLINE_25Q4_VERSION (BPFLOADER_MAINLINE_25Q3_VERSION + 1u)
|
||||
|
||||
// Android Mainline BpfLoader when running on Android 26Q1 (sdk 36.1+)
|
||||
#define BPFLOADER_MAINLINE_26Q1_VERSION (BPFLOADER_MAINLINE_25Q4_VERSION + 1u)
|
||||
|
||||
// Android Mainline BpfLoader when running on Android 26Q2 (sdk 37.0)
|
||||
#define BPFLOADER_MAINLINE_26Q2_VERSION (BPFLOADER_MAINLINE_26Q1_VERSION + 1u)
|
||||
|
||||
/* You should #define BPFLOADER_{MIN/MAX}_VER before #include "bpf_helpers.h"
|
||||
* to change which bpfloaders will process the resulting .o file.
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -138,8 +138,8 @@ struct bpf_map_def {
|
|||
unsigned int gid; // gid_t
|
||||
unsigned int mode; // mode_t
|
||||
|
||||
unsigned int bpfloader_min_ver;
|
||||
unsigned int bpfloader_max_ver;
|
||||
int bpfloader_min_ver;
|
||||
int bpfloader_max_ver;
|
||||
|
||||
// kernelVer must be >= min_kver and < max_kver
|
||||
unsigned int min_kver;
|
||||
|
|
@ -181,8 +181,8 @@ struct bpf_prog_def {
|
|||
|
||||
char pad0[3]; // manually pad up to 4 byte alignment, may be used for extensions in the future
|
||||
|
||||
unsigned int bpfloader_min_ver;
|
||||
unsigned int bpfloader_max_ver;
|
||||
int bpfloader_min_ver;
|
||||
int bpfloader_max_ver;
|
||||
|
||||
char create_location[BPF_DEF_CHAR_ARRAY_SIZE];
|
||||
char pin_location[BPF_DEF_CHAR_ARRAY_SIZE];
|
||||
|
|
@ -203,3 +203,13 @@ static_assert(std::is_standard_layout_v<struct bpf_prog_def>);
|
|||
_Static_assert(sizeof(struct bpf_prog_def) == 40 + 2 * BPF_DEF_CHAR_ARRAY_SIZE, "wrong sizeof struct bpf_prog_def");
|
||||
_Static_assert(__alignof__(struct bpf_prog_def) == 4, "__alignof__ struct bpf_prog_def != 4");
|
||||
_Static_assert(_Alignof(struct bpf_prog_def) == 4, "_Alignof struct bpf_prog_def != 4");
|
||||
|
||||
// Android Mainline BpfLoader version when running on:
|
||||
#define BPFLOADER_MAINLINE_S_VERSION 3100 // Android S (31)
|
||||
#define BPFLOADER_MAINLINE_T_VERSION 3300 // Android T (33)
|
||||
#define BPFLOADER_MAINLINE_U_VERSION 3400 // Android U (34)
|
||||
#define BPFLOADER_MAINLINE_V_VERSION 3500 // Android V (35)
|
||||
#define BPFLOADER_MAINLINE_25Q2_VERSION 3600 // Android 25Q2 (36.0)
|
||||
#define BPFLOADER_MAINLINE_25Q4_VERSION 3610 // Android 25Q4 (36.1)
|
||||
#define BPFLOADER_MAINLINE_26Q1_VERSION 3612 // Android 26Q1 (36.1+)
|
||||
#define BPFLOADER_MAINLINE_26Q2_VERSION 3700 // Android 26Q2 (37.0)
|
||||
|
|
|
|||
|
|
@ -32,7 +32,6 @@ using std::set;
|
|||
using std::string;
|
||||
|
||||
using android::bpf::isAtLeastKernelVersion;
|
||||
using android::bpf::isAtLeastR;
|
||||
using android::bpf::isAtLeastS;
|
||||
using android::bpf::isAtLeastT;
|
||||
using android::bpf::isAtLeastU;
|
||||
|
|
@ -203,7 +202,7 @@ TEST_F(BpfExistenceTest, TestPrograms) {
|
|||
ASSERT_TRUE(isAtLeastS); // Q & R are no longer supported by mainline
|
||||
|
||||
// S requires Linux Kernel 4.9+ and thus requires eBPF support.
|
||||
if (isAtLeastS) ASSERT_TRUE(isAtLeastKernelVersion(4, 9, 0));
|
||||
if (isAtLeastS) ASSERT_TRUE(isAtLeastKernelVersion(4, 9));
|
||||
|
||||
// on S without a new enough DnsResolver apex, NetBpfLoad doesn't get triggered,
|
||||
// and thus no mainline programs get loaded.
|
||||
|
|
@ -215,28 +214,28 @@ TEST_F(BpfExistenceTest, TestPrograms) {
|
|||
|
||||
// T still only requires Linux Kernel 4.9+.
|
||||
DO_EXPECT(isAtLeastT, MAINLINE_FOR_T_PLUS);
|
||||
DO_EXPECT(isAtLeastT && isAtLeastKernelVersion(4, 14, 0), MAINLINE_FOR_T_4_14_PLUS);
|
||||
DO_EXPECT(isAtLeastT && isAtLeastKernelVersion(4, 19, 0), MAINLINE_FOR_T_4_19_PLUS);
|
||||
DO_EXPECT(isAtLeastT && isAtLeastKernelVersion(5, 10, 0), MAINLINE_FOR_T_5_10_PLUS);
|
||||
DO_EXPECT(isAtLeastT && isAtLeastKernelVersion(5, 15, 0), MAINLINE_FOR_T_5_15_PLUS);
|
||||
DO_EXPECT(isAtLeastT && isAtLeastKernelVersion(4, 14), MAINLINE_FOR_T_4_14_PLUS);
|
||||
DO_EXPECT(isAtLeastT && isAtLeastKernelVersion(4, 19), MAINLINE_FOR_T_4_19_PLUS);
|
||||
DO_EXPECT(isAtLeastT && isAtLeastKernelVersion(5, 10), MAINLINE_FOR_T_5_10_PLUS);
|
||||
DO_EXPECT(isAtLeastT && isAtLeastKernelVersion(5, 15), MAINLINE_FOR_T_5_15_PLUS);
|
||||
|
||||
// U requires Linux Kernel 4.14+, but nothing (as yet) added or removed in U.
|
||||
if (isAtLeastU) ASSERT_TRUE(isAtLeastKernelVersion(4, 14, 0));
|
||||
if (isAtLeastU) ASSERT_TRUE(isAtLeastKernelVersion(4, 14));
|
||||
DO_EXPECT(isAtLeastU, MAINLINE_FOR_U_PLUS);
|
||||
DO_EXPECT(isAtLeastU && isAtLeastKernelVersion(5, 10, 0), MAINLINE_FOR_U_5_10_PLUS);
|
||||
DO_EXPECT(isAtLeastU && isAtLeastKernelVersion(5, 10), MAINLINE_FOR_U_5_10_PLUS);
|
||||
|
||||
// V requires Linux Kernel 4.19+, but nothing (as yet) added or removed in V.
|
||||
if (isAtLeastV) ASSERT_TRUE(isAtLeastKernelVersion(4, 19, 0));
|
||||
if (isAtLeastV) ASSERT_TRUE(isAtLeastKernelVersion(4, 19));
|
||||
DO_EXPECT(isAtLeastV, MAINLINE_FOR_V_PLUS);
|
||||
DO_EXPECT(isAtLeastV && isAtLeastKernelVersion(5, 4, 0), MAINLINE_FOR_V_5_4_PLUS);
|
||||
DO_EXPECT(isAtLeastV && isAtLeastKernelVersion(5, 4), MAINLINE_FOR_V_5_4_PLUS);
|
||||
|
||||
if (isAtLeast25Q2) ASSERT_TRUE(isAtLeastKernelVersion(5, 4, 0));
|
||||
if (isAtLeast25Q2) ASSERT_TRUE(isAtLeastKernelVersion(5, 4));
|
||||
DO_EXPECT(isAtLeast25Q2, MAINLINE_FOR_25Q2_PLUS);
|
||||
|
||||
if (isAtLeast25Q4) ASSERT_TRUE(isAtLeastKernelVersion(5, 10, 0));
|
||||
if (isAtLeast25Q4) ASSERT_TRUE(isAtLeastKernelVersion(5, 10));
|
||||
|
||||
DO_EXPECT(isAtLeast26Q2, MAINLINE_FOR_26Q2_PLUS);
|
||||
DO_EXPECT(isAtLeast26Q2 && isAtLeastKernelVersion(6, 1, 0), MAINLINE_FOR_26Q2_6_1_PLUS);
|
||||
DO_EXPECT(isAtLeast26Q2 && isAtLeastKernelVersion(6, 1), MAINLINE_FOR_26Q2_6_1_PLUS);
|
||||
|
||||
for (const auto& file : mustExist) {
|
||||
EXPECT_EQ(0, access(file.c_str(), R_OK)) << file << " does not exist";
|
||||
|
|
|
|||
|
|
@ -19,6 +19,8 @@ package android.net;
|
|||
import static com.android.internal.annotations.VisibleForTesting.Visibility.PRIVATE;
|
||||
import static com.android.net.module.util.BitUtils.appendStringRepresentationOfBitMaskToStringBuilder;
|
||||
import static com.android.net.module.util.BitUtils.describeDifferences;
|
||||
import static com.android.net.module.util.FrameworkConnectivityStatsLog.CORE_NETWORKING_TERRIBLE_ERROR_OCCURRED;
|
||||
import static com.android.net.module.util.FrameworkConnectivityStatsLog.CORE_NETWORKING_TERRIBLE_ERROR_OCCURRED__ERROR_TYPE__TYPE_SPECIFIER_REDACTION_REFLECTION_ERROR;
|
||||
|
||||
import android.annotation.FlaggedApi;
|
||||
import android.annotation.IntDef;
|
||||
|
|
@ -37,6 +39,7 @@ import android.os.Process;
|
|||
import android.text.TextUtils;
|
||||
import android.util.ArraySet;
|
||||
import android.util.Log;
|
||||
import android.util.LruCache;
|
||||
import android.util.Range;
|
||||
|
||||
import com.android.internal.annotations.VisibleForTesting;
|
||||
|
|
@ -44,15 +47,19 @@ import com.android.modules.utils.build.SdkLevel;
|
|||
import com.android.net.flags.Flags;
|
||||
import com.android.net.module.util.BitUtils;
|
||||
import com.android.net.module.util.CollectionUtils;
|
||||
import com.android.net.module.util.FrameworkConnectivityStatsLog;
|
||||
import com.android.net.module.util.NetworkCapabilitiesUtils;
|
||||
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.StringJoiner;
|
||||
|
||||
|
|
@ -169,6 +176,15 @@ public final class NetworkCapabilities implements Parcelable {
|
|||
@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
|
||||
public static final long REDACT_FOR_NETWORK_SETTINGS = 1 << 2;
|
||||
|
||||
/**
|
||||
* Redact any fields that need {@link android.Manifest.permission#THREAD_NETWORK_PRIVILEGED}
|
||||
* permission since the receiving app does not hold this permission.
|
||||
*
|
||||
* @see android.Manifest.permission#THREAD_NETWORK_PRIVILEGED
|
||||
* @hide for only {@code ThreadNetworkSpecifier} which is in the same module
|
||||
*/
|
||||
public static final long REDACT_FOR_THREAD_NETWORK_PRIVILEGED = 1 << 3;
|
||||
|
||||
/**
|
||||
* Redact all fields in this object that require any relevant permission.
|
||||
* @hide
|
||||
|
|
@ -182,6 +198,7 @@ public final class NetworkCapabilities implements Parcelable {
|
|||
REDACT_FOR_ACCESS_FINE_LOCATION,
|
||||
REDACT_FOR_LOCAL_MAC_ADDRESS,
|
||||
REDACT_FOR_NETWORK_SETTINGS,
|
||||
REDACT_FOR_THREAD_NETWORK_PRIVILEGED,
|
||||
REDACT_ALL
|
||||
})
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
|
|
@ -335,6 +352,11 @@ public final class NetworkCapabilities implements Parcelable {
|
|||
if (mTransportInfo != null) {
|
||||
mTransportInfo = nc.mTransportInfo.makeCopy(redactions);
|
||||
}
|
||||
if (mNetworkSpecifier != null) {
|
||||
mNetworkSpecifier =
|
||||
(NetworkSpecifier) RedactionHelper.getSpecifierRedactResult(
|
||||
mNetworkSpecifier, redactions);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -3005,13 +3027,14 @@ public final class NetworkCapabilities implements Parcelable {
|
|||
* @hide
|
||||
*/
|
||||
public @RedactionType long getApplicableRedactions() {
|
||||
// Currently, there are no fields redacted in NetworkCapabilities itself, so we just
|
||||
// passthrough the redactions required by the embedded TransportInfo. If this changes
|
||||
// in the future, modify this method.
|
||||
if (mTransportInfo == null) {
|
||||
return NetworkCapabilities.REDACT_NONE;
|
||||
long redactions = REDACT_NONE;
|
||||
if (mTransportInfo != null) {
|
||||
redactions |= mTransportInfo.getApplicableRedactions();
|
||||
}
|
||||
return mTransportInfo.getApplicableRedactions();
|
||||
if (mNetworkSpecifier != null) {
|
||||
redactions |= RedactionHelper.getSpecifierApplicableRedactions(mNetworkSpecifier);
|
||||
}
|
||||
return redactions;
|
||||
}
|
||||
|
||||
private NetworkCapabilities removeDefaultCapabilites() {
|
||||
|
|
@ -3509,4 +3532,148 @@ public final class NetworkCapabilities implements Parcelable {
|
|||
return new NetworkCapabilities(mCaps);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper class for providing access to {@link NetworkSpecifier} redaction methods via
|
||||
* reflection.
|
||||
*
|
||||
* Reflection is necessary because NetworkSpecifier is platform code. So it's possible for a
|
||||
* mainline update to define a getApplicableRedactions() or redact(long) method in a subclass
|
||||
* of NetworkSpecifier (e.g., EthernetNetworkSpecifier), and then to run that mainline module
|
||||
* on an Android release where the NetworkSpecifier base class does not have those methods.
|
||||
* On such a device, ConnectivityService must still call those methods if they exist, otherwise
|
||||
* it would fail to redact information that needs to be redacted. Another way to do this would
|
||||
* be for ConnectivityService to just call the method and catch NoSuchMethodError, but this
|
||||
* code runs fairly often and constructing exceptions is expensive.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public static final class RedactionHelper {
|
||||
// LruCache is thread-safe so that no locks are used to project it this class
|
||||
private static final LruCache<Class<? extends NetworkSpecifier>, Optional<Method>>
|
||||
sGetApplicableRedactionsMethodCache = new LruCache<>(100);
|
||||
private static final LruCache<Class<? extends NetworkSpecifier>, Optional<Method>>
|
||||
sRedactLongMethodCache = new LruCache<>(100);
|
||||
|
||||
private RedactionHelper() {}
|
||||
|
||||
/**
|
||||
* Returns the {@link Method} of the {@link NetworkSpecifier} class for the given method
|
||||
* name, or {@code null} if the method doesn't exist.
|
||||
* <p>
|
||||
* This is typically used for {@link NetworkSpecifier#getApplicableRedactions()} and {@link
|
||||
* NetworkSpecifier#redact(long)} to work with new NetworkSpecifier subclasses added in new
|
||||
* mainline version but deployed to older platforms which don't have those two
|
||||
* NetworkSpecifier methods.
|
||||
* <p>
|
||||
* See {@link #createWithSensitiveInfoSanitizedIfNecessaryWhenParceled} and {@link
|
||||
* #ensureSufficientPermissionsForRequest} for how this method should be used.
|
||||
*/
|
||||
@Nullable
|
||||
private static Method getNetworkSpecifierMethod(
|
||||
Class<? extends NetworkSpecifier> clazz, String name, Class<?> returnType,
|
||||
Class<?>... parameterTypes) {
|
||||
// Use {@code getMethods()} rather than {@code getDeclaredMethods()} so that it can
|
||||
// return the inherited NetworkSpecifier methods for those existing subclasses which
|
||||
// don't override {@link NetworkSpecifier#getApplicableRedactions()} and {@link
|
||||
// NetworkSpecifier#redact(long)}.
|
||||
// Note also {@code getMethods()} returns only public methods, which is necessary for
|
||||
// code to call the method later.
|
||||
for (Method method : clazz.getMethods()) {
|
||||
if (name.equals(method.getName())
|
||||
&& returnType.equals(method.getReturnType())
|
||||
&& Arrays.equals(parameterTypes, method.getParameterTypes())) {
|
||||
return method;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@link NetworkSpecifier#getApplicableRedactions()} method or {@code null} if
|
||||
* it doesn't exist for the given {@link NetworkSpecifier} subclass.
|
||||
*/
|
||||
@Nullable
|
||||
private static Method getApplicableRedactionsMethod(
|
||||
Class<? extends NetworkSpecifier> clazz) {
|
||||
// If this method is called concurrently from two threads, both may see cache missed
|
||||
// and the reflection branch in method will be executed for twice. But this is okay
|
||||
// because getApplicableRedactions() itself has no side-effect (and this is why we can
|
||||
// create caches for it).
|
||||
Optional<Method> cache = sGetApplicableRedactionsMethodCache.get(clazz);
|
||||
if (cache != null) {
|
||||
return cache.orElse(null);
|
||||
}
|
||||
Method method = getNetworkSpecifierMethod(
|
||||
clazz, "getApplicableRedactions", long.class);
|
||||
sGetApplicableRedactionsMethodCache.put(clazz, Optional.ofNullable(method));
|
||||
return method;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the value of {@link NetworkSpecifier#getApplicableRedactions()} or
|
||||
* {@link REDACT_NONE} if the platform doesn't support
|
||||
* {@link NetworkSpecifier#getApplicableRedactions()}.
|
||||
*/
|
||||
public static long getSpecifierApplicableRedactions(NetworkSpecifier specifier) {
|
||||
final Method getApplicableRedactions =
|
||||
getApplicableRedactionsMethod(specifier.getClass());
|
||||
if (getApplicableRedactions == null) {
|
||||
return REDACT_NONE;
|
||||
}
|
||||
|
||||
try {
|
||||
return (long) getApplicableRedactions.invoke(specifier);
|
||||
} catch (IllegalAccessException | InvocationTargetException
|
||||
| IllegalArgumentException e) {
|
||||
FrameworkConnectivityStatsLog.write(CORE_NETWORKING_TERRIBLE_ERROR_OCCURRED,
|
||||
CORE_NETWORKING_TERRIBLE_ERROR_OCCURRED__ERROR_TYPE__TYPE_SPECIFIER_REDACTION_REFLECTION_ERROR);
|
||||
Log.wtf(TAG, "Un expected reflection error on specifier " + specifier, e);
|
||||
return REDACT_ALL;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@link NetworkSpecifier#redact(long)} method or {@code null} if it doesn't
|
||||
* exist for the given {@link NetworkSpecifier} subclass.
|
||||
*/
|
||||
@Nullable
|
||||
private static Method getRedactLongMethod(Class<? extends NetworkSpecifier> clazz) {
|
||||
// If this method is called concurrently from two threads, both may see cache missed
|
||||
// and the reflection branch in method will be executed for twice. But this is okay
|
||||
// because redact(long) itself has no side-effect (and this is why we can create caches
|
||||
// for it).
|
||||
Optional<Method> cache = sRedactLongMethodCache.get(clazz);
|
||||
if (cache != null) {
|
||||
return cache.orElse(null);
|
||||
}
|
||||
Method method = getNetworkSpecifierMethod(
|
||||
clazz, "redact", NetworkSpecifier.class, long.class);
|
||||
sRedactLongMethodCache.put(clazz, Optional.ofNullable(method));
|
||||
return method;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the result of {@link NetworkSpecifier#redact(long)} or {@code specifier} itself
|
||||
* if the platform doesn't support {@link NetworkSpecifier#redact(long)}.
|
||||
*/
|
||||
@Nullable
|
||||
public static NetworkSpecifier getSpecifierRedactResult(
|
||||
NetworkSpecifier specifier, long redactions) {
|
||||
final Method redactLong = getRedactLongMethod(specifier.getClass());
|
||||
if (redactLong == null) {
|
||||
return specifier;
|
||||
}
|
||||
|
||||
try {
|
||||
return (NetworkSpecifier) redactLong.invoke(specifier, redactions);
|
||||
} catch (IllegalAccessException | InvocationTargetException
|
||||
| IllegalArgumentException e) {
|
||||
FrameworkConnectivityStatsLog.write(CORE_NETWORKING_TERRIBLE_ERROR_OCCURRED,
|
||||
CORE_NETWORKING_TERRIBLE_ERROR_OCCURRED__ERROR_TYPE__TYPE_SPECIFIER_REDACTION_REFLECTION_ERROR);
|
||||
Log.wtf(TAG, "Un expected reflection error on specifier " + specifier, e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -50,6 +50,9 @@ public interface TransportInfo {
|
|||
* </ul>
|
||||
* </p>
|
||||
*
|
||||
* <p> Subclasses should silently ignore redactions which are not included in {@link
|
||||
* #getApplicableRedactions()}.
|
||||
*
|
||||
* @param redactions bitmask of redactions that needs to be performed on this instance.
|
||||
* @return Copy of this instance with the necessary redactions.
|
||||
* @hide
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue