bpf: git mv DnsResolver bpf/dns_helper

This is entirely bpf specific logic for the dns_helper.so
shared library.  Let's put it under bpf/ so it can be covered
by bpf source hash logic and used by UML NetBpfLoad test.

Test: TreeHugger
Signed-off-by: Maciej Żenczykowski <maze@google.com>
Change-Id: I7b544cb1db9f13f2467519068cefd236b814413a
This commit is contained in:
Maciej Żenczykowski 2024-09-26 18:57:04 -07:00
commit 2bb5b7ebee
8 changed files with 1 additions and 1 deletions

87
bpf/dns_helper/Android.bp Normal file
View file

@ -0,0 +1,87 @@
//
// 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 {
default_team: "trendy_team_fwk_core_networking",
default_applicable_licenses: ["Android-Apache-2.0"],
}
cc_library {
name: "libcom.android.tethering.dns_helper",
version_script: "libcom.android.tethering.dns_helper.map.txt",
stubs: {
versions: [
"1",
],
symbol_file: "libcom.android.tethering.dns_helper.map.txt",
},
defaults: ["netd_defaults"],
header_libs: [
"bpf_connectivity_headers",
"libcutils_headers",
],
srcs: [
"DnsBpfHelper.cpp",
"DnsHelper.cpp",
],
static_libs: [
"libmodules-utils-build",
],
shared_libs: [
"libbase",
],
export_include_dirs: ["include"],
header_abi_checker: {
enabled: true,
symbol_file: "libcom.android.tethering.dns_helper.map.txt",
},
sanitize: {
cfi: true,
},
apex_available: ["com.android.tethering"],
min_sdk_version: "30",
}
cc_test {
name: "dns_helper_unit_test",
defaults: ["netd_defaults"],
test_suites: [
"general-tests",
"mts-tethering",
],
test_config_template: ":net_native_test_config_template",
header_libs: [
"bpf_connectivity_headers",
],
srcs: [
"DnsBpfHelperTest.cpp",
],
static_libs: [
"libcom.android.tethering.dns_helper",
],
shared_libs: [
"libbase",
"libcutils",
],
compile_multilib: "both",
multilib: {
lib32: {
suffix: "32",
},
lib64: {
suffix: "64",
},
},
}

View file

@ -0,0 +1,87 @@
/*
* 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.
*/
#define LOG_TAG "DnsBpfHelper"
#include "DnsBpfHelper.h"
#include <android-base/logging.h>
#include <android-modules-utils/sdk_level.h>
namespace android {
namespace net {
#define RETURN_IF_RESULT_NOT_OK(result) \
do { \
if (!result.ok()) { \
LOG(ERROR) << "L" << __LINE__ << " " << __func__ << ": " << strerror(result.error().code()); \
return result.error(); \
} \
} while (0)
base::Result<void> DnsBpfHelper::init() {
if (!android::modules::sdklevel::IsAtLeastT()) {
LOG(ERROR) << __func__ << ": Unsupported before Android T.";
return base::Error(EOPNOTSUPP);
}
RETURN_IF_RESULT_NOT_OK(mConfigurationMap.init(CONFIGURATION_MAP_PATH));
RETURN_IF_RESULT_NOT_OK(mUidOwnerMap.init(UID_OWNER_MAP_PATH));
RETURN_IF_RESULT_NOT_OK(mDataSaverEnabledMap.init(DATA_SAVER_ENABLED_MAP_PATH));
return {};
}
base::Result<bool> DnsBpfHelper::isUidNetworkingBlocked(uid_t uid, bool metered) {
if (is_system_uid(uid)) return false;
if (!mConfigurationMap.isValid() || !mUidOwnerMap.isValid()) {
LOG(ERROR) << __func__
<< ": BPF maps are not ready. Forgot to call ADnsHelper_init?";
return base::Error(EUNATCH);
}
auto enabledRules = mConfigurationMap.readValue(UID_RULES_CONFIGURATION_KEY);
RETURN_IF_RESULT_NOT_OK(enabledRules);
auto value = mUidOwnerMap.readValue(uid);
uint32_t uidRules = value.ok() ? value.value().rule : 0;
// For doze mode, battery saver, low power standby.
if (isBlockedByUidRules(enabledRules.value(), uidRules)) return true;
// For data saver.
// DataSaverEnabled map on V+ platforms is the only reliable source of information about the
// current data saver status. While ConnectivityService offers two ways to update this map for U
// and V+, the U- platform implementation can have delays, potentially leading to inaccurate
// results. Conversely, the V+ platform implementation is synchronized with the actual data saver
// state, making it a trustworthy source. Since this library primarily serves DNS resolvers,
// relying solely on V+ data prevents erroneous blocking of DNS queries.
if (android::modules::sdklevel::IsAtLeastV() && metered) {
// The background data setting (PENALTY_BOX_USER_MATCH, PENALTY_BOX_ADMIN_MATCH) and
// unrestricted data usage setting (HAPPY_BOX_MATCH) for individual apps override the system
// wide Data Saver setting.
if (uidRules & (PENALTY_BOX_USER_MATCH | PENALTY_BOX_ADMIN_MATCH)) return true;
if (uidRules & HAPPY_BOX_MATCH) return false;
auto dataSaverSetting = mDataSaverEnabledMap.readValue(DATA_SAVER_ENABLED_KEY);
RETURN_IF_RESULT_NOT_OK(dataSaverSetting);
return dataSaverSetting.value();
}
return false;
}
} // namespace net
} // namespace android

View file

@ -0,0 +1,46 @@
/*
* 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.
*/
#pragma once
#include <android-base/result.h>
#include "bpf/BpfMap.h"
#include "netd.h"
namespace android {
namespace net {
class DnsBpfHelper {
public:
DnsBpfHelper() = default;
DnsBpfHelper(const DnsBpfHelper&) = delete;
DnsBpfHelper& operator=(const DnsBpfHelper&) = delete;
base::Result<void> init();
base::Result<bool> isUidNetworkingBlocked(uid_t uid, bool metered);
private:
android::bpf::BpfMapRO<uint32_t, uint32_t> mConfigurationMap;
android::bpf::BpfMapRO<uint32_t, UidOwnerValue> mUidOwnerMap;
android::bpf::BpfMapRO<uint32_t, bool> mDataSaverEnabledMap;
// For testing
friend class DnsBpfHelperTest;
};
} // namespace net
} // namespace android

View file

@ -0,0 +1,210 @@
/*
* 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.
*/
#include <gtest/gtest.h>
#include <private/android_filesystem_config.h>
#define BPF_MAP_MAKE_VISIBLE_FOR_TESTING
#include "DnsBpfHelper.h"
using namespace android::bpf; // NOLINT(google-build-using-namespace): exempted
namespace android {
namespace net {
constexpr int TEST_MAP_SIZE = 2;
#define ASSERT_VALID(x) ASSERT_TRUE((x).isValid())
class DnsBpfHelperTest : public ::testing::Test {
protected:
DnsBpfHelper mDnsBpfHelper;
BpfMap<uint32_t, uint32_t> mFakeConfigurationMap;
BpfMap<uint32_t, UidOwnerValue> mFakeUidOwnerMap;
BpfMap<uint32_t, bool> mFakeDataSaverEnabledMap;
void SetUp() {
mFakeConfigurationMap.resetMap(BPF_MAP_TYPE_ARRAY, CONFIGURATION_MAP_SIZE);
ASSERT_VALID(mFakeConfigurationMap);
mFakeUidOwnerMap.resetMap(BPF_MAP_TYPE_HASH, TEST_MAP_SIZE);
ASSERT_VALID(mFakeUidOwnerMap);
mFakeDataSaverEnabledMap.resetMap(BPF_MAP_TYPE_ARRAY, DATA_SAVER_ENABLED_MAP_SIZE);
ASSERT_VALID(mFakeDataSaverEnabledMap);
mDnsBpfHelper.mConfigurationMap = mFakeConfigurationMap;
ASSERT_VALID(mDnsBpfHelper.mConfigurationMap);
mDnsBpfHelper.mUidOwnerMap = mFakeUidOwnerMap;
ASSERT_VALID(mDnsBpfHelper.mUidOwnerMap);
mDnsBpfHelper.mDataSaverEnabledMap = mFakeDataSaverEnabledMap;
ASSERT_VALID(mDnsBpfHelper.mDataSaverEnabledMap);
}
void ResetAllMaps() {
mDnsBpfHelper.mConfigurationMap.reset();
mDnsBpfHelper.mUidOwnerMap.reset();
mDnsBpfHelper.mDataSaverEnabledMap.reset();
}
};
TEST_F(DnsBpfHelperTest, IsUidNetworkingBlocked) {
struct TestConfig {
const uid_t uid;
const uint32_t enabledRules;
const uint32_t uidRules;
const int expectedResult;
std::string toString() const {
return fmt::format(
"uid: {}, enabledRules: {}, uidRules: {}, expectedResult: {}",
uid, enabledRules, uidRules, expectedResult);
}
} testConfigs[] = {
// clang-format off
// No rule enabled:
// uid, enabledRules, uidRules, expectedResult
{AID_APP_START, NO_MATCH, NO_MATCH, false},
// An allowlist rule:
{AID_APP_START, NO_MATCH, DOZABLE_MATCH, false},
{AID_APP_START, DOZABLE_MATCH, NO_MATCH, true},
{AID_APP_START, DOZABLE_MATCH, DOZABLE_MATCH, false},
// A denylist rule
{AID_APP_START, NO_MATCH, STANDBY_MATCH, false},
{AID_APP_START, STANDBY_MATCH, NO_MATCH, false},
{AID_APP_START, STANDBY_MATCH, STANDBY_MATCH, true},
// Multiple rules enabled:
// Match only part of the enabled allowlist rules.
{AID_APP_START, DOZABLE_MATCH|POWERSAVE_MATCH, DOZABLE_MATCH, true},
{AID_APP_START, DOZABLE_MATCH|POWERSAVE_MATCH, POWERSAVE_MATCH, true},
// Match all of the enabled allowlist rules.
{AID_APP_START, DOZABLE_MATCH|POWERSAVE_MATCH, DOZABLE_MATCH|POWERSAVE_MATCH, false},
// Match allowlist.
{AID_APP_START, DOZABLE_MATCH|STANDBY_MATCH, DOZABLE_MATCH, false},
// Match no rule.
{AID_APP_START, DOZABLE_MATCH|STANDBY_MATCH, NO_MATCH, true},
{AID_APP_START, DOZABLE_MATCH|POWERSAVE_MATCH, NO_MATCH, true},
// System UID: always unblocked.
{AID_SYSTEM, NO_MATCH, NO_MATCH, false},
{AID_SYSTEM, NO_MATCH, DOZABLE_MATCH, false},
{AID_SYSTEM, DOZABLE_MATCH, NO_MATCH, false},
{AID_SYSTEM, DOZABLE_MATCH, DOZABLE_MATCH, false},
{AID_SYSTEM, NO_MATCH, STANDBY_MATCH, false},
{AID_SYSTEM, STANDBY_MATCH, NO_MATCH, false},
{AID_SYSTEM, STANDBY_MATCH, STANDBY_MATCH, false},
{AID_SYSTEM, DOZABLE_MATCH|POWERSAVE_MATCH, DOZABLE_MATCH, false},
{AID_SYSTEM, DOZABLE_MATCH|POWERSAVE_MATCH, POWERSAVE_MATCH, false},
{AID_SYSTEM, DOZABLE_MATCH|POWERSAVE_MATCH, DOZABLE_MATCH|POWERSAVE_MATCH, false},
{AID_SYSTEM, DOZABLE_MATCH|STANDBY_MATCH, DOZABLE_MATCH, false},
{AID_SYSTEM, DOZABLE_MATCH|STANDBY_MATCH, NO_MATCH, false},
{AID_SYSTEM, DOZABLE_MATCH|POWERSAVE_MATCH, NO_MATCH, false},
// clang-format on
};
for (const auto& config : testConfigs) {
SCOPED_TRACE(config.toString());
// Setup maps.
EXPECT_RESULT_OK(mFakeConfigurationMap.writeValue(UID_RULES_CONFIGURATION_KEY,
config.enabledRules, BPF_EXIST));
EXPECT_RESULT_OK(mFakeUidOwnerMap.writeValue(config.uid, {.iif = 0, .rule = config.uidRules},
BPF_ANY));
// Verify the function.
auto result = mDnsBpfHelper.isUidNetworkingBlocked(config.uid, /*metered=*/false);
EXPECT_TRUE(result.ok());
EXPECT_EQ(config.expectedResult, result.value());
}
}
TEST_F(DnsBpfHelperTest, IsUidNetworkingBlocked_uninitialized) {
ResetAllMaps();
auto result = mDnsBpfHelper.isUidNetworkingBlocked(AID_APP_START, /*metered=*/false);
EXPECT_FALSE(result.ok());
EXPECT_EQ(EUNATCH, result.error().code());
result = mDnsBpfHelper.isUidNetworkingBlocked(AID_SYSTEM, /*metered=*/false);
EXPECT_TRUE(result.ok());
EXPECT_FALSE(result.value());
}
// Verify DataSaver on metered network.
TEST_F(DnsBpfHelperTest, IsUidNetworkingBlocked_metered) {
struct TestConfig {
const uint32_t enabledRules; // Settings in configuration map.
const bool dataSaverEnabled; // Settings in data saver enabled map.
const uint32_t uidRules; // Settings in uid owner map.
const int blocked; // Whether the UID is expected to be networking blocked or not.
std::string toString() const {
return fmt::format(
", enabledRules: {}, dataSaverEnabled: {}, uidRules: {}, expect blocked: {}",
enabledRules, dataSaverEnabled, uidRules, blocked);
}
} testConfigs[]{
// clang-format off
// enabledRules, dataSaverEnabled, uidRules, blocked
{NO_MATCH, false, NO_MATCH, false},
{NO_MATCH, false, PENALTY_BOX_USER_MATCH, true},
{NO_MATCH, false, PENALTY_BOX_ADMIN_MATCH, true},
{NO_MATCH, false, PENALTY_BOX_USER_MATCH|PENALTY_BOX_ADMIN_MATCH, true},
{NO_MATCH, false, HAPPY_BOX_MATCH, false},
{NO_MATCH, false, PENALTY_BOX_USER_MATCH|HAPPY_BOX_MATCH, true},
{NO_MATCH, false, PENALTY_BOX_ADMIN_MATCH|HAPPY_BOX_MATCH, true},
{NO_MATCH, true, NO_MATCH, true},
{NO_MATCH, true, PENALTY_BOX_USER_MATCH, true},
{NO_MATCH, true, PENALTY_BOX_ADMIN_MATCH, true},
{NO_MATCH, true, PENALTY_BOX_USER_MATCH|PENALTY_BOX_ADMIN_MATCH, true},
{NO_MATCH, true, HAPPY_BOX_MATCH, false},
{NO_MATCH, true, PENALTY_BOX_USER_MATCH|HAPPY_BOX_MATCH, true},
{NO_MATCH, true, PENALTY_BOX_ADMIN_MATCH|HAPPY_BOX_MATCH, true},
{STANDBY_MATCH, false, STANDBY_MATCH, true},
{STANDBY_MATCH, false, STANDBY_MATCH|PENALTY_BOX_USER_MATCH, true},
{STANDBY_MATCH, false, STANDBY_MATCH|PENALTY_BOX_ADMIN_MATCH, true},
{STANDBY_MATCH, false, STANDBY_MATCH|HAPPY_BOX_MATCH, true},
{STANDBY_MATCH, false, STANDBY_MATCH|PENALTY_BOX_USER_MATCH|HAPPY_BOX_MATCH, true},
{STANDBY_MATCH, false, STANDBY_MATCH|PENALTY_BOX_ADMIN_MATCH|HAPPY_BOX_MATCH, true},
{STANDBY_MATCH, true, STANDBY_MATCH, true},
{STANDBY_MATCH, true, STANDBY_MATCH|PENALTY_BOX_USER_MATCH, true},
{STANDBY_MATCH, true, STANDBY_MATCH|PENALTY_BOX_ADMIN_MATCH, true},
{STANDBY_MATCH, true, STANDBY_MATCH|HAPPY_BOX_MATCH, true},
{STANDBY_MATCH, true, STANDBY_MATCH|PENALTY_BOX_USER_MATCH|HAPPY_BOX_MATCH, true},
{STANDBY_MATCH, true, STANDBY_MATCH|PENALTY_BOX_ADMIN_MATCH|HAPPY_BOX_MATCH, true},
// clang-format on
};
for (const auto& config : testConfigs) {
SCOPED_TRACE(config.toString());
// Setup maps.
EXPECT_RESULT_OK(mFakeConfigurationMap.writeValue(UID_RULES_CONFIGURATION_KEY,
config.enabledRules, BPF_EXIST));
EXPECT_RESULT_OK(mFakeDataSaverEnabledMap.writeValue(DATA_SAVER_ENABLED_KEY,
config.dataSaverEnabled, BPF_EXIST));
EXPECT_RESULT_OK(mFakeUidOwnerMap.writeValue(AID_APP_START, {.iif = 0, .rule = config.uidRules},
BPF_ANY));
// Verify the function.
auto result = mDnsBpfHelper.isUidNetworkingBlocked(AID_APP_START, /*metered=*/true);
EXPECT_RESULT_OK(result);
EXPECT_EQ(config.blocked, result.value());
}
}
} // namespace net
} // namespace android

View file

@ -0,0 +1,37 @@
/*
* 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.
*/
#include <errno.h>
#include "DnsBpfHelper.h"
#include "DnsHelperPublic.h"
static android::net::DnsBpfHelper sDnsBpfHelper;
int ADnsHelper_init() {
auto result = sDnsBpfHelper.init();
if (!result.ok()) return -result.error().code();
return 0;
}
int ADnsHelper_isUidNetworkingBlocked(uid_t uid, bool metered) {
auto result = sDnsBpfHelper.isUidNetworkingBlocked(uid, metered);
if (!result.ok()) return -result.error().code();
// bool -> int conversion.
return result.value();
}

View file

@ -0,0 +1,46 @@
/*
* 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.
*/
#pragma once
#include <sys/cdefs.h>
#include <sys/types.h>
__BEGIN_DECLS
/*
* Perform any required initialization - including opening any required BPF maps. This function
* needs to be called before using other functions of this library.
*
* Returns 0 on success, -EOPNOTSUPP when the function is called on the Android version before
* T. Returns a negative POSIX error code (see errno.h) on other failures.
*/
int ADnsHelper_init();
/*
* The function reads bpf maps and returns whether the given uid has blocked networking or not. The
* function is supported starting from Android T.
*
* |uid| is a Linux/Android UID to be queried. It is a combination of UserID and AppID.
* |metered| indicates whether the uid is currently using a billing network.
*
* Returns 0(false)/1(true) on success, -EUNATCH when the ADnsHelper_init is not called before
* calling this function. Returns a negative POSIX error code (see errno.h) on other failures
* that return from bpf syscall.
*/
int ADnsHelper_isUidNetworkingBlocked(uid_t uid, bool metered);
__END_DECLS

View file

@ -0,0 +1,27 @@
#
# 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.
#
# This lists the entry points visible to applications that use the
# libcom.android.tethering.dns_helper library. Other entry points present in
# the library won't be usable.
LIBCOM_ANDROID_TETHERING_DNS_HELPER {
global:
ADnsHelper_init; # apex
ADnsHelper_isUidNetworkingBlocked; # apex
local:
*;
};

View file

@ -47,8 +47,8 @@ cc_library_headers {
"com.android.tethering",
],
visibility: [
"//packages/modules/Connectivity/bpf/dns_helper",
"//packages/modules/Connectivity/bpf/netd",
"//packages/modules/Connectivity/DnsResolver",
"//packages/modules/Connectivity/service",
"//packages/modules/Connectivity/service/native/libs/libclat",
"//packages/modules/Connectivity/Tethering",